npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

astm-universal-server-nestjs

v1.0.1

Published

Universal ASTM Laboratory Server built with NestJS

Readme

Universal ASTM Laboratory Server - NestJS

Tổng quan hệ thống

Universal ASTM Laboratory Server là một hệ thống server TCP hiện đại được xây dựng bằng NestJS, chuyên dụng để kết nối và xử lý dữ liệu từ các thiết bị xét nghiệm y tế trong phòng thí nghiệm. Server hỗ trợ nhiều giao thức truyền thông khác nhau và cung cấp khả năng giám sát thời gian thực thông qua WebSocket.

🔄 LUỒNG XỬ LÝ DỮ LIỆU - TỪ KHI NHẬN KẾT QUẢ ĐẾN XỬ LÝ CUỐI CÙNG

1. Khởi động hệ thống (main.ts:5-31)

Hàm: bootstrap()

  • Công dụng: Khởi tạo và cấu hình toàn bộ ứng dụng NestJS
  • Gọi các hàm:
    • NestFactory.create(AppModule) - Tạo instance NestJS
    • app.useGlobalPipes() - Cấu hình validation
    • app.enableCors() - Bật CORS
    • app.listen(port) - Lắng nghe HTTP trên port 3000
  • Kết quả: Server HTTP chạy trên port 3000, TCP server tự động khởi động

2. Khởi tạo TCP Server (tcp-server.service.ts:33-73)

Hàm: onModuleInit()start()

  • Công dụng: Khởi động TCP server để nhận dữ liệu từ thiết bị lab
  • Gọi các hàm:
    • net.createServer() - Tạo TCP server
    • server.listen() - Lắng nghe trên port 1008
    • eventEmitter.emit('server.started') - Thông báo server đã sẵn sàng
  • Kết quả: TCP server đang lắng nghe các kết nối từ thiết bị lab

3. Thiết bị kết nối (tcp-server.service.ts:90-138)

Hàm: handleConnection(socket)

  • Công dụng: Xử lý khi có thiết bị xét nghiệm kết nối TCP mới
  • Gọi các hàm:
    • deviceService.findDevice(ip, port) - Tìm thiết bị trong config
    • deviceService.getDeviceName(ip, port) - Lấy tên thiết bị
    • deviceService.isDeviceKnown(ip, port) - Kiểm tra thiết bị có trong danh sách
    • deviceService.addClient(clientInfo) - Thêm client vào danh sách quản lý
  • Kết quả: ClientInfo được tạo và quản lý, socket events được đăng ký

4. Nhận dữ liệu từ thiết bị (tcp-server.service.ts:140-181)

Hàm: handleData(clientInfo, data, socket)

  • Công dụng: Xử lý mỗi packet dữ liệu nhận được từ máy xét nghiệm
  • Gọi các hàm:
    • deviceService.updateClientActivity(clientId) - Cập nhật thời gian hoạt động
    • detectProtocol(data) - Phát hiện giao thức (ASTM/HL7/JSON...)
    • eventEmitter.emit('protocol.detected') - Thông báo phát hiện giao thức
    • eventEmitter.emit('data.received') - Broadcast dữ liệu thô
    • processProtocolData() - Xử lý theo giao thức cụ thể
  • Kết quả: DeviceData object được tạo và truyền qua event system

5. Phát hiện giao thức (tcp-server.service.ts:183-226)

Hàm: detectProtocol(data)

  • Công dụng: Tự động xác định giao thức của dữ liệu nhận được
  • Logic xử lý:
    • ASTM: Kiểm tra bytes 0x05, 0x02, 0x04, 0x03 hoặc pattern H|\\^&
    • HL7: Kiểm tra byte 0x0B hoặc bắt đầu với MSH|
    • JSON: Validate cấu trúc JSON hợp lệ
    • XML: Kiểm tra XML tags
    • LIS2-A2: Kiểm tra frame format
    • Raw: Mặc định cho dữ liệu không nhận dạng được
  • Kết quả: Trả về string protocol type

6. Xử lý theo giao thức (tcp-server.service.ts:228-248)

Hàm: processProtocolData(protocol, deviceData, socket)

  • Công dụng: Xử lý và ghi log theo từng loại giao thức cụ thể
  • Gọi các hàm:
    • logProtocolData(protocol, deviceData) - Ghi log với format phù hợp
    • eventEmitter.emit('astm.message') - Cho ASTM (kèm gửi ACK)
    • eventEmitter.emit('hl7.message') - Cho HL7
    • eventEmitter.emit('raw.data') - Cho dữ liệu thô
  • Xử lý đặc biệt: Gửi ACK response cho ASTM protocol
  • Kết quả: Events được emit theo loại giao thức

7. Ghi log dữ liệu (tcp-server.service.ts:250-283)

Hàm: logProtocolData(protocol, deviceData)

  • Công dụng: Format và ghi log theo từng loại giao thức
  • Gọi các hàm format:
    • formatAstmLogEntry() - Xử lý ASTM: bỏ control chars, chỉ lưu records hợp lệ
    • formatHl7LogEntry() - Xử lý HL7: phân tích message type
    • formatJsonLogEntry() - Xử lý JSON: extract test type
    • formatXmlLogEntry() - Xử lý XML: lấy root element
    • formatRawLogEntry() - Xử lý raw data: preview 100 ký tự
  • Cuối cùng gọi: loggingService.appendToSessionLog() - Ghi vào file log
  • Kết quả: Log entry được format và truyền đến logging service

8. Ghi log session (logging.service.ts:59-76)

Hàm: appendToSessionLog(clientInfo, data)

  • Công dụng: Ghi dữ liệu vào cả file log và database
  • Gọi các hàm:
    • createSessionLogFile(clientInfo) - Tạo file log nếu chưa có
    • fs.appendFileSync() - Ghi vào file log
    • dataLogsService.saveDataLog(clientInfo, data) - Lưu vào database
  • Kết quả: Dữ liệu được lưu song song vào file và database

9. Tạo file log session (logging.service.ts:27-56)

Hàm: createSessionLogFile(clientInfo)

  • Công dụng: Tạo file log riêng biệt cho mỗi session kết nối
  • Format tên file: {DeviceName}_{IP}_{Port}_{Timestamp}.txt
  • Tạo header: Thông tin thiết bị, IP, port, thời gian kết nối
  • Gọi: fs.writeFileSync() - Tạo file với header
  • Kết quả: Đường dẫn file log được trả về và lưu trong clientInfo

10. Lưu vào database (data-logs.service.ts:46-93)

Hàm: saveDataLog(clientInfo, rawData)

  • Công dụng: Lưu dữ liệu vào bảng data_logs trong SQL Server
  • Gọi các hàm xử lý:
    • generateSessionId(clientInfo) - Tạo session ID duy nhất
    • cleanData(rawDataString) - Làm sạch dữ liệu (bỏ control characters)
    • detectDataType(cleanedData) - Xác định loại dữ liệu (HL7/ASTM/Lab_Result...)
    • getNextSequence(sessionId) - Tạo số thứ tự message trong session
    • dataLogsRepository.save() - Lưu vào database với TypeORM
  • Kết quả: Record được lưu vào bảng data_logs với đầy đủ metadata

11. Broadcast qua WebSocket (lab.gateway.ts:87-177)

Event listeners nhận và xử lý:

  • handleDeviceConnected() - Khi thiết bị kết nối
  • handleDeviceDisconnected() - Khi thiết bị ngắt kết nối
  • handleDataReceived() - Khi nhận dữ liệu thô
  • handleAstmMessage() - Khi nhận ASTM message
  • handleHl7Message() - Khi nhận HL7 message
  • handleRawData() - Khi nhận raw data

Công dụng: Broadcast thông tin real-time đến các WebSocket clients Gọi các hàm:

  • server.to(room).emit() - Gửi đến specific room subscribers
  • server.emit() - Broadcast đến tất cả clients
  • Kết quả: Frontend nhận updates real-time

12. API endpoints (lab-server.controller.ts)

REST API cung cấp các chức năng:

  • getStatus() - Trạng thái server và thiết bị đang kết nối
  • getDevices() - Danh sách tất cả thiết bị đang kết nối
  • getDevice(clientId) - Thông tin chi tiết một thiết bị cụ thể
  • getDevicesByIP(ip) - Lọc thiết bị theo IP address
  • getLogs() - Danh sách tất cả file log sessions
  • getLogContent(filename) - Đọc nội dung file log cụ thể
  • getStats() - Thống kê chi tiết hệ thống và performance

Gọi các service methods:

  • tcpServerService.getStatus() - Server status
  • deviceService.getServerStatus() - Device status
  • labGateway.getConnectedClientsCount() - WebSocket clients
  • loggingService.getLogFiles() - Log files list

🎯 Tóm tắt luồng hoàn chỉnh từ A-Z:

[Thiết bị Lab]
    ↓ (TCP connection)
[TCP Server:1008]
    ↓ (socket.on('data'))
[handleData]
    ↓ (detectProtocol)
[Protocol Detection]
    ↓ (processProtocolData)
[Format & Log]
    ↓ (appendToSessionLog)
[File + Database]
    ↓ (eventEmitter.emit)
[WebSocket Broadcast]
    ↓ (real-time)
[Frontend Clients]

Mỗi bước đều có error handling, logging chi tiết và event emission để đảm bảo tính ổn định và khả năng truy vết hoàn chỉnh của hệ thống.

🚀 Tính năng chính

1. TCP Server đa giao thức

  • Tự động nhận diện giao thức: ASTM, HL7, JSON, XML, LIS2-A2, Raw data
  • Kết nối đồng thời: Hỗ trợ nhiều thiết bị kết nối cùng lúc
  • Quản lý timeout: Tự động ngắt kết nối các client không hoạt động

2. Nhận diện thiết bị thông minh

  • Mapping thiết bị: Tự động nhận diện thiết bị dựa trên IP và port
  • Danh sách thiết bị được cấu hình sẵn: Bao gồm các máy xét nghiệm phổ biến
  • Xử lý thiết bị không xác định: Vẫn cho phép kết nối và xử lý dữ liệu

3. WebSocket Gateway (Thời gian thực)

  • Broadcasting events: Phát sóng sự kiện đến tất cả client đã kết nối
  • Room-based subscription: Client có thể đăng ký theo dõi các sự kiện cụ thể
  • Real-time monitoring: Giám sát trạng thái server và thiết bị trực tiếp

4. Logging và Monitoring

  • Session logs: Lưu trữ toàn bộ dữ liệu giao tiếp với từng thiết bị
  • API monitoring: Theo dõi trạng thái server và thống kê kết nối
  • Device statistics: Thống kê chi tiết về các thiết bị đã kết nối

🔧 Cấu trúc hệ thống

TCP Server Service

Thành phần cốt lõi xử lý kết nối TCP và nhận diện giao thức:

Quy trình xử lý kết nối:

  1. Nhận kết nối mới → Tạo clientId duy nhất
  2. Nhận diện thiết bị → Tra cứu trong device.config.ts dựa trên IP:Port
  3. Detect protocol → Phân tích dữ liệu đầu tiên để xác định giao thức
  4. Xử lý dữ liệu → Forward đến module xử lý tương ứng
  5. Logging → Ghi lại toàn bộ session

Device Service

Quản lý thông tin thiết bị và client kết nối:

  • Lưu trữ danh sách client đang kết nối
  • Emit events khi có thiết bị kết nối/ngắt kết nối
  • Cung cấp API để truy vấn thông tin thiết bị

WebSocket Gateway

Cung cấp giao tiếp thời gian thực:

  • Broadcast events đến WebSocket clients
  • Hỗ trợ subscription theo room/topic
  • Realtime monitoring cho frontend applications

🔍 Nhận diện giao thức

Server tự động phát hiện giao thức dựa trên đặc điểm dữ liệu:

ASTM (Automated Sequential Treatment Machine)

- Byte đầu: 0x05 (ENQ) hoặc 0x02 (STX)
- Byte cuối: 0x04 (EOT) hoặc 0x03 (ETX)
- Pattern: H|\^& (Header record)
- Format: [HPORCL]\d*\| (Record types)

HL7 (Health Level 7)

- Byte đầu: 0x0B (VT - Vertical Tab)
- Bắt đầu với: MSH| (Message Header)
- Kết thúc với: \r (Carriage Return)

Các giao thức khác

  • JSON: Phát hiện cấu trúc {...} và validate JSON
  • XML: Phát hiện tags <...>
  • LIS2-A2: Kiểm tra control characters (SOH, STX, etc.)
  • Raw: Dữ liệu không thuộc các loại trên

🏥 Cấu hình thiết bị

File src/config/device.config.ts chứa mapping các thiết bị phòng thí nghiệm:

Các thiết bị được hỗ trợ:

  • Máy xét nghiệm HbA1c AdamA1C
  • Máy Cobas6000 (nhiều máy)
  • Máy điện giải Erba
  • Máy xét nghiệm đông máu CA600
  • Máy xét nghiệm nước tiểu UraM/U411
  • Máy cobas E601
  • Máy định danh kháng sinh Vitek 2

📡 WebSocket Events

Client Events (Gửi đến server)

  • subscribe: Đăng ký theo dõi events
  • unsubscribe: Hủy đăng ký events
  • getStatus: Lấy trạng thái server

Server Events (Phát từ server)

  • device.connected: Thiết bị kết nối
  • device.disconnected: Thiết bị ngắt kết nối
  • protocol.detected: Phát hiện giao thức mới
  • data.received: Nhận dữ liệu từ thiết bị
  • astm.message: Tin nhắn ASTM cụ thể
  • hl7.message: Tin nhắn HL7 cụ thể
  • raw.data: Dữ liệu raw

🔬 Luồng xử lý kết quả xét nghiệm

Quy trình xử lý từ A-Z khi nhận 1 kết quả xét nghiệm

Bước 1: Thiết bị kết nối và nhận diện

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   Thiết bị Lab  │───▶│   TCP Server     │───▶│ Device Service  │
│  (Cobas6000)    │    │  (Port 1008)     │    │ (Nhận diện)     │
└─────────────────┘    └──────────────────┘    └─────────────────┘
         │                       │                       │
         │                       │                       ▼
         │                       │              ✅ Mapping IP:Port
         │                       │              📍 "Máy Cobas6000 số 1"
         │                       │              🔍 isKnownDevice = true

Bước 2: Phát hiện giao thức từ dữ liệu đầu tiên

// Ví dụ dữ liệu ASTM từ Cobas6000
const astmData = "\u0005H|\\^&|||Cobas6000^123456||||||P||20250915\r\u0004";

// Logic detection trong tcp-server.service.ts:188
private detectProtocol(data: Buffer): string {
  const firstByte = data[0];  // 0x05 (ENQ)
  const lastByte = data[data.length - 1]; // 0x04 (EOT)

  if (firstByte === 0x05 || dataStr.includes('H|\\^&')) {
    return 'astm'; // ✅ Xác định là ASTM
  }
  // ... logic khác
}

Bước 3: Xử lý dữ liệu và tạo DeviceData object

// Tạo đối tượng DeviceData chuẩn hóa
const deviceData: DeviceData = {
  clientId: "client_1726388445123_abc123",
  protocol: "astm",
  data: "H|\\^&|||Cobas6000^123456||||||P||20250915\r",
  timestamp: new Date(),
  deviceInfo: {
    remoteAddress: "192.168.25.107",
    remotePort: 52341,
    localPort: 10008,
    deviceName: "Máy Cobas6000 số 1",
    connectionId: "client_1726388445123_abc123",
    isKnownDevice: true
  },
  sessionLogFile: "May_Cobas6000_so_1_192.168.25.107_10008_2025-09-15T14-20-45.txt"
};

Bước 4: Logging session chi tiết

📁 ./logs/May_Cobas6000_so_1_192.168.25.107_10008_2025-09-15T14-20-45.txt

=== LOG SESSION ===
Thiết bị: Máy Cobas6000 số 1
IP: 192.168.25.107
Port: 10008
Kết nối lúc: 2025-09-15T14:20:45.123Z
Protocol: ASTM
=====================================

[2025-09-15T14:20:45.123Z] [Máy Cobas6000 số 1] [ASTM] ENQ received
[2025-09-15T14:20:45.124Z] [Máy Cobas6000 số 1] [ASTM] ACK sent
[2025-09-15T14:20:45.200Z] [Máy Cobas6000 số 1] [ASTM] H|\\^&|||Cobas6000^123456||||||P||20250915
[2025-09-15T14:20:45.201Z] [Máy Cobas6000 số 1] [ASTM] P|1|||NguyenVanA^123456789||19900101|M
[2025-09-15T14:20:45.202Z] [Máy Cobas6000 số 1] [ASTM] O|1|Sample001||GLU^Glucose|||||||||||
[2025-09-15T14:20:45.203Z] [Máy Cobas6000 số 1] [ASTM] R|1|^^^GLU|120.5|mg/dL|70.0-110.0|H|||F

Bước 5: Broadcasting Events thông qua EventEmitter

// Trong tcp-server.service.ts:182-185
this.eventEmitter.emit('data.received', deviceData);

// Xử lý theo từng loại giao thức
switch (protocol) {
  case 'astm':
    this.eventEmitter.emit('astm.message', deviceData);
    // Gửi ACK cho ASTM protocol
    if (deviceData.data.includes('\u0005')) { // ENQ
      socket.write('\u0006'); // ACK
    }
    break;
}

Bước 6: WebSocket Broadcasting cho Real-time Monitoring

// Trong lab.gateway.ts:179-202
@OnEvent('astm.message')
handleAstmMessage(deviceData: DeviceData) {
  // Gửi đến client đã subscribe 'astm.message'
  this.server.to('astm.message').emit('astm.message', {
    clientId: deviceData.clientId,
    deviceName: deviceData.deviceInfo.deviceName,
    data: deviceData.data,
    timestamp: deviceData.timestamp,
    sessionLogFile: deviceData.sessionLogFile,
  });

  // Broadcast tổng quát đến tất cả clients
  this.server.emit('astm.message', {
    type: 'astm_message',
    data: {
      deviceName: "Máy Cobas6000 số 1",
      dataPreview: "H|\\^&|||Cobas6000^123456...",
      timestamp: "2025-09-15T14:20:45.203Z"
    }
  });
}

Bước 7: Frontend nhận real-time update

// Frontend WebSocket client
const socket = io('http://localhost:3000/lab');

socket.on('astm.message', (data) => {
  console.log('🧪 Kết quả xét nghiệm mới từ:', data.deviceName);
  console.log('📊 Dữ liệu:', data.dataPreview);

  // Update UI real-time
  updateLabResultsTable({
    device: data.deviceName,
    timestamp: data.timestamp,
    preview: data.dataPreview
  });
});

Ví dụ cụ thể: Xử lý kết quả Glucose từ Cobas6000

1. Raw ASTM Message nhận được:

\u0005                                    # ENQ (Enquiry)
H|\\^&|||Cobas6000^123456||||||P||20250915\r  # Header Record
P|1|||NguyenVanA^123456789||19900101|M\r      # Patient Record
O|1|Sample001||GLU^Glucose||||||||||||\r      # Order Record
R|1|^^^GLU|120.5|mg/dL|70.0-110.0|H|||F\r    # Result Record
L|1|N\r                                       # Terminator Record
\u0004                                    # EOT (End of Transmission)

2. Parsing và extract thông tin:

// Parsing ASTM result record
const resultRecord = "R|1|^^^GLU|120.5|mg/dL|70.0-110.0|H|||F";
const fields = resultRecord.split('|');

const parsedResult = {
  recordType: 'R',           // Result Record
  sequenceNumber: '1',       // Sequence #1
  testCode: 'GLU',          // Glucose test
  value: '120.5',           // Result value
  unit: 'mg/dL',            // Unit
  referenceRange: '70.0-110.0', // Normal range
  abnormalFlag: 'H',        // High (above normal)
  resultStatus: 'F'         // Final result
};

3. Enriched data object được tạo:

const enrichedResult = {
  ...deviceData,
  parsedData: {
    patient: {
      id: 'NguyenVanA',
      sampleId: '123456789',
      birthDate: '1990-01-01',
      gender: 'M'
    },
    test: {
      code: 'GLU',
      name: 'Glucose',
      value: 120.5,
      unit: 'mg/dL',
      normalRange: '70.0-110.0',
      status: 'HIGH',
      flag: 'H'
    },
    metadata: {
      sampleId: 'Sample001',
      resultStatus: 'Final',
      testDate: '2025-09-15'
    }
  }
};

4. Event cascade và notifications:

📡 Events được emit theo thứ tự:
1. 'device.connected'     → UI hiển thị thiết bị online
2. 'protocol.detected'    → UI hiển thị "ASTM detected"
3. 'data.received'        → General data logging
4. 'astm.message'         → Specific ASTM processing
5. WebSocket broadcast    → Frontend update real-time
6. Session log update     → File system logging

Xử lý các trường hợp đặc biệt

Lỗi transmission:

// Nếu dữ liệu bị lỗi hoặc incomplete
socket.on('error', (error) => {
  this.logger.error(`❌ Lỗi truyền dữ liệu từ ${clientInfo.deviceName}:`, error);
  this.eventEmitter.emit('transmission.error', {
    clientId: clientInfo.id,
    deviceName: clientInfo.deviceName,
    error: error.message,
    timestamp: new Date()
  });
});

Timeout handling:

// Client timeout sau 30 giây không hoạt động
socket.setTimeout(30000);
socket.on('timeout', () => {
  this.logger.warn(`⏱️ Timeout thiết bị: ${clientInfo.deviceName}`);
  socket.destroy();
});

Protocol ACK/NAK responses:

// ASTM cần ACK response
if (deviceData.data.includes('\u0005')) {  // ENQ
  socket.write('\u0006');  // Send ACK
  this.logger.log(`✅ ACK sent to ${deviceInfo.deviceName}`);
} else if (deviceData.data.includes('\u0002')) {  // STX
  // Process data and send ACK
  socket.write('\u0006');
}

Luồng này đảm bảo mọi kết quả xét nghiệm được xử lý một cách đáng tin cậy, có thể truy vết và cung cấp monitoring real-time cho hệ thống lab.

⚡ Hướng dẫn cài đặt

1. Cài đặt dependencies

cd nestjs-server
npm install

2. Cấu hình môi trường

Tạo file .env:

PORT=3000              # Port cho HTTP API
TCP_PORT=1008          # Port TCP cho thiết bị lab
TCP_HOST=0.0.0.0       # Listen trên tất cả interfaces
LOG_DIRECTORY=./logs   # Thư mục lưu log files
DEBUG=true             # Bật debug logging
CLIENT_TIMEOUT=30000   # Timeout cho client (ms)

3. Chạy server

# Development mode
npm run start:dev

# Production mode
npm run build
npm run start:prod

# Debug mode
npm run start:debug

4. Kết nối thiết bị

Cấu hình các thiết bị xét nghiệm để kết nối đến:

  • IP: Địa chỉ IP của server
  • Port: Port cụ thể cho từng thiết bị (xem bảng cấu hình thiết bị)

📋 Danh sách thiết bị được hỗ trợ

Server tự động nhận diện các thiết bị sau:

| Tên thiết bị | Địa chỉ IP | Port | |-------------|------------|------| | Máy XN HbA1c AdamA1C | 192.168.25.107 | 10001 | | Máy Cobas6000 số 1 | 192.168.25.107 | 10008 | | Máy điện giải Erba số 2 | 192.168.25.107 | 10003 | | Máy Cobas6000 số 2 | 192.168.3.127 | 10004 | | Máy XN đông máu CA600 số 1 | 192.168.3.127 | 10006 | | Máy Xn nước tiểu UraM | 192.168.3.127 | 10007 | | Máy điện giải Erba số 1 | 192.168.3.127 | 10001 | | Máy cobas E601 | 192.168.3.233 | 10002 | | Máy đông máu CA600 số 2 | 192.168.3.233 | 10003 | | Máy định danh KSĐ Vitek 2 | 192.168.3.233 | 10005 | | Máy Cobas 6000 | 10.10.4.215 | 10004 | | Máy điện giải Elyte | 10.10.4.215 | 10002 | | Máy Xn nước tiểu U411 | 10.10.4.215 | 30024 |

📊 API Endpoints

Monitoring APIs

  • GET /lab-server/status - Trạng thái tổng quan server
  • GET /lab-server/devices - Danh sách thiết bị đang kết nối
  • GET /lab-server/devices/:clientId - Thông tin chi tiết một thiết bị
  • GET /lab-server/devices/by-ip/:ip - Thiết bị theo IP
  • GET /lab-server/stats - Thống kê chi tiết
  • GET /lab-server/logs - Danh sách log files
  • GET /lab-server/logs/:filename - Nội dung log file
  • GET /lab-server/logs/:filename?tail=100 - N dòng cuối của log
  • GET /health - Health check cơ bản

🌐 WebSocket Integration

Kết nối WebSocket

const socket = io('http://localhost:3000/lab');

// Lắng nghe kết nối thành công
socket.on('connected', (data) => {
  console.log('🧪 Kết nối thành công:', data.message);
});

Đăng ký theo dõi sự kiện

// Đăng ký theo dõi các sự kiện cụ thể
socket.emit('subscribe', {
  events: ['device.connected', 'astm.message', 'hl7.message']
});

// Lắng nghe thiết bị kết nối
socket.on('device.connected', (data) => {
  console.log('📱 Thiết bị kết nối:', data.deviceName);
});

// Lắng nghe dữ liệu ASTM
socket.on('astm.message', (data) => {
  console.log('🧪 Dữ liệu ASTM từ:', data.deviceName);
});

// Lấy trạng thái server
socket.emit('getStatus');
socket.on('status', (status) => {
  console.log('📊 Trạng thái server:', status);
});

📝 Format Log Files

Tên file log

TenThietBi_IP_Port_Timestamp.txt
May_Cobas6000_so_1_192.168.25.107_10008_2025-09-12T11-30-45.txt

Định dạng nội dung log

=== LOG SESSION ===
Thiết bị: Máy Cobas6000 số 1
IP: 192.168.25.107
Port: 10008
Kết nối lúc: 2025-09-12T11:30:45.123Z
=====================================

[2025-09-12T11:30:45.123Z] [Máy Cobas6000 số 1] <Dữ liệu ASTM>
[2025-09-12T11:30:50.456Z] [Máy Cobas6000 số 1] <Dữ liệu tiếp theo>

🏗️ Kiến trúc hệ thống

Các Services chính

  • TcpServerService: Xử lý TCP connections và protocol detection
  • DeviceService: Quản lý thông tin thiết bị và client connections
  • LoggingService: Ghi log và quản lý session data
  • LabGateway: WebSocket gateway cho real-time communication

Event System

Sử dụng NestJS EventEmitter cho giao tiếp không đồng bộ:

  • Kết nối/ngắt kết nối thiết bị
  • Phát hiện giao thức
  • Nhận và xử lý dữ liệu
  • Xử lý lỗi và exception

Cơ chế hoạt động

  1. TCP Server lắng nghe trên port cấu hình
  2. Device Service nhận diện thiết bị khi có kết nối mới
  3. Protocol Detection tự động phát hiện giao thức từ dữ liệu
  4. Event Emitter phát sóng events đến các module khác
  5. WebSocket Gateway broadcast real-time updates
  6. Logging Service ghi lại toàn bộ session

🛠️ Development

Lệnh phát triển

npm run start:dev        # Development với hot reload
npm run start:debug      # Debug mode
npm run build           # Build cho production
npm run test            # Chạy tests
npm run lint            # Lint code
npm run format          # Format code

Thêm thiết bị mới

Chỉnh sửa file src/config/device.config.ts:

export const labDeviceMapping: DeviceInfo[] = [
  // Thêm thiết bị mới
  { "ip": "192.168.1.100", "name": "Tên thiết bị mới", "port": 10009 },
  // ... các thiết bị hiện có
];

Tạo Protocol Handler mới

Tạo handler mới trong src/services/ và đăng ký trong TCP server service.

Cấu trúc thư mục

src/
├── config/           # Cấu hình thiết bị và hệ thống
├── dto/              # Data Transfer Objects
├── gateways/         # WebSocket gateways
├── interfaces/       # TypeScript interfaces
├── lab-server/       # Lab server module
├── services/         # Business logic services
└── main.ts           # Entry point

🚀 Triển khai Production

Docker (Khuyến nghị)

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY dist ./dist
EXPOSE 3000 1008
CMD ["npm", "run", "start:prod"]

PM2 Process Manager

npm install -g pm2
npm run build
pm2 start dist/main.js --name "astm-lab-server"
pm2 startup
pm2 save

Systemd Service

[Unit]
Description=Universal ASTM Lab Server
After=network.target

[Service]
Type=simple
User=labuser
WorkingDirectory=/opt/astm-lab-server
ExecStart=/usr/bin/node dist/main.js
Restart=always
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target

📈 Monitoring và Logging

Các loại logs

  • Application logs: Console output với timestamp
  • Session logs: Thư mục ./logs/ cho từng thiết bị
  • WebSocket events: Real-time events trong browser console

Health Checks

# Kiểm tra sức khỏe server
curl http://localhost:3000/health

# Trạng thái đầy đủ
curl http://localhost:3000/lab-server/status

# Thống kê chi tiết
curl http://localhost:3000/lab-server/stats

Real-time Monitoring

Sử dụng WebSocket để theo dõi:

  • Kết nối/ngắt kết nối thiết bị
  • Dữ liệu đến từ thiết bị
  • Trạng thái server
  • Thống kê performance

🔒 Bảo mật

CORS Configuration

app.enableCors({
  origin: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
});

Validation

Sử dụng class-validatorValidationPipe để validate input data.

Khuyến nghị bảo mật

  • Cấu hình firewall cho TCP ports
  • Sử dụng SSL/TLS cho WebSocket trong production
  • Implement authentication cho REST API nếu cần
  • Monitor log file sizes và implement rotation

🚨 Xử lý lỗi và Troubleshooting

Graceful Shutdown

process.on('SIGINT', async () => {
  logger.log('🛑 Received shutdown signal...');
  // Cleanup resources
  process.exit(0);
});

Các lỗi thường gặp

  1. Xung đột port: Kiểm tra TCP_PORT không bị service khác sử dụng
  2. Thiết bị kết nối thất bại: Kiểm tra cấu hình IP/port trong thiết bị
  3. Lỗi WebSocket: Verify CORS settings và port accessibility
  4. Quyền thư mục log: Đảm bảo quyền ghi cho LOG_DIRECTORY
  5. Timeout detection: Tự động ngắt kết nối client không hoạt động

Debug Mode

Bật debug logging bằng cách set DEBUG=true trong file .env.

Client Error Handling

  • Timeout detection và auto-disconnect
  • Error logging cho debugging
  • Event emission cho monitoring

📝 Ghi chú quan trọng

  1. Port Configuration: Mỗi thiết bị cần được cấu hình với IP và port cụ thể
  2. Protocol Detection: Tự động nhưng có thể override nếu cần
  3. Real-time Updates: WebSocket cung cấp updates tức thời
  4. Logging: Toàn bộ dữ liệu được ghi log để audit và debugging
  5. Scalability: Có thể mở rộng để hỗ trợ thêm nhiều giao thức

📞 Liên hệ và Hỗ trợ

Để được hỗ trợ kỹ thuật hoặc đóng góp vào dự án, vui lòng liên hệ team phát triển hoặc tạo issue trên repository.

📄 License

MIT License - Tự do sử dụng trong môi trường phòng thí nghiệm.


Universal ASTM Laboratory Server - Connecting Your Lab Equipment Seamlessly 🧪🔬