diff --git a/packages/client/src/routes/+page.svelte b/packages/client/src/routes/+page.svelte index 605d57e..585b194 100644 --- a/packages/client/src/routes/+page.svelte +++ b/packages/client/src/routes/+page.svelte @@ -19,13 +19,13 @@ import { dev } from '$app/environment'; // connection state - let ws = $state(undefined); + let ws = $state(undefined); // websocket client let stationId = $state(undefined); // stationId assigned by server let connectionState = $state(ConnectionState.disconnected); // client state - let composedMessage = $state(''); - let sentMessages = $state([]); // storing sent messages with client-timestamp for now + let composedMessage = $state(''); // content of text-box + let sentMessages = $state([]); // sent messages stored as type ReceiveHl7v2Message because of the timestamp let receivedMessages = $state([]); let isSending = $state(false); let copySuccess = $state(false); @@ -60,6 +60,7 @@ }; const segmentTypes = Object.keys(segmentTemplates) as Array; + // handles the connection process and message handling function connectToServer() { console.log('Connecting to server...'); @@ -67,16 +68,20 @@ const socket = new WebSocket(`${dev ? 'ws' : 'wss'}://${env.PUBLIC_SERVER}`); + // store websocket on successful connection socket.onopen = () => { console.log('WebSocket connection established.'); ws = socket; }; + // register message handlers socket.onmessage = (event) => { + // parse every message as type Message const message = JSON.parse(event.data) as Message; console.log('Message received from server:', message); + // react based on message type switch (message.type) { // initial message from server assigning ID @@ -99,12 +104,14 @@ } }; + // reset connection state and websocket client on disconnect socket.onclose = () => { console.log('WebSocket connection closed.'); connectionState = ConnectionState.disconnected; ws = undefined; }; + // reset connection state and websocket client on error socket.onerror = (error) => { console.error('WebSocket error:', error); connectionState = ConnectionState.disconnected; @@ -112,7 +119,7 @@ }; } - // clean up on close + // reset connection state and websocket client on tab closed $effect(() => { return () => { if (ws && ws.readyState === WebSocket.OPEN) { @@ -122,21 +129,26 @@ }; }); + // adds the template of the provided segment type at the bottom of the textbox function addSegment(type: keyof typeof segmentTemplates) { const template = segmentTemplates[type].template(); composedMessage += `\r\n${template}`; } + // handles sending a message to the websocket server function handleSendMessage(message: string) { + // check for active connection if (!ws || ws.readyState !== WebSocket.OPEN || isSending || !stationId) { console.log('Socket not ready'); return; } + // set UI state isSending = true; deliveryError = ''; + // construct Message object and send it const messageToSend = { type: 'send_hl7v2', payload: { message }, @@ -148,9 +160,12 @@ payload: { message, timestamp: new Date().toISOString() }, } as ReceiveHl7v2Message, ...sentMessages]; composedMessage = segmentTemplates.MSH.template(); + + // reset UI state isSending = false; } + // copies the stationId to the clipboard and handles UI state function copyStationId() { if (stationId) { navigator.clipboard.writeText(stationId).then(() => { @@ -163,6 +178,7 @@ + {#snippet message(msg: ReceiveHl7v2Message)}

{new Date(msg.payload.timestamp).toLocaleString()}

@@ -296,4 +312,4 @@
- \ No newline at end of file + diff --git a/packages/server/config.ts b/packages/server/config.ts index 006b447..53ab614 100644 --- a/packages/server/config.ts +++ b/packages/server/config.ts @@ -1,23 +1,25 @@ + +// define available configurations interface Config { port: number; prefixes: string[]; poolSize: number; } +// construct config object from environment variables export const config: Config = { port: parseInt(process.env.PORT || '8080', 10), prefixes: (process.env.PREFIXES || 'STA').split(","), poolSize: parseInt(process.env.POOL_SIZE || '100', 10), }; +// validate config if (isNaN(config.port) || config.port < 1024 || config.port > 49151) { throw new Error('Invalid PORT environment variable (1024 - 49151)'); } - if (config.prefixes.length === 0) { throw new Error('Invalid PREFIXES environment variable (length > 1)'); } - if (isNaN(config.poolSize) || config.poolSize < config.prefixes.length) { - throw new Error('Invalid PORT environment variable (poolSize >= prefix.length)'); + throw new Error('Invalid pool size environment variable (poolSize >= prefix.length)'); } diff --git a/packages/server/index.ts b/packages/server/index.ts index b4ce411..acbf484 100644 --- a/packages/server/index.ts +++ b/packages/server/index.ts @@ -17,6 +17,7 @@ for (const prefix of config.prefixes) { } } +// mixes the array randomly to avoid getting the same ID reassigned function shake(arr: any[]) { for (let i = arr.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); @@ -71,9 +72,11 @@ wss.on('connection', (ws) => { console.log(`Client connected. Assigning ID: ${stationId}`); + // listen for messages and defined message handling ws.on('message', (message) => { try { + // parse every message to type Message const parsedMessage: Message = JSON.parse(message.toString()); console.log(`Received message from ${stationId}:`, parsedMessage); @@ -81,6 +84,7 @@ wss.on('connection', (ws) => { if (parsedMessage.type === MessageType.send_hl7v2) { const { message } = parsedMessage.payload; + // TODO: validate message // get sender and recipient ID @@ -98,6 +102,7 @@ wss.on('connection', (ws) => { // Find the recipient's WebSocket connection in our map. const recipientWs = clients.get(recipientId); + // check if recipient exists and is connected if (recipientWs && recipientWs.readyState === WebSocket.OPEN) { // The recipient is connected. Forward the message. @@ -136,6 +141,7 @@ wss.on('connection', (ws) => { if (stationId) { availableIds.push(stationId); } + // shake IDs to avoid getting the same ID reassigned availableIds = shake(availableIds); }); diff --git a/packages/shared/types.ts b/packages/shared/types.ts index 15b05b4..12a45fb 100644 --- a/packages/shared/types.ts +++ b/packages/shared/types.ts @@ -1,9 +1,12 @@ + +// defines different connection states export enum ConnectionState { connecting = 'connecting', connected = 'connected', disconnected = 'disconnected', } +// defines message types export enum MessageType { assign_id = 'assign_id', send_hl7v2 = 'send_hl7v2', @@ -11,10 +14,12 @@ export enum MessageType { delivery_error = 'delivery_error', } +// defines body of message types export type Message = | { type: MessageType.assign_id, payload: { stationId: string }} | { type: MessageType.send_hl7v2, payload: { message: string }} | { type: MessageType.receive_hl7v2, payload: { message: string, timestamp: string }} | { type: MessageType.delivery_error, payload: { error: string }} +// required to define list of this message type export type ReceiveHl7v2Message = Extract;