const fs = require('fs'); const config = require('../../config/app'); function normalizeConnectorType(type) { const value = String(type || '').trim().toLowerCase(); if (value === 'serial' || value === 'astm-serial') return 'astm-serial'; if (value === 'tcp-server' || value === 'hl7-tcp') return 'hl7-tcp'; if (value === 'http-json' || value === 'http') return 'http-json'; if (value === 'tcp-client') return 'tcp-client'; return ''; } function defaultParserForConnector(connector) { if (connector === 'astm-serial') return 'astm'; if (connector === 'hl7-tcp') return 'hl7'; if (connector === 'http-json') return 'http-json'; return ''; } function toEntityRows(entities = []) { return entities.map((entity) => { const connector = entity.connector && typeof entity.connector === 'object' ? entity.connector : {}; return { instrument_id: entity.instrument_id, enabled: entity.enabled, connector: connector.type, connectorConfig: connector, config: entity.config, translator: entity.translator }; }); } class InstrumentConfigValidationError extends Error { constructor(errors) { super(`instrument config validation failed with ${errors.length} issue(s)`); this.name = 'InstrumentConfigValidationError'; this.errors = errors; } } function validateAndLoadInstrumentConfigs({ instruments = config.instrumentEntities?.length ? toEntityRows(config.instrumentEntities) : config.instruments, configFilePath = config.configPath } = {}) { const errors = []; const entries = []; const instrumentIds = new Set(); if (configFilePath && !fs.existsSync(configFilePath)) { errors.push(`config file not found: ${configFilePath}`); throw new InstrumentConfigValidationError(errors); } if (!Array.isArray(instruments)) { errors.push('config.instruments: expected an array'); throw new InstrumentConfigValidationError(errors); } if (!instruments.length) { errors.push('config.instruments: array cannot be empty'); } for (let index = 0; index < instruments.length; index += 1) { const item = instruments[index]; const label = `instrument[${index}]`; if (!item || typeof item !== 'object' || Array.isArray(item)) { errors.push(`${label}: must be an object`); continue; } const instrumentId = item.instrument_id; const connector = normalizeConnectorType(item.connector); const translator = item.translator; if (!instrumentId || typeof instrumentId !== 'string') { errors.push(`${label}: instrument_id is required`); continue; } if (instrumentIds.has(instrumentId)) { errors.push(`${label}: duplicate instrument_id "${instrumentId}"`); continue; } instrumentIds.add(instrumentId); if (!connector) { errors.push(`${label}: connector.type is required`); continue; } if (connector === 'tcp-client') { errors.push(`${label}: connector.type tcp-client is not supported yet`); continue; } if (!translator || typeof translator !== 'object' || Array.isArray(translator)) { item.translator = { parser: defaultParserForConnector(connector) }; } const resolvedTranslator = item.translator; if (!resolvedTranslator.parser || typeof resolvedTranslator.parser !== 'string') { errors.push(`${label}: translator.parser is required`); continue; } const connectorConfig = item.connectorConfig && typeof item.connectorConfig === 'object' ? item.connectorConfig : {}; const match = item.match && typeof item.match === 'object' ? { ...item.match } : {}; if (connector === 'astm-serial') { const comPort = connectorConfig.port || connectorConfig.comPort; if (!comPort || typeof comPort !== 'string') { errors.push(`${label}: connector.port is required for serial`); continue; } match.comPort = comPort; } if (connector === 'hl7-tcp' || connector === 'http-json') { const localPort = connectorConfig.port || connectorConfig.localPort; if (localPort !== undefined && localPort !== null && localPort !== '') { match.localPort = Number(localPort); } } entries.push({ instrument_id: instrumentId, connector, enabled: item.enabled !== false, match, config: item.config || {}, translator: resolvedTranslator, connectorConfig, files: { config: configFilePath } }); } if (errors.length) { throw new InstrumentConfigValidationError(errors); } return entries; } module.exports = { InstrumentConfigValidationError, validateAndLoadInstrumentConfigs };