HL7-1: add documentation #4

Merged
markus merged 5 commits from 1-add-documentation into development 2025-07-30 11:07:34 +00:00
4 changed files with 37 additions and 8 deletions
Showing only changes of commit 3898715915 - Show all commits

View file

@ -19,13 +19,13 @@
import { dev } from '$app/environment'; import { dev } from '$app/environment';
// connection state // connection state
let ws = $state<WebSocket | undefined>(undefined); let ws = $state<WebSocket | undefined>(undefined); // websocket client
let stationId = $state<string | undefined>(undefined); // stationId assigned by server let stationId = $state<string | undefined>(undefined); // stationId assigned by server
let connectionState = $state<ConnectionState>(ConnectionState.disconnected); let connectionState = $state<ConnectionState>(ConnectionState.disconnected);
// client state // client state
let composedMessage = $state(''); let composedMessage = $state(''); // content of text-box
let sentMessages = $state<ReceiveHl7v2Message[]>([]); // storing sent messages with client-timestamp for now let sentMessages = $state<ReceiveHl7v2Message[]>([]); // sent messages stored as type ReceiveHl7v2Message because of the timestamp
let receivedMessages = $state<ReceiveHl7v2Message[]>([]); let receivedMessages = $state<ReceiveHl7v2Message[]>([]);
let isSending = $state(false); let isSending = $state(false);
let copySuccess = $state(false); let copySuccess = $state(false);
@ -60,6 +60,7 @@
}; };
const segmentTypes = Object.keys(segmentTemplates) as Array<keyof typeof segmentTemplates>; const segmentTypes = Object.keys(segmentTemplates) as Array<keyof typeof segmentTemplates>;
// handles the connection process and message handling
function connectToServer() { function connectToServer() {
console.log('Connecting to server...'); console.log('Connecting to server...');
@ -67,16 +68,20 @@
const socket = new WebSocket(`${dev ? 'ws' : 'wss'}://${env.PUBLIC_SERVER}`); const socket = new WebSocket(`${dev ? 'ws' : 'wss'}://${env.PUBLIC_SERVER}`);
// store websocket on successful connection
socket.onopen = () => { socket.onopen = () => {
console.log('WebSocket connection established.'); console.log('WebSocket connection established.');
ws = socket; ws = socket;
}; };
// register message handlers
socket.onmessage = (event) => { socket.onmessage = (event) => {
// parse every message as type Message
const message = JSON.parse(event.data) as Message; const message = JSON.parse(event.data) as Message;
console.log('Message received from server:', message); console.log('Message received from server:', message);
// react based on message type
switch (message.type) { switch (message.type) {
// initial message from server assigning ID // initial message from server assigning ID
@ -99,12 +104,14 @@
} }
}; };
// reset connection state and websocket client on disconnect
socket.onclose = () => { socket.onclose = () => {
console.log('WebSocket connection closed.'); console.log('WebSocket connection closed.');
connectionState = ConnectionState.disconnected; connectionState = ConnectionState.disconnected;
ws = undefined; ws = undefined;
}; };
// reset connection state and websocket client on error
socket.onerror = (error) => { socket.onerror = (error) => {
console.error('WebSocket error:', error); console.error('WebSocket error:', error);
connectionState = ConnectionState.disconnected; connectionState = ConnectionState.disconnected;
@ -112,7 +119,7 @@
}; };
} }
// clean up on close // reset connection state and websocket client on tab closed
$effect(() => { $effect(() => {
return () => { return () => {
if (ws && ws.readyState === WebSocket.OPEN) { 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) { function addSegment(type: keyof typeof segmentTemplates) {
const template = segmentTemplates[type].template(); const template = segmentTemplates[type].template();
composedMessage += `\r\n${template}`; composedMessage += `\r\n${template}`;
} }
// handles sending a message to the websocket server
function handleSendMessage(message: string) { function handleSendMessage(message: string) {
// check for active connection
if (!ws || ws.readyState !== WebSocket.OPEN || isSending || !stationId) { if (!ws || ws.readyState !== WebSocket.OPEN || isSending || !stationId) {
console.log('Socket not ready'); console.log('Socket not ready');
return; return;
} }
// set UI state
isSending = true; isSending = true;
deliveryError = ''; deliveryError = '';
// construct Message object and send it
const messageToSend = { const messageToSend = {
type: 'send_hl7v2', type: 'send_hl7v2',
payload: { message }, payload: { message },
@ -148,9 +160,12 @@
payload: { message, timestamp: new Date().toISOString() }, payload: { message, timestamp: new Date().toISOString() },
} as ReceiveHl7v2Message, ...sentMessages]; } as ReceiveHl7v2Message, ...sentMessages];
composedMessage = segmentTemplates.MSH.template(); composedMessage = segmentTemplates.MSH.template();
// reset UI state
isSending = false; isSending = false;
} }
// copies the stationId to the clipboard and handles UI state
function copyStationId() { function copyStationId() {
if (stationId) { if (stationId) {
navigator.clipboard.writeText(stationId).then(() => { navigator.clipboard.writeText(stationId).then(() => {
@ -163,6 +178,7 @@
</script> </script>
<!-- 'component' for displaying a message in the message lists (sent and received) -->
{#snippet message(msg: ReceiveHl7v2Message)} {#snippet message(msg: ReceiveHl7v2Message)}
<div class="bg-foreground/10 p-3 rounded-md space-y-1"> <div class="bg-foreground/10 p-3 rounded-md space-y-1">
<p class="text-xs">{new Date(msg.payload.timestamp).toLocaleString()}</p> <p class="text-xs">{new Date(msg.payload.timestamp).toLocaleString()}</p>
@ -296,4 +312,4 @@
</div> </div>
</main> </main>
</div> </div>
</div> </div>

View file

@ -1,23 +1,25 @@
// define available configurations
interface Config { interface Config {
port: number; port: number;
prefixes: string[]; prefixes: string[];
poolSize: number; poolSize: number;
} }
// construct config object from environment variables
export const config: Config = { export const config: Config = {
port: parseInt(process.env.PORT || '8080', 10), port: parseInt(process.env.PORT || '8080', 10),
prefixes: (process.env.PREFIXES || 'STA').split(","), prefixes: (process.env.PREFIXES || 'STA').split(","),
poolSize: parseInt(process.env.POOL_SIZE || '100', 10), poolSize: parseInt(process.env.POOL_SIZE || '100', 10),
}; };
// validate config
if (isNaN(config.port) || config.port < 1024 || config.port > 49151) { if (isNaN(config.port) || config.port < 1024 || config.port > 49151) {
throw new Error('Invalid PORT environment variable (1024 - 49151)'); throw new Error('Invalid PORT environment variable (1024 - 49151)');
} }
if (config.prefixes.length === 0) { if (config.prefixes.length === 0) {
throw new Error('Invalid PREFIXES environment variable (length > 1)'); throw new Error('Invalid PREFIXES environment variable (length > 1)');
} }
if (isNaN(config.poolSize) || config.poolSize < config.prefixes.length) { 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)');
} }

View file

@ -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[]) { function shake(arr: any[]) {
for (let i = arr.length - 1; i > 0; i--) { for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1)); const j = Math.floor(Math.random() * (i + 1));
@ -71,9 +72,11 @@ wss.on('connection', (ws) => {
console.log(`Client connected. Assigning ID: ${stationId}`); console.log(`Client connected. Assigning ID: ${stationId}`);
// listen for messages and defined message handling
ws.on('message', (message) => { ws.on('message', (message) => {
try { try {
// parse every message to type Message
const parsedMessage: Message = JSON.parse(message.toString()); const parsedMessage: Message = JSON.parse(message.toString());
console.log(`Received message from ${stationId}:`, parsedMessage); console.log(`Received message from ${stationId}:`, parsedMessage);
@ -81,6 +84,7 @@ wss.on('connection', (ws) => {
if (parsedMessage.type === MessageType.send_hl7v2) { if (parsedMessage.type === MessageType.send_hl7v2) {
const { message } = parsedMessage.payload; const { message } = parsedMessage.payload;
// TODO: validate message // TODO: validate message
// get sender and recipient ID // get sender and recipient ID
@ -98,6 +102,7 @@ wss.on('connection', (ws) => {
// Find the recipient's WebSocket connection in our map. // Find the recipient's WebSocket connection in our map.
const recipientWs = clients.get(recipientId); const recipientWs = clients.get(recipientId);
// check if recipient exists and is connected
if (recipientWs && recipientWs.readyState === WebSocket.OPEN) { if (recipientWs && recipientWs.readyState === WebSocket.OPEN) {
// The recipient is connected. Forward the message. // The recipient is connected. Forward the message.
@ -136,6 +141,7 @@ wss.on('connection', (ws) => {
if (stationId) { if (stationId) {
availableIds.push(stationId); availableIds.push(stationId);
} }
// shake IDs to avoid getting the same ID reassigned
availableIds = shake(availableIds); availableIds = shake(availableIds);
}); });

View file

@ -1,9 +1,12 @@
// defines different connection states
export enum ConnectionState { export enum ConnectionState {
connecting = 'connecting', connecting = 'connecting',
connected = 'connected', connected = 'connected',
disconnected = 'disconnected', disconnected = 'disconnected',
} }
// defines message types
export enum MessageType { export enum MessageType {
assign_id = 'assign_id', assign_id = 'assign_id',
send_hl7v2 = 'send_hl7v2', send_hl7v2 = 'send_hl7v2',
@ -11,10 +14,12 @@ export enum MessageType {
delivery_error = 'delivery_error', delivery_error = 'delivery_error',
} }
// defines body of message types
export type Message = export type Message =
| { type: MessageType.assign_id, payload: { stationId: string }} | { type: MessageType.assign_id, payload: { stationId: string }}
| { type: MessageType.send_hl7v2, payload: { message: string }} | { type: MessageType.send_hl7v2, payload: { message: string }}
| { type: MessageType.receive_hl7v2, payload: { message: string, timestamp: string }} | { type: MessageType.receive_hl7v2, payload: { message: string, timestamp: string }}
| { type: MessageType.delivery_error, payload: { error: string }} | { type: MessageType.delivery_error, payload: { error: string }}
// required to define list of this message type
export type ReceiveHl7v2Message = Extract<Message, { type: MessageType.receive_hl7v2 }>; export type ReceiveHl7v2Message = Extract<Message, { type: MessageType.receive_hl7v2 }>;