VitalRecorder – All Versions
You can browse all available versions by OS and download any version directly.
Total downloads since Feb 2017: 182,983
| Version | Date | Changes | |
|---|---|---|---|
| 1.19.9 | 2026-08-14 | 1.19.9 Fixed: 큰 녹화 파일(약 100MB 이상)을 서버로 올릴 때 프로그램이 메모리 부족으로 강제 종료되고, 재시작 후 같은 파일을 다시 시도하다 무한 반복되던 문제 — 업로드 본문을 메모리에 통째로 만들지 않고 디스크에서 흘려보내도록 변경(100MB 파일 기준 최대 메모리 208MB → 14MB). 큰 파일이 한 개라도 쌓이면 그 장비의 이후 업로드가 전부 막히던 증상이 사라진다. 1.19.8 Fixed: 자동 파일 자르기(CUT_FILE) 를 쓰는 베드에서 환자가 계속 연결돼 있는데도 녹화 파일이 PT_WAITING_TIME(기본 5분) 마다 잘리던 문제 — 웹 모니터링 전송 후 메모리를 비우는 과정에서 환자 연결 판정에 쓰이는 최근 구간까지 지워져 "환자가 나갔다" 로 오판하던 것. 이제 판정에 필요한 구간은 보존한다. 파일이 5분마다 끊기고 그때마다 수 초가 유실되던 증상이 사라진다. 1.19.7 Added: GE Corometrics(분만 태아감시) 모체 vitals 지원 — 기존엔 태아 심박(HR1/HR2)·자궁수축(UACT)만 기록됐으나 이제 같은 장비가 함께 보내는 모체 SpO2/맥박(PR)/NIBP(수축·이완·평균)도 한 파일에 기록. Fixed: 프로세스 종료(서비스 매니저의 정상 종료 요청, SIGTERM) 시 즉시 종료되지 않고 강제 종료되던 문제 — 이제 SIGTERM 을 받아 녹화 버퍼를 flush 하고 스레드를 정리한 뒤 정상 종료. 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.8 | 2026-08-14 | 1.19.8 Fixed: 자동 파일 자르기(CUT_FILE) 를 쓰는 베드에서 환자가 계속 연결돼 있는데도 녹화 파일이 PT_WAITING_TIME(기본 5분) 마다 잘리던 문제 — 웹 모니터링 전송 후 메모리를 비우는 과정에서 환자 연결 판정에 쓰이는 최근 구간까지 지워져 "환자가 나갔다" 로 오판하던 것. 이제 판정에 필요한 구간은 보존한다. 파일이 5분마다 끊기고 그때마다 수 초가 유실되던 증상이 사라진다. 1.19.7 Added: GE Corometrics(분만 태아감시) 모체 vitals 지원 — 기존엔 태아 심박(HR1/HR2)·자궁수축(UACT)만 기록됐으나 이제 같은 장비가 함께 보내는 모체 SpO2/맥박(PR)/NIBP(수축·이완·평균)도 한 파일에 기록. Fixed: 프로세스 종료(서비스 매니저의 정상 종료 요청, SIGTERM) 시 즉시 종료되지 않고 강제 종료되던 문제 — 이제 SIGTERM 을 받아 녹화 버퍼를 flush 하고 스레드를 정리한 뒤 정상 종료. 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.7 | 2026-08-12 | 1.19.7 Added: GE Corometrics(분만 태아감시) 모체 vitals 지원 — 기존엔 태아 심박(HR1/HR2)·자궁수축(UACT)만 기록됐으나 이제 같은 장비가 함께 보내는 모체 SpO2/맥박(PR)/NIBP(수축·이완·평균)도 한 파일에 기록. Fixed: 프로세스 종료(서비스 매니저의 정상 종료 요청, SIGTERM) 시 즉시 종료되지 않고 강제 종료되던 문제 — 이제 SIGTERM 을 받아 녹화 버퍼를 flush 하고 스레드를 정리한 뒤 정상 종료. 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.5 | 2026-08-06 | 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.4 | 2026-08-05 | 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.3 | 2026-08-04 | 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.0 | 2026-08-03 | 1.19.0 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스 전체가 SIGSEGV 로 죽던 문제. accept_handoff 가 교체(replace) 시 옛 소켓을 즉시 closesocket 했는데, m_sock_mtx 는 fd 번호만 보호할 뿐 fd 의 수명은 보호하지 않아 reader 가 그 fd 로 recv 중일 수 있었다. close 는 blocking recv 를 깨우지도 않고 fd 번호를 즉시 재사용 가능하게 만들어, 다음 접속이 같은 번호를 받으면 reader 가 남의 세션을 읽었다. SNUH 회복실(VRN 15대, 최다 베드는 하루 785회 재접속)에서 실제로 크래시 후 9시간 48분 전 베드 무수집. 수정 세 겹: (1) tcp reader 를 poll(POLLIN,200ms) 게이트 뒤의 recv 로 재구성 - recv 가 무기한 블로킹되지 않으므로 close/replace 가 늦어도 200ms 안에, shutdown 이 동반되면 즉시 감지된다. fd 에 O_NONBLOCK 은 걸지 않는다(같은 fd 로 송신하는 write_transport 의 부분 전송 방지). (2) replace 는 closesocket 대신 shutdown 만 하고 옛 fd 를 retired 목록에 넣으며, 닫기는 깨어난 reader 또는 Serial::close 가 정확히 한 번 수행 - fd 번호 재사용 창이 사라진다. (3) Serial::close 가 join 전에 shutdown 으로 reader 를 깨우고(RCVTIMEO 는 설정된 적이 없어 종전에는 피어가 끊을 때까지 join 이 매달렸다), m_sock 접근을 전부 락으로 감싼다. has_socket 의 무락 읽기와 udp_thread_func 의 이중 close 도 같은 계열이라 함께 수정. Added: AUTO_DETECT=1 (vr.conf) - 시리얼 포트에 무엇이 물려 있는지 스스로 알아내 즉시 수집을 시작한다. 판별용 프로브 바이트를 따로 두지 않고 후보 드라이버를 실제로 열어 유효 레코드 도달 여부로 판정하므로 장비별 판별 코드가 없고 드라이버는 무수정이다. 4단계: 회선 측정(커널 프레이밍/패리티/브레이크 카운터로 보레이트와 프레임 폭 확정) -> 패리티 추론(10비트 프레임의 7E1/7O1 대 8N1 모호성을 MSB 상관으로 해소) -> 후보 필터(보레이트+프레임폭+데이터비트 3축 일치, 베드당 환자모니터 1대/마취기 1대 배타, 포트 위치 사전확률, 지난 판별 결과 캐시) -> 남으면 트랙 수확량으로 경쟁. SNUH 수술장 9방 + ICU 3베드 실측에서 Intellivue/MedibusX/Datex-Ohmeda/Primus 를 오탐 0 으로 판별했고, 표준과 다른 포트에 꽂힌 장비(P3/P4 의 Intellivue)도 찾아냈다. AUTO_DETECT 가 없으면 스캐너는 유휴 대기만 하므로 기존 설정에 영향이 없다. | |
| 1.18.52 | 2026-07-31 | 1.18.52 Changed: VR 기본 운영이 k3s 로 이관됨에 따라 서버→VR 하향 제어 경로를 제거. VitalServer 로는 데이터를 일방 push 만 하고, websocket(socket.io) 으로 내려오던 원격 명령 update/restart/reboot/edit_conf/new_bed/del_bed/edit_bed/dt/add_event 를 더 이상 처리하지 않는다 (원격 재부팅·바이너리 교체·conf 덮어쓰기·베드 구성변경 봉쇄). 구현: sendweb_thread_func 의 socket.io 42 이벤트 dispatch 와 on_sio_msg 함수를 통째로 제거. engine.io ping(2->3) 및 ws 레벨 ping/pong 은 연결 유지를 위해 그대로 응답하므로 데이터 업링크·웹모니터링은 종전과 동일. 서버는 VR 버전을 알므로 52+ 로는 명령을 보내지 않고, 받아도 무시. Fixed: --console 모드 (MFC) 에서 stdout 이 이미 유효한 파이프/파일 (> log.txt, ssh 파이프, cmd 상속 핸들) 인데도 무조건 _open_osfhandle+_dup2 로 재배선하면서 그 OS 핸들을 닫아버려, 초기화 이후 모든 로그가 조용히 사라지던 문제 (콘솔 모드 서버 배포에서 진단 불능의 원인, 1.18.7 부터 잠재). stdout 핸들이 유효하면 (GetFileType != UNKNOWN) 재배선하지 않고, 핸들이 없을 때만 (Explorer/schtasks 실행) AttachConsole 로 부모 콘솔에 붙인다. 실측: 종전 초기화 후 로그 침묵 -> 수정 후 지속 출력. (앞서 1.18.51 의 VRQt qDebug->stdout 통일과 함께 세 빌드의 --debug 로그 일관성 완성.) | |
| 1.18.51 | 2026-07-30 | 1.18.51 Fixed: VRQt (Qt 빌드) 에서 --debug 옵션을 켜도 콘솔에 로그가 전혀 출력되지 않던 문제. 원인: Qt 빌드의 TRACE 가 qDebug() 로 매핑되어 있었는데 (UTIL.h, 1.14.4 부터), Windows 에서 Qt 로깅은 GUI 앱이면 콘솔/파이프가 아니라 디버거 (OutputDebugString) 로만 보내므로 VRQt 만 --debug 콘솔 출력이 구조적으로 불가능했다 (MFC 는 stdafx.h 의 #undef TRACE 로 printf 버전을 사용해 정상). 수정: TRACE 를 세 빌드 (MFC/Qt/CLI) 공통 fprintf(stdout)+fflush 로 통일 - qDebug 직접 사용처는 코드에 없음을 확인. 이제 --debug 동작과 리다이렉트 (> log.txt) 가 전 빌드에서 일관되고, cmd 대화형 실행 시에는 기존 AttachConsole/AllocConsole 로직 (1.18.30) 이 콘솔을 만들어 준다. terapc 실측 검증: 종전 VRQt stdout 0 chars → 수정 후 4,690 chars 정상 출력, Vital.exe (MFC) 는 종전과 동일 정상. 추가 방어: --debug 의 콘솔 확보 검사에 GetFileType 추가 - 일부 런처/셸이 non-NULL 무효 stdout 핸들을 상속시키면 NULL 검사만으로는 AttachConsole 을 건너뛰어 로그가 사라질 수 있던 엣지 케이스 (MFC/Qt 공통). | |
| 1.18.50 | 2026-07-30 | 1.18.50 Fixed: 장비 연결이 한번 끊어지면 다시 붙지 않던 문제 (Link+ 현장 리포트, Intellivue 늦은 전원-온과 공통 근본 원인). 근본 원인: DEVICE::open() 의 초기화 호출 set_rx_time(0) 이 값 0 인데도 OPENING→ACTIVE 로 승격시키고 m_dtStatus 까지 0 으로 만들어, (1) 모든 장비가 open 즉시 가짜-ACTIVE (OPENING 상태가 사실상 부재), (2) restart 워치독의 dtlast(rx ?: status_time) 가 0 → read_timeout 조건이 영구 거짓 → 재연결 시 상대 무응답이면 (Link+ rack 재부팅/세션 잔존, 세션 프레임 1회 전송 후 무응답) 재시도 없이 영구 고착. 1.18.35 이하의 rec_timeout (상태 무관 60s 강제 재시작) 이 이 잠재 버그를 우연히 가리고 있었고 1.18.36 전환으로 노출된 것. 수정 3건: (1) set_rx_time 은 실수신 (dt>0) 일 때만 ACTIVE 승격 - 상태 표시도 정직해짐 (open 후 첫 수신까지 "con"), (2) restart 워치독에 OPENING-stuck 분기 추가 - open 후 read_timeout 내 첫 수신이 없으면 재시작해 협상 프레임 재전송, tx_time>0 (protocol-initiator) 조건으로 passive/keyword-sub 디바이스는 제외해 유휴 churn 없음, (3) tx_time 을 모든 transport (socket/fd/COM) 에서 갱신 (기존 Windows COM 만). 영향 장비: Link+/Agilia, Intellivue, S5, Datex, Hamilton 등 open 시 세션/assoc 을 시작하는 모든 protocol-initiator. E2E 검증: 무응답 서버 재현에서 종전 접속 1회 후 영구 침묵 → 수정 후 30초마다 재접속 반복, 장비 복구 시 자동 연결. Mindray 멀티베드 회귀 테스트 PASS (ACK 75/75, tier1 라우팅, probe 거절, stuck 오발동 0). Intellivue 1.18.48 assoc-retry (5s) 는 유지 - 이번 워치독은 30s 백스톱으로 공존. | |
| 1.18.49 | 2026-07-29 | 1.18.49 Fixed: (1) Mindray eGateway HL7 멀티베드 (catch-all + #키워드 sub) 에서 전 베드가 readonly=1 이면 데이터가 전혀 수집되지 않고 게이트웨이가 수초마다 재접속하던 문제. 원인 셋 - (a) readonly catch-all 이 open_socket 에서 조기 반환해 reader/parser 스레드가 아예 생성되지 않음 (아무도 안 읽는 소켓에 데이터가 쌓이다 게이트웨이가 연결 재수립 반복; PortRouter 리팩토링 시 readonly 체크가 blocking accept 앞으로 이동하며 생긴 1.18.36 회귀 - 1.18.31 은 accept 가 먼저라 우연히 동작), (b) readonly 가 HL7 ACK 송신까지 차단 (eGateway 는 메시지마다 ACK 요구), (c) sibling 베드 메시지는 broadcast 후 ACK 없이 return. 수정 - readonly 는 송신만 차단하고 수신은 정상화 (broadcast-only keyword sub 만 소켓 미보유), write_protocol 경로 신설로 ACK 는 readonly 우회, catch-all 이 sibling 메시지도 대신 ACK (R01/R40 타입 구분), broadcast 수신 sub 의 rx_time 갱신으로 상태표시 정상화, PortRouter takeover 는 기존 소켓이 stale (10초+ 무수신) 일 때만 허용해 게이트웨이 health-probe 접속이 live 데이터 소켓을 뺏지 못하게 함. 시뮬레이터 E2E 검증: ORU 75건 -> ACK 75건 (100%), probe 5회 전부 무해 거절, SU-2/SU-4 tier1 라우팅 정상. (2) Getinge/MAQUET Flow-i (FCI 프로토콜) tidal volume 이 실제의 절반으로 기록되던 문제. 원인: FCI 프로토콜 v0005 부터 TV 채널 (101/102) gain 이 0.1 -> 0.2 로 변경됐는데 (Flow-i communication interface v0007 문서), VR 은 open 시 SPVE0005 로 v5 를 요청하면서 파싱은 구버전 gain 0.1 을 적용. 병원 vital 파일 실측 (Flow-i 직결 TV 201.4/188.2 vs 필립스 경유 403.0/376.6, MV 는 정상) 으로 검증 - TV_INSP/TV_EXP gain 0.2 적용. MV 는 v3+ 스케일 (0.01) 이 이미 반영돼 있어 변경 없음. (3) Flow-i 설정 파라미터 미지원 (m_ch_settings 빈 맵) - v5+ 에 존재하는 수치 설정 채널 11종 등록: SET_RR(411), SET_INSP_TM(413), SET_IE(418), SET_MV(421), SET_PC(423), SET_PEEP(424), SET_FRESH_FLOW(428), SET_FRESH_O2(430), SET_AGENT_PERC(434), SET_PS(436), SET_SIMV_RR(437). gain 은 문서 표기 그대로. 주의: Set Tidal Volume (채널 422) 은 FCI v5 에서 프로토콜 자체에서 삭제되어 직접 수신 불가 - 볼륨 모드에서 SET_MV/SET_RR 로 파생 가능. 설정 채널은 문서 기반 구현으로 실기 검증 대기 상태. | |
| 1.18.48 | 2026-07-24 | 1.18.48 Fixed: VR 이 먼저 실행된 상태에서 Philips Intellivue 모니터를 나중에 켜면 웨이브가 영원히 수집되지 않던 문제 (VR 재시작 전까지 모니터로 아무 요청도 안 나감). 원인: Intellivue::open() 은 assoc_req 를 딱 한 번 보내는데, 시리얼(MIB)/UDP 는 모니터가 꺼져 있어도 포트가 열리므로 이 요청이 응답 없이 사라진다. Philips 는 자기가 먼저 말을 걸지 않는 프로토콜이고, 첫 수신이 있어야 디바이스 상태가 OPENING→ACTIVE 로 승격되는데 수신이 없으니 OPENING 에 고정 - DEVICE::restart_thread_func 워치독은 is_closed(재오픈)/is_active(read_timeout 30s) 두 분기만 있어 OPENING 은 어느 쪽에도 안 걸림 → assoc 재전송도 재시작도 영원히 없음. 수정: Intellivue 에 assoc 재시도 스레드 추가 - 세션 확립(MDS_CREAT 수신, m_bSessionUp atomic 플래그) 전까지 5초 간격으로 assoc_req 재전송, 최근 5초 내 수신이 있으면(assoc 진행 중) 재전송 보류. 모니터를 나중에 켜도 최대 5초 내 연결. 세션 확립 후에는 재시도 루프 종료 - 기존 동작(AB_SPDU 시 재연결, read_timeout 재시작)과 충돌 없음. 모니터가 켜져 있던 기존 시나리오는 첫 assoc_req 에 바로 응답하므로 동작 변화 없음 - 0-impact. | |
| 1.18.47 | 2026-07-24 | 1.18.47 Fixed: 파일 업로드(및 Http 클래스를 쓰는 모든 경로 - upload_vital, getvr 업데이트체크, ADT 조회, 필터서버)가 SERVER_IP/UPLOAD_SERVER_IP 에 "https://host:port" 풀 URL 형식을 넣으면 "Couldn't resolve host name" 으로 전부 실패하던 문제. 원인은 src/Https.cpp 의 Http 생성자 - 1.18.46 의 wss.cpp 수정과 동일한 버그의 나머지 반쪽. 첫 콜론을 포트 구분자로 오인해 "https://eicuvital.ywmc.or.kr:4432" 입력 시 m_strHost 가 문자 그대로 "https" 가 되어 DNS 조회 실패 (웹모니터링 wss 는 1.18.46 에서 이미 수정되어 정상, 파일 업로드만 실패하는 비대칭 발생 - 현장에서는 업로드 루프의 실패시 break 정책과 겹쳐 "베드명에 # 가 있으면 전송이 안 된다" 로 오인 관찰됨. 실제로는 # 파일이 디렉토리 순서상 항상 첫 시도라 거기서 host 오류로 중단 → 뒤 파일 전체가 막힌 것. 베드명 특수문자는 업로드 URL 과 무관). 수정: Http 생성자에 wss 와 동일한 선행 스킴(https/http) 파싱 추가 - 스킴이 TLS 여부를 결정 (포트 무관, 4432 같은 커스텀 포트에서 https 정상), "https://host" 포트 생략 시 443, 경로 붙은 입력도 host 만 추출. 기존 "host" / "host:443" 형식은 종전과 완전 동일 동작 - 0-impact. Windows(WinINet)/Linux(curl) 공통 생성자라 모든 플랫폼에 적용. | |
| 1.18.46 | 2026-07-23 | 1.18.46 Fixed: WSSClient 가 "https/wss 스킴 + 비표준 포트" 조합을 제대로 처리하지 못하던 문제 (커스텀 포트의 wss 서버에 연결 불가). 원인은 src/wss.cpp 의 WSSClient 생성자 두 곳 - (1) SERVER_IP 파싱이 첫 콜론을 포트 구분자로 써서 "https://host:4432" 를 넣으면 "https" 의 콜론을 먼저 잡아 m_host 가 "https" 가 되고 포트 파싱이 깨져 getaddrinfo 실패, (2) m_secure = (m_port == 443) 로 TLS 여부를 오직 포트 443 여부로만 판단해 4432 같은 커스텀 포트는 무조건 평문 ws 로 붙어 wss 전용 서버에서 실패. 수정: 생성자가 선행 URL 스킴(https/wss/http/ws)을 먼저 파싱해 스킴이 있으면 스킴이 TLS 를 결정(포트 무관)하고, 스킴이 없으면 기존 포트 443 휴리스틱을 유지(하위호환). 이제 SERVER_IP=https://eicuvital.ywmc.or.kr:4432 (또는 wss://...) 형식이 비표준 포트에서도 wss 로 정상 연결되며, SNI/인증서 검증은 도메인(m_host)을 쓰므로 와일드카드 인증서도 정상 매칭. 기존 host / host:443 설정은 종전대로 secure 유지 - 0-impact. | |
| 1.18.45 | 2026-06-16 | 1.18.45 Fixed: Windows VR 에서 사용자가 외부에서 vr.conf 를 멀티베드 설정으로 교체 후 VR 을 재실행하면, 첫 실행에서는 멀티탭이 정상 생성되지만 다음 실행에서 모든 탭이 사라지던 문제. 원인은 VRApp.cpp:1110 의 VRCODE 미설정 분기 — 새 vr.conf 가 VRCODE 라인을 포함하지 않으면 load_settings() 가 신규 VRCODE 를 issue_code() 로 발급하고 즉시 save_settings() 를 호출하는데, 이 시점은 load_settings() 가 파싱한 베드들이 로컬 newbeds 벡터에만 있고 아직 m_pdisps 로 옮겨지기 전 (set_beds(newbeds) 는 initialize() 가 load_settings 반환 후 라인 1328 에서 호출). 결과: save_settings() 가 빈 m_pdisps 를 iterate 하며 vr.conf 를 글로벌 설정만으로 덮어써 모든 [BED/...] 섹션이 디스크에서 사라짐. 첫 실행은 메모리의 newbeds 로 UI 멀티탭이 그려져 정상으로 보이지만 (사용자가 "멀티탭은 정상적으로 생성되지만" 으로 표현), VR 종료/재실행 시 디스크 conf 에는 BED 섹션이 없으므로 다음 load_settings 가 빈 베드 리스트를 반환 → 탭 전부 사라짐. 수정: VRApp.h 에 m_vrcode_newly_issued bool 플래그 추가, load_settings 의 즉시 save 를 플래그 set 만 으로 교체, initialize() 의 set_beds(newbeds) 직후 (m_pdisps 가 채워진 시점) 플래그가 켜져 있으면 한 번만 save_settings() 호출 → BED 섹션이 정상 직렬화됨. Linux/Pi 분기 (m_serial_number 가 채워져 있는 경우) 는 원래도 save 안 했으므로 영향 없음, VRCODE 가 이미 conf 에 있는 모든 기존 deployment 도 분기 미진입으로 0-impact. m_conf_from_stdin / m_bDebug 모드는 save_settings 내부에서 이미 early return 이라 그대로 안전. MFC + Qt 빌드 모두 통과 (이번 변경 관련 0 warning). | |
| 1.18.44 | 2026-06-12 | 1.18.44: vr.conf FHIR=1 모드 추가 (자체 FHIR 수신서버 대상 실시간 푸시) + VS 2026 / 새 OpenSSL 인스톨러 빌드 환경 호환 shim. (1) sendweb_thread_func 의 기존 HL7/JSON 양분 분기 (VRApp.cpp:2466) 옆에 bFHIR 분기 추가. 1초 tick 마다 한 vrcode 의 모든 방 Observation 을 하나의 FHIR R4 collection Bundle 로 묶어 기존 WSS 채널로 push (gzip + WSSClient 인프라 그대로 재사용). MLLP 같은 신규 transport 안 도입 — FHIR R5 Subscription 의 websocket channel type 과 동형이라 표준 부합. (2) DISPLAY::get_fhir + TRACK::get_fhir 신규. v2 (vitaldb-fhir-v2) 와 동일한 reference-only 패턴 (Patient.id = m_patient_id, Encounter.id = vrcode-bedname-startsec, Observation.subject/encounter = relative reference) + hybrid keyframe: 첫 tick / m_patient_id 변경 / get_start_time() 변경 / 5분 경과 중 하나라도 발생 시 다음 tick Bundle 에 Patient + Encounter resource 를 inline. 그 외 tick 은 Observation 만. 수신서버가 도중에 새로 connect 해도 최대 5분 안에 자동 동기화 (keyframe heartbeat). DISPLAY 에 m_fhir_pt_sent_at/m_fhir_pt_last_id/m_fhir_enc_last_start 3개 멤버 추가 (m_setting_sent 패턴 그대로 per-display). (3) Observation 인코딩: NUM 은 effectiveDateTime + valueQuantity 각 데이터 포인트당 한 entry, WAV 은 effectivePeriod + valueSampledData (period=1000/srate ms, factor=1, origin=0, data=공백구분 floats) 한 entry. CO2/AWP wav 는 기존 get_json 처럼 25Hz 다운샘플 유지. NUM 은 m_montype 기반 LOINC 매핑 15개 등록 (ECG_HR/PLETH_HR=8867-4, PLETH_SPO2=59408-5, NIBP/IABP SBP/DBP/MBP=8480-6/8462-4/8478-0, BT=8310-5, RR=9279-1, etCO2=19891-1, CVP=8556-0, FETAL_HR=11627-3) + VR 자체 코드 시스템 (https://vitaldb.net/fhir/CodeSystem/track, code = dname/tname) 항상 동반. m_patient_id 미설정 (PATIENT_ID_UNSET) 시 anon-vrcode-bedname identifier 로 fallback. (4) Bundle wrapper: VRApp 가 방별 entries 를 콤마로 합쳐 {resourceType:Bundle, type:collection, timestamp:iso8601Z, entry:[...]} 로 wrap, 기존 gzip → WSS pipeline 동일. ISO 8601 UTC 헬퍼는 std::gmtime_s/gmtime_r 인라인. FHIR id 안전화 (A-Za-z0-9-.) 64자 제한 lambda 각 함수에 inline. (5) 빌드 환경 호환성: VRQt/CMakeLists.txt 에 _SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS 추가 — VS 2026 (MSVC 14.51+) 의 STL 이 <experimental/coroutine> deprecation 을 warning → hard static_assert 로 바꾼 것이 cppwinrt 의 winrt::* (Serial.cpp 의 Bluetooth LE) 를 끌어오는 모든 TU 에서 컴파일 실패 시켜서, build_mfc.py 가 이미 CL env 로 주입하던 것과 동치 shim 을 Qt 쪽에도. (6) VRQt/CMakeLists.txt 의 OpenSSL link 경로 시프트. 새 OpenSSL 인스톨러가 libssl_static.lib / libcrypto_static.lib 를 lib/VC/x64/{MT,MTd}/ 에 두고 v1.x 의 libssl64MT.lib / libcrypto64MT.lib 네이밍과 lib/VC/static 경로를 깬다. build_mfc.py 의 %LOCALAPPDATA%/openssl-compat 패턴 (옛 이름으로 복사한 사본 폴더) 을 그대로 따라 CMakeLists 도 그 폴더가 있으면 우선, 없으면 옛 lib/VC/static 으로 fallback. 부트스트랩 스크립트는 build_mfc.py 의 docstring 참조 (한 줄). MFC + Qt + RPi + Ubuntu CLI + AppImage 빌드 모두 통과. 동작 영향: vr.conf 에 FHIR=1 추가 안 한 기존 deployment 는 sendweb 가 종전 JSON 모드 그대로 (HL7=1 만 분기보다 우선) — 0-impact migration. | |
| 1.18.43 | 2026-05-21 | 1.18.43: 멀티베드 console (PACU 10베드 Pi) syslog noise 80,500/hr → ~1,400/hr (-98.3%), cpu_usage 측정 1.16.12 (9f26e36) 이후 8년치 영구 -1 버그 수정, 멀티베드 환경 베드 식별용 [bedname / devicename] 로그 prefix 도입. 동기는 PACU production Pi (1.18.40) 4일치 syslog 분석 — 1.24M 라인 중 vr 라인 836K, idle 8시간 평균 시간당 80,500 라인 (베드 10 × 1초마다 "Waiting for next patient" × syslog 2x 중복). (1) VRApp.cpp:2696-2710 timer_thread_func 의 cpu usage 계산이 `if (totalcpu && m_cpu_total_last && totalcpu > m_cpu_total_last)` 가드 안에서만 m_cpu_total_last 갱신해 첫 호출 시 m_cpu_total_last=0 → else 분기 → -1 → m_cpu_total_last 갱신 안 됨 → 영원히 false → 영원히 -1 데드락. PACU 1,048 샘플 100% -1 실측 확인. m_cpu_total_last/m_cpu_used_last 갱신을 if 밖으로 빼서 첫 호출만 baseline 으로 -1, 두 번째 (60s 후) 부터 정상 측정. /proc/stat 의 iowait/irq/softirq 까지 7개 필드 fscanf 로 정확도 ±5% 개선. 부수: 같은 함수의 cpu_temp 가 `temp / 1000` int 나눗셈으로 소수점 절단되어 PACU 측정값이 정수 50/51/...,/58 9종만 출력 (millidegree 정밀도 손실) → `temp / 1000.0f` 로 fix. ram 계산은 `(totalram + totalswap) * mem_unit` 결과에 다시 mem_unit 곱하던 차원 혼용 (Linux 가 mem_unit=1 이라 우연히 정상 동작했지만 dimensionally wrong) → `(units1 + units2) * mem_unit` 로 정정 + underflow 가드. (2) DISPLAY::log(string/printf) + DEVICE::log(string/printf) 멤버 추가. DISPLAY::log 는 `[bedname] msg`, DEVICE::log 는 m_pdisp 위임 없이 직접 `[bedname / devicename] msg` 한 줄 prefix (nested `[bed] [dev]` 보다 가독성 우선). m_pdisp 또는 bedname null 케이스 safe fallback. (3) show_msg 의 console path TRACE 를 log() 로 교체 → "Waiting for next patient" 자동 [bedname] prefix. ADT patient id unset 도 log() 로. (4) 이미 수동으로 get_bedname() 을 인자로 넘기던 8군데 (DISPLAY ptcon_thread_func / add_trk / add_dev / del_dev / cut-recording, Device add_rec 콜백, FILT stop, VRApp add_tab/del_tab) 를 log() 로 전환 — 이중 prefix 방지 + 일관성. Serial::reading_thread / tcp_thread / open_socket waiting / timeout / interrupted / got-client 6군데 DEVICE::log() 전환 → `[bedname / Intellivue]` 등 자동 prefix, 멀티탭 환경에서 어느 베드 어느 device 출처인지 즉시 식별. (5) wss 재시도 폭주 throttle. WSSClient 에 m_verbose (debug step 로그 게이트) + m_dt_last_err_log (60s window) 추가. connect() 내부 step-by-step TRACE 4개 ("connecting" / "connect error" / "connected") 는 m_verbose 가드, 최종 "wss connect error" 는 60s window throttle (첫 줄 즉시, 그 다음 60s 1회). connect 성공 path 에서 throttle reset 으로 다음 disconnect → 첫 에러 즉시 로깅. sendweb_thread_func 의 "connecting websocket server" 호출자 라인도 같은 60s window, m_bDebug 면 verbose 활성화. PACU idle 8h 측정 7,024/hr (4종 × 1,756) → 약 60/hr (-99%). (6) "Waiting for next patient" / "ADT patient id unset" 의 60s throttle 자체는 feature/wav-mem-retention 브랜치 ptcon_thread_func 재구성 (b75928f 시리즈) 에서 이미 들어가 있어 이 릴리즈에 자동 포함. 종합 영향: idle 시간당 syslog 라인 80,500 → ~1,400 (-98.3%), active 시간당 14,900 → ~700 (-95%). 진단 측면: cpu_usage 가 부활하면서 웹모니터링 json 의 cpu_usage 필드도 한 번도 안 채워지던 게 정상 전송 (VRApp.cpp:2499 의 `if (m_cpu_usage > 0)` 가드가 영원히 false 였음). 별건: syslog 라인 2× 중복은 application 측이 아니라 rsyslog imuxsock + imjournal + journald.ForwardToSyslog=yes 의 Debian Bookworm 전형 misconfig — admin 측 fix (rsyslog.conf 에서 imjournal 주석 처리 + restart) 로 추가 50% 감소 가능 (별도 문서). MFC + Qt + Pi (RPI64) + Ubuntu CLI 빌드 모두 통과. | |
| 1.18.41 | 2026-05-13 | 1.18.41 Hotfix: TCP-server protocol-initiator device (Intellivue/S5/Datex/Hamilton/MPS/Agilia/IAP) 가 open() 직후 보내는 첫 패킷이 wire 에 안 나가던 회귀. 1.18.34 의 PortRouter refactor 가 Serial::open_socket() 의 8초 handoff blocking 을 제거하고 비동기 모델로 전환했는데, protocol-initiator 의 open() 코드가 m_sock=INVALID 상태에서 write 를 시도해 silent fail (Linux 는 m_fd=0 이라 false 반환). Intellivue 케이스 production journald 의 Pi → ESP TX 700K~3M bytes 는 monitor 측 abort 로 늦게 트리거된 AB_SPDU_SI recovery assoc_req 후 request_thread 의 정상 polling 트래픽이지만 일부 베드 (abort 없는 monitor) 는 recovery 도 안 되어 영구 silent (RX 300 bytes). 수정 방향: open() 의 옛 contract ("return 시점에 통신 가능") 복구. (1) Serial::open_socket() TCP-server 분기가 PortRouter::subscribe 후 m_sock_cv 로 첫 client handoff 까지 block (timeout = device read_timeout, 기본 30s) — 옛 모델 그대로. lock_and_open 이 false 면 다음 tick 에서 retry. close 신호로 abort 가능. (2) Serial::close() 는 PortRouter::unsubscribe 안 함 — subscribe 는 device 생애 동안 sticky 라 read_timeout 으로 인한 close→re-open 사이클 동안 listen socket 이 유지되어 1.18.29/1.18.34 의 anti-churn 이익 보존. unsubscribe 는 ~Serial() 에서만. (3) Per-device lifecycle thread (DEVICE::m_thread_restart): 각 device 가 자기 open/close/timeout 재시작을 별도 thread 에서 관리. 옛 모델의 DISPLAY::bed_manager 가 베드의 모든 device 를 sequential iterate 하던 구조에선 한 device 의 blocking open() 이 같은 베드의 다른 device 의 lifecycle 을 차단했는데, per-device thread 로 분리하여 멀티 device 베드도 병렬 진행. add_dev 에서 start_lifecycle, del_dev / ~DISPLAY 에서 stop_lifecycle. (4) Serial::tcp_thread_func 단순화 — outer m_sock_cv wait 패턴 (1.18.34/1.18.39 가 추가했던 것) 제거. open_socket 이 m_sock valid 보장하므로 reader thread 시작 시점에 simple inner recv loop 만. accept_handoff 의 replace (zombie cleanup) 케이스는 recv error 후 m_sock 변경 확인 + retry 로 처리. (5) reading_thread_func 의 `else if (m_sock != INVALID_SOCKET) tcp_thread_func();` 가드 복원 (1.18.39 가 제거). (6) Serial::write 의 silent-fail 케이스에 WARN trace 추가 — 미래 동종 회귀 즉시 노출. (7) PortRouter::subscribe 가 같은 (port, type) 에서 같은 IP filter 또는 둘 이상 catch-all 발견 시 reject (config 실수 원천 차단, 옛 WARN-only 동작 강화). (8) DISPLAY::bed_manager_thread_func 의 restart 블록 제거 — ptcon + adt 만 담당. "Waiting for next patient" TRACE 가 Linux/console 에서 1초 → 60초마다 throttle (별개 journald 부풀림 fix). 영향 범위: Intellivue / S5 / Datex / Hamilton / MPS / Agilia / IAP TCP-server 모드 베드 전부. 1.18.34 이후 우회 방법은 race-win 케이스 (VRN 이 PortRouter::subscribe 직후 connect) 외엔 없었음. MFC + Qt + Pi (RPI64) + Ubuntu Qt + AppImage 빌드 모두 통과. | |
| 1.18.40 | 2026-05-12 | Hotfix: Drager Medibus 프로토콜 사용 베드에서 1.15.11 이후 vr.service 가 SIGSEGV 로 무한 재시작하던 문제. 1.15.10 (a8c7b2a) 의 "Sampling rate estimation" 리팩터링이 m_ptrk_awp/awf/co2 트랙을 add_trks() 에서의 eager 생성 (default srate=62.5Hz) 에서 realtime data 100 샘플 모인 후 lazy 생성으로 바꿨는데, Medibus 핸드셰이크 순서 (0x51 ICC → 0x52 DeviceID → 0x53 RealtimeConfig 응답 → 0x54 → realtime data 시작) 상 0x53 응답 핸들러 (Drager.cpp:295-307) 가 트랙들이 아직 nullptr 인 시점에 `ptrk->m_srate = 1000000.0/interval` 로 NULL 역참조 → 즉시 SEGV. 그 베드의 vr.service 가 systemd Restart=on-failure 로 재시작 → 다시 connect → 동일 0x53 패킷 받고 동일 SEGV → 무한 루프 (journald 로그상 restart counter 61 까지 누적, code=killed status=11/SEGV). 1.15.9 까진 트랙이 add_trks() 에서 생성되어 0x53 도착 시 항상 non-NULL 이라 안전했음. 수정: (a) add_trks() 에서 m_ptrk_awp/awf/co2 eager 생성 복원 (srate=62.5Hz default) — 0x53 는 ptrk->m_srate 만 갱신 (트랙 재생성 X). (b) on_received() 의 lazy creation + packet-timing srate 추정 블록 제거 — 0x53 가 권위 있는 srate 소스이고 packet 타이밍 기반 추정은 network jitter 에 약해 불안정. (c) 0x53 핸들러에 `if (interval > 0)` guard 추가 — 페이로드 깨졌을 때 div-by-zero / inf srate 방지. 영향 범위: Drager Primus / Apollo / Perseus / Fabius 등 Medibus 사용 마취기 전부. 우회 방법은 1.15.9 다운그레이드 외엔 없었음 (그 베드의 모든 데이터 유실 + 환자 모니터링 불가). MFC + Qt + Pi (RPI64) + Ubuntu Qt + AppImage 빌드 모두 통과. | |
| 1.18.39 | 2026-05-08 | Fixed: PortRouter (TCP server) 모드에서 reader thread 가 startup 시점에 죽어 client 데이터를 영구히 못 받던 문제. 1.18.34 (9bd252f) 의 PortRouter refactor 가 m_sock 세팅을 dispatch 시점으로 옮겼는데 reading_thread_func (Serial.cpp:354) 의 가드 `else if (m_sock != INVALID_SOCKET) tcp_thread_func();` 는 옛 모델 (open 시점 이미 connect 완료) 가정 그대로라 — 시작 직후 m_sock=INVALID 면 tcp_thread_func 진입 안 하고 reader thread 즉시 종료. 이후 VRN 이 connect → PortRouter::dispatch → accept_handoff 가 m_sock 채우고 m_sock_cv.notify_all() → 그러나 wait 중인 reader 가 없음 (이미 죽음) → kernel recv buffer 에만 데이터 적재. 증상은 ss -tn state established 의 Recv-Q 가 0 이 아닌 값 (수백 bytes) 으로 누적, 베드 화면에는 데이터 안 들어옴. 그 베드의 VRN 이 Serial::open() 의 PortRouter::subscribe 직후~reader thread spawn 사이의 짧은 윈도우 안에 connect 한 경우만 우연히 정상 동작 (race-win). 수정: 가드 제거 — TCP 분기에 항상 진입. tcp_thread_func 자체에 이미 m_sock_cv outer-wait 루프가 있어 m_sock 이 INVALID 인 동안 dispatch 가 깨워줄 때까지 안전하게 대기. Pi 핫스팟에 VRN 여러 대 (e.g. 10.42.0.21~25) 를 같은 (4343, Philips:Intellivue) 키로 묶고 IP filter 로 라우팅하는 시나리오에서 가장 자주 노출. Added: 진단 로그 보강 — PortRouter::accept_loop 의 accept 이벤트, dispatch 의 라우팅 결정 (ip_specific/catch_all subs 수, 어느 dev 가 받았는지), subscribe/unsubscribe 의 entry 생성/append/destroy, reading_thread / tcp_thread 의 시작·wait·wake 라이프사이클 — 이전엔 모두 m_bDebug 게이트 안에 있어서 production journald 에서 안 보였는데, 이제 항상 출력 (이번 같은 dispatch routing 문제 진단을 production 에서 바로 가능). MFC + Qt + Pi (RPI64) + Ubuntu Qt + AppImage 빌드 모두 통과. | |
| 1.18.38 | 2026-05-08 | Fixed: 웹모니터링 우클릭 → 디바이스 세팅 (websocket edit_bed 명령) 으로 베드의 device/filter 를 변경할 때 베드 이름이 매번 "_2" suffix 로 자동 리네임되어 vr.conf (Pi: /data/vr.conf) 에 잘못된 이름으로 저장되던 문제. 원인은 1.18.22 (cda8aa5) 에서 add_bed 에 추가된 bedname dedup ("최후 방어선" 으로 forward_frame auto-create / 수동 편집된 conf 의 중복 이름 처리용). edit_bed 핸들러는 옛 pdel 이 m_pdisps 에 그대로 있는 상태에서 같은 bedname 으로 new DISPLAY 를 add_bed 하고 그 다음 del_bed(pdel) 하는 순서였는데, add_bed 가 dup 감지해 새 pdisp 를 bedname_2 로 자동 리네임 → save_settings 가 [BED/bedname_2] 로 직렬화 → 사용자 입장에선 "설정 적용 안됨" 으로 보였다. 추가로 옛/새 DISPLAY 가 잠깐 m_pdisps 에 공존하면서 같은 device 포트를 동시에 들고 있으려 해 로그가 dup 으로 찍히는 부수 증상도 있었음. 수정: del_bed(pdel) 를 add_bed(pdisp) 보다 앞으로 이동 — 옛 베드의 device 포트가 깨끗이 release 된 뒤 새 pdisp 가 같은 포트를 reopen, dedup 발동 안 함. 다른 베드의 recording 은 영향 없음 (인메모리 hot swap 설계 유지). 부수: edit_bed 핸들러의 데드 코드 (`break` 뒤의 unreachable olddevs populate 루프 + `if (olddevs.find(name) == olddevs.end())` 항상-true 래퍼) 정리. edit_conf 와 save_settings 는 손대지 않음. MFC + Qt + Pi (RPI64) 빌드 모두 통과. | |
| 1.18.37 | 2026-05-08 | Refactored: 필터 다이얼로그의 python 런타임 — pyvital → openvital 0.3.0 로 교체 + 동봉 python.zip 슬림화. 핵심 동기 셋: (1) pyvital 0.6.0 이 tensorflow+torch+keras 를 hard-dep 으로 끌어들여 PyPI upgrade 가 사실상 깨짐, (2) 기존 zip 121 MB 안에 botocore/aiobotocore/s3fs/pyarrow/pandas/aiohttp 등 vitaldb 클라우드 업로드용 deps 가 ~250 MB unpacked 차지 — 필터 서버는 한 번도 쓴 적 없는 dead weight, (3) sanic 25.x 의 multi-process worker 가 openvital __main__.py 를 fork 후 재import 하면서 worker 가 module-top app instance 를 못 찾아 "Sanic app name not found" 로 죽음 — 단순 endpoint 2개 로컬호스트 서버에 async stack 자체가 과함. 해결: openvital 0.3.0 (commit b6f445b) 가 sanic 을 stdlib http.server (BaseHTTPRequestHandler + HTTPServer) 로 50 LOC 재작성 — wire protocol (GET / 필터목록 JSON, POST /<modname> gzip 본문) 동일해 VR 클라이언트 변경 0줄. 단일 스레드로 기존 sanic event loop semantics (cfgs/default_cfgs lock-free 변경) 보존. build_python_zip.py (NEW) 가 Python 3.11.9 embed + numpy + openecg + openvital (local checkout 우선, fallback PyPI) 만으로 27 MB zip 빌드 — 기존 121 MB 대비 78% 감소. ML 필터 (sv_dlapco/abp_hpi/ecg_classifier/ecg_beat_noise_detector) 는 [Hybrid 모델] 사용자가 Add filter 다이얼로그의 "Install ML filters" 버튼 누를 때 silent pip install openvital[all] (cmd 창 미노출 — VRApp::install_pip_package + run_pip_silent 이 자체 progress 다이얼로그에 stdout heartbeat 흘림). 버튼 라벨은 "Upgrade pyvital" → "Install ML filters" 로 의도 명시. openvital __main__.py 가 missing extras 를 graceful skip 하므로 base 환경에서도 11개 필터 (ECG QRS detector, HRV, MTWA, annotator, eeg_fft, nirs_cox, pkpd_3comp, pleth_dpop/ptt/pvi, resp_compliance) 즉시 사용 가능. Migration: 기존 사용자의 user_dir/python 폴더는 pyvital + 옛 deps 가 그대로 남아있고 vitaldb.net/python.zip 만 새로 올리면 (a) 기존 폴더 삭제 후 install_python 으로 새 zip 받으면 깔끔, (b) 폴더 유지 시 server 가 openvital 못 찾아 필터 다이얼로그 에러 → release note 에 명시 필요. MFC + Qt 양쪽 코드: VRGui.rc 의 IDC_UPGRADE 버튼 라벨 + DlgAddFilter.cpp 의 dist-info prefix 매칭 (pyvital- 8자 → openvital- 10자) + QtDlgAddFilter.cpp 도 동일 변경 + onUpgrade 가 cmd /k QProcess::startDetached 대신 theApp.install_pip_package("openvital[all]") 호출 (Linux Qt 빌드는 #ifdef _WIN32 가드). VRApp.cpp:318 의 python -m pyvital → -m openvital. install_pip_package 는 anonymous namespace 의 is_safe_pkg_spec 화이트리스트 검증 + run_pip_silent (CreateProcessA + CREATE_NO_WINDOW + 자식 stdout pipe) + CDlgProgress / QtDlgProgress 자체 progress UI. 빌드 검증: python -m openvital 이 GET / 로 11 필터 반환, sanic import 부재 확인. zip 27.5 MB. | |
| 1.18.36 | 2026-05-07 | Fixed: 3-파티션 레이아웃 (boot/rootfs/data) 에서 websocket edit_conf / update 명령이 read-only fs 로 실패하던 문제. vr.conf 는 /data 폴더가 있으면 /data/vr.conf 로 자동 정착 — get_conf_path() 가 write target 으로 결정, load_settings() 는 primary 가 없으면 legacy /boot/firmware/vr.conf 또는 /boot/vr.conf 에서 fallback read 후 다음 save 부터 primary 로 자동 이전 (마이그레이션 step 없음, legacy 파일은 read-only fs 라 무해). binary update (upgrade_now) 는 RAII BootRwGuard 가 dirname(module_path) 를 잠시 rw remount, scope 종료 시 sync() + MS_REMOUNT|MS_RDONLY 로 자동 ro 복귀 — vfat 노출 윈도우 최소화 + exception/return 어디서든 복구 보장. statvfs ST_RDONLY 검사로 이미 rw 인 fs (x86 dev) 에서는 no-op, non-Linux (macOS) 는 stub 으로 통과. save_settings() 의 silent EROFS 실패가 TRACE 로 가시화 (이전엔 무시되어 conf 수정 안 먹는 원인 추적 불가). 부수: PortRouter.cpp 가 commit 9bd252f 이후 CMakeLists 의 RPI sources 목록에서 누락되어 Pi 빌드 link 에러 — 추가. | |
| 1.18.35 | 2026-05-01 | Added: alarm parsing for Mindray HL7 (eGateway ORU^R40) and Nihon Kohden (NealTime HL7GW + ORF, BSM serial D-port, EGA UDP) - emits ALARM_STATUS / ALARM_MESSAGE / ALARM_PRIORITY tracks (same 3-track schema already used by Philips IntelliVue, GE Solar/Dash, GE/Datex S5); type info preserved via [T]/[A]/[ARR] message prefixes; Mindray facet 1/5/6/7 walker with 92-entry MDC+MNDRY alarm code lookup; NK HL7GW parses OBR-4=EVENT with arrhythmia/technical disambiguation by OBX-5; NK BSM serial decodes per-parameter * flags (edge-triggered to avoid 1Hz flooding) plus 32-bit Table 9 arrhythmia bitmap, D19 alarm-suspend nibble, and the VPC CODE byte; NK EGA UDP extracts Priority + Tech Alarm + Arrhythmia from JSON Vital Sign packets with edge-trigger cache for start/end inference; ACK^R40 reply added for Mindray alert messages; simulators gained periodic alarm cycling for end-to-end smoke testing | |
| 1.18.34 | 2026-04-27 | Added: Linux Desktop Qt GUI build (VRQt) - first GUI build for Linux distributed as a self-contained AppImage (~32MB, Qt6 runtime + libGL/libEGL/libGLX/libOpenGL/libGLESv2/libGLdispatch bundled inside) so users on minimal Ubuntu/Debian/Fedora can run it without installing Qt or GL system packages; tested on AWS clean Ubuntu 22.04 cloud image; build via build_ubuntu_appimage.py which is now invoked by deploy.py; Fixed: Linux Qt right-side device/filter panels rendered with white background below cards because the inner listWidget did not autoFillBackground from parent palette on Linux Qt (Windows/macOS implicitly inherit, X11/Wayland do not); explicit stylesheet on listWidget; Added: Linux installation section in user manuals (EN/KO/ZH/ES) covering AppImage chmod+run flow and dialout group note for serial/USB device access | |
| 1.18.33 | 2026-04-25 | Fixed: Demo device never started since 1.18.11 (bed_manager skipped empty port_name as passive but Demo has no port and still needs lock_and_open); Fixed: Qt fit-to-window button rendered at top-left (0,0) instead of right-aligned in bottom nav bar; Fixed: Qt crash when closing tab / deleting device / deleting filter - paint dereferenced freed pointer before panel timer rebuilt items (added MFC-style stale-pointer guard to QtDeviceItem and QtFilterItem); Added: macOS DMG main window now shown on launch (restoreWindowPos had no Mac/Linux branch); Added: macOS Homebrew dylibs (openssl) bundled in Contents/Frameworks; Added: macOS .app bundle now notarized + stapled (drag-to-Applications no longer needs xattr -cr on first launch); Changed: Qt MSI ~1MB smaller (dropped unused iconengines and imageformats plugins) | |
| 1.18.32 | 2026-04-23 | Fixed: same-port multi-client TCP - primary now runs a continuous accept loop and hands off each connection to the matching sub by IP filter (previously second connection got rejected/listened closed); Added: @IP and #keyword port filters are now mutually exclusive per bed (IP wins, keyword dropped with WARN); Added: WARN log when two beds on the same port share an identical IP filter; Added: catch-all primary keeps its single connection and rejects extras (preserves broadcast mode) | |
| 1.18.31 | 2026-04-22 | Added: drug slot shows DOSE_AMOUNT (mg etc) in preference to VOL (mL) when available; Added: DOSE_RATE / DOSE_AMOUNT montypes bound to BBraun tracks for monitor view; Added: HL7 MDC_DOSE_DRUG_DELIV_TOTAL unit picked up dynamically from UCUM | |
| 1.18.30 | 2026-04-21 | Changed: monitor view pump layout - 8 slots with CE or RATE (CE-less pumps show infused volume as small annotation); Reverted: BBraun minimal=1 (1.18.29) - was dropping data from file, monitor view redesign handles display clutter instead | |
| 1.18.29 | 2026-04-21 | Fixed: BBraun multi-rack auto-tab data drop, primary TCP churn, save_settings lock race, TCP reader spin on peer close; Added: BBraun minimal=1 option (display only drug name / rate / volume) | |
| 1.18.28 | 2026-04-20 | Fixed: vr.conf on Pi/Linux now looked up next to executable (supports /boot/firmware); deploy.py --store auto-submits MSIX to Microsoft Store | |
| 1.18.27 | 2026-04-18 | Fixed: vital file periodic flush (5000s→5s bug), Fit-to-Window icon redesigned to match navigation button style | |
| 1.18.26 | 2026-04-18 | Fixed: periodic .vital file flush interval was 5000 seconds instead of 5 (unit-bug since comment said seconds but value was milliseconds). Data now actually reaches disk every 5 s during recording instead of only on close. | |
| 1.18.25 | 2026-04-18 | Fixed: BBraun HL7 rec_timeout raised 10s→60s to prevent primary churn during DoseLink R42→R01 phase transition and long idle periods | |
| 1.18.24 | 2026-04-18 | Fixed: GUI silently exits when another recorder running (now falls back to viewer mode), .vital file association written to HKCU (no admin required, auto-updates exe path on every run) | |
| 1.18.23 | 2026-04-18 | Fixed: atof decimal-separator bug on non-English Windows locales (rates <1.0 recorded as 0 in Norwegian/German/French etc.), BBraun HL7 pump limit raised from 8 to 16 | |
| 1.18.22 | 2026-04-18 | Fixed: multi-bed TCP port bind race on restart (Mindray/BBraun HL7 auto-reconnect without manual Add device) | |
| 1.18.21 | 2026-04-18 | Fixed: port name filter (#keyword@ip) lost on save, BBraun HL7 VMD-block parsing with single dt, Fit-to-Window button sizing (MFC) and added to Qt | |
| 1.18.20 | 2026-04-15 | BBraun: comprehensive protocol parsing (dose rate, infusion time, syringe, weight, drug amounts, delivery status) | |
| 1.18.19 | 2026-04-13 | Fixed: BBraun HL7 multi-pump identification (rack serial filtering), multi-bed forwarding, port filter parsing | |
| 1.18.18 | 2026-04-10 | Added: GE alarm limits, S5 alarm/ext3/ext1 parsing | |
| 1.18.17 | 2026-04-04 | Changed: TRACE debug-only guard, show_msg for file errors, deploy sequential builds | |
| 1.18.16 | 2026-04-04 | Added: TCP port filtering (#keyword@ip), frame forwarding for multi-bed. Fixed: --debug conf file mistaken as vital file, device type ambiguity in forwarding | |
| 1.18.15 | 2026-04-04 | Changed: Port to Port Name, keyword filter AND/OR (#kw1 kw2#kw3), IP postfix matching (@10.1), MEKICS SerialFramed | |
| 1.18.14 | 2026-04-03 | Added: TCP port filtering (#keyword@ip), Fixed: devtypes not sent on boot | |
| 1.18.13 | 2026-04-02 | Changed: Covidien company name to Medtronic (BIS, INVOS) | |
| 1.18.12 | 2026-04-02 | Bug Fixed: NihonKohden EGA SpO2 waveform not displayed (channel name mapping) | |
| 1.18.11 | 2026-03-31 | Added: Mindray HL7 multi-bed separation via PV1, auto-tab creation for EGA/Mindray | |
| 1.18.10 | 2026-03-31 | Added: HL7 ZBR segment now includes ver, os, arch, dgmt | |
| 1.18.8 | 2026-03-25 | Added: --demo mode for console operation with demo signals | |
| 1.18.7 | 2026-03-25 | Added: --console mode for headless operation on Windows | |
| 1.18.6 | 2026-03-25 | Added: Mindray eGateway HL7 protocol (IHE PCD-01, HL7 v2.6 over MLLP, waveform + numeric + infusion pump) | |
| 1.18.4 | 2026-03-24 | Added: BBraun SpaceCom HL7 protocol (HL7 v2.6 over MLLP, TCP server mode) | |
| 1.18.3 | 2026-03-24 | Added: HL7 OBX-14 per-track dtstart, ZBR segment with dtcase/dtapp timestamps | |
| 1.18.2 | 2026-03-24 | Added: HL7 ZBR segment with dtcase (recording start) and dtapp (app start) timestamps | |
| 1.18.1 | 2026-03-23 | Fixed: HL7 OBX-3 format (code^device/track@srate), OBX-7 refrange (min^max), NA null sample gaps | |
| 1.17.6 | 2026-03-22 | Bug fixed: Resource ID conflicts WiX installer cleanup | |
| 1.17.5 | 2026-03-21 | Bug fixed: Vital packed file loading MFC device button icon padding | |
| 1.17.4 | 2026-03-19 | Bug fixed: NirsitON EEG 250Hz VRQt device minimize/rename | |
| 1.17.3 | 2026-03-18 | Bug fixed: BBraun protocol NirsitON protocol VRQt | |
| 1.17.2 | 2026-03-14 | Added: NirsitOn NDI protocol support. | |
| 1.17.1 | 2026-03-12 | Added: VRQt | |
| 1.16.13 | 2026-03-07 | Added: New Setup File with VRQt | |
| 1.16.12 | 2026-03-06 | Added: Vector Type Icons | |
| 1.16.10 | 2026-03-02 | Bug fixed: Autodetecting BBraun Stuffing feature. | |
| 1.16.8 | 2026-02-27 | Bug fixed: Masimo IAP protocol. | |
| 1.16.6 | 2026-02-13 | Added: Servo-u ventilator protocol | |
| 1.16.5 | 2026-02-09 | Bug Fixed: NihonKohden HL7GW Pleth waveform gain correction. | |
| 1.16.4 | 2026-01-20 | Bug Fixed: Serial communication. | |
| 1.16.3 | 2026-01-14 | Bug Fixed: Masimo IAP Protocol. | |
| 1.16.2 | 2026-01-03 | Added: BLE Support for Movesense devices on Raspberry Pi | |
| 1.16.1 | 2026-01-01 | Added: BLE Support for Movesense devices on Raspberry Pi and Linux versions. | |
| 1.16.0 | 2025-12-31 | Added: BLE Support for Movesense devices | |
| 1.15.16 | 2025-09-18 | Bug Fixed: Restoring last window position. | |
| 1.15.15 | 2025-09-05 | Added: CPU optimization Bug fixed: Masimo IAP protocol. | |
| 1.15.14 | 2025-09-04 | Added: CPU optimization | |
| 1.15.13 | 2025-09-04 | Bug Fixed: Masimo IAP Protocol. | |
| 1.15.12 | 2025-09-04 | Bug Fixed: Hardware handshake for Link+ connection. | |
| 1.15.11 | 2025-09-01 | Added: Recording alarm messages for Drager anesthesia machines | |
| 1.15.10 | 2025-07-14 | Bug Fixed: Sampling rate estimation in Medibus protocol. | |
| 1.15.9 | 2025-06-03 | Bug Fixed: EEG waveform labels from Philips monitors. | |
| 1.15.8 | 2025-05-26 | Added: EM_UPLOAD option to upload vital files to server immediately | |
| 1.15.7 | 2025-05-26 | Bug Fixed: Websocket connection error | |
| 1.15.6 | 2025-05-19 | Bug Fixed: S5 checksum error | |
| 1.15.5 | 2025-04-17 | Bug Fixed: saving the raw data | |
| 1.15.4 | 2025-04-15 | Added: Recording alarms from Philips monitor | |
| 1.15.3 | 2025-04-14 | Bug fixed: saving the raw data | |
| 1.15.2 | 2025-03-12 | Fixed: bug in opening file | |
| 1.15.1 | 2025-01-30 | Supporting for Fresenius Kabi Conox, Minor Bug fixes | |
| 1.14.13 | 2025-01-18 | Fixed bug in handling non-ascii filepath | |
| 1.14.12 | 2024-11-20 | Fixed: bug in 64bit ARM Architecture | |
| 1.14.11 | 2024-10-24 | Added: feature for updating to a specific version | |
| 1.14.10 | 2024-10-22 | Fixed: bug in json streamming | |
| 1.14.9 | 2024-10-17 | Added: montype for drug rates | |
| 1.14.6 | 2024-09-20 | Added: montype for drug rates | |
| 1.14.5 | 2024-09-13 | Added: auto cutting by any of hr or spo2 | |
| 1.14.4 | 2024-09-12 | Fixed: bug in serial port mapping | |
| 1.14.3 | 2024-09-10 | Added: temperature monitoring Fixed: bug in cpu usage calculation | |
| 1.14.2 | 2024-08-29 | Added: DI-245 protocol | |
| 1.14.1 | 2024-07-26 | Added: GE cardiac output parameters | |
| 1.14.0 | 2024-07-25 | Added: GE Canvas device | |
| 1.13.10 | 2024-07-17 | Fixed: NirsitOn protocol bug | |
| 1.13.9 | 2024-06-27 | Bug fix for file loading | |
| 1.13.8 | 2024-06-05 | Bug fix for file loading | |
| 1.13.7 | 2024-06-04 | Bug fix for file loading | |
| 1.13.6 | 2024-06-03 | Bug fix for the NirsitOn+ protocols with 32Hz | |
| 1.13.5 | 2024-05-30 | Added support for the NirsitOn+ protocols with 32Hz | |
| 1.13.4 | 2024-05-23 | Added fetal heart rate and uterine activity | |
| 1.13.3 | 2024-05-23 | Tested for BIS vista protocol | |
| 1.13.2 | 2024-04-26 | Support for BIS vista protocol | |
| 1.13.1 | 2024-04-26 | Support for BIS vista protocol | |
| 1.12.12 | 2024-04-09 | Deployment script updated | |
| 1.12.11 | 2024-04-06 | Deployment script updated | |
| 1.12.10 | 2024-02-25 | Deployment script updated | |
| 1.12.8.0 | 2024-02-25 | Added support for GE Coro fetal monitor | |
| 1.12.8 | 2024-02-25 | Added support for GE Coro fetal monitor | |
| 1.12.7.0 | 2024-02-24 | Added support for GE Coro fetal monitor | |
| 1.12.6.0 | 2024-01-05 | Minor bug fix | |
| 1.12.5.0 | 2023-12-22 | Minor bug fix | |
| 1.12.4.0 | 2023-12-22 | Minor bug fix Added support of Pleth(Left, Right) and Aortic pressure for the Intellivue protocol Added option for the cut by heart rate Added option for setting the waveform by its name - Intellivue: I, II, III, V, aVR, aVL, avF, MCL, MCL1, V1, V2, V3, V4, V5, V6, PLETH, ABP, ART, CVP, FAP, PAP, ICP, EEG, CO2, RESP, AWF, AWP - S5: ECG1, ECG2, EEGBIS, EEGENT, EEGENT400, IABP1, IABP2, IABP3, IABP4, IABP5, IABP6, IABP7, IABP8, EEG1, EEG2, PLETH, CO2, O2, N2O, AWP | |
| 1.12.1.0 | 2023-12-11 | Minor bug fixes Improvement of the Bx50 protocol Improvement of the NIHONKOHDEN protocol | |
| 1.12.0.0 | 2023-12-05 | Supported protocol for Drager B1x5M. | |
| 1.11.20.0 | 2023-11-07 | Supported protocol for Daiwha TCI pump. | |
| 1.11.19.0 | 2023-10-23 | Additional pleth waveforms for the Philips protocol Minor Bug fixes | |
| 1.11.18.0 | 2023-09-14 | Bug fix for EGA protocol Minor Bug fixes | |
| 1.11.17.0 | 2023-09-08 | Bug fix for EGA protocol | |
| 1.11.16.0 | 2023-08-30 | Bug fix for EGA protocol | |
| 1.11.15.0 | 2023-08-29 | Bug fix for EGA protocol | |
| 1.11.14.0 | 2023-08-29 | Bug fix for EGA protocol | |
| 1.11.13.0 | 2023-08-28 | Bug fix for EGA protocol | |
| 1.11.12.0 | 2023-08-08 | EGA protocol supported PulsioFlex protocol supported | |
| 1.11.11.0 | 2023-07-12 | Fixed bug for TOFCuff | |
| 1.11.10.0 | 2023-06-01 | Fixed bug for VitalConnect | |
| 1.11.9.0 | 2023-05-30 | Fixed bug for BUTTON | |
| 1.11.8.0 | 2023-05-27 | Fixed bug for MEKICS Fixed bug for remote update | |
| 1.11.7.0 | 2023-05-24 | Added support for NirsitOn+ | |
| 1.11.6.0 | 2023-05-23 | Added support for NirsitOn+ | |
| 1.11.5.0 | 2023-05-22 | Added support for NirsitOn+ | |
| 1.11.4.0 | 2023-05-22 | Added support for NirsitOn+ | |
| 1.11.3.0 | 2023-05-21 | Added support for SNUSensor | |
| 1.11.2.0 | 2023-05-16 | Added support for SNUSensor | |
| 1.11.1.0 | 2023-05-03 | Added support for TOFcuff | |
| 1.11.0.0 | 2023-03-29 | Added support for TOFcuff | |
| 1.10.21.0 | 2023-02-16 | Added support for TOFcuff | |
| 1.10.20.0 | 2023-02-05 | Added support for file cutting based on SpO2 | |
| 1.10.19.0 | 2023-01-12 | Added support for file cutting based on SpO2 | |
| 1.10.18.0 | 2022-12-16 | Added support for AU protocol | |
| 1.10.17.0 | 2022-12-16 | Added support for AU protocol | |
| 1.10.16.0 | 2022-12-14 | Added support for AU protocol | |
| 1.10.15.0 | 2022-12-12 | Added support for AU protocol | |
| 1.10.14.0 | 2022-12-08 | Added support for AU protocol | |
| 1.10.13.0 | 2022-12-07 | Added support for AU protocol | |
| 1.10.12.0 | 2022-12-05 | Added support for AU protocol | |
| 1.10.11.0 | 2022-12-05 | Added support for AU protocol | |
| 1.10.10.0 | 2022-12-02 | Added support for AU protocol | |
| 1.10.9.0 | 2022-12-01 | Fixed bug for Link+ protocol | |
| 1.10.8.0 | 2022-11-29 | Fixed bug for Servo-i Ventilator Fixed bug for Hamilton Ventilator | |
| 1.10.7.0 | 2022-11-08 | Improved support for Nihon kohden ADT | |
| 1.10.6.0 | 2022-11-04 | Improved support for Nihon kohden HL7GW | |
| 1.10.4.0 | 2022-11-02 | Hamiltonian ventilator supported | |
| 1.10.3.0 | 2022-10-21 | Hamiltonian ventilator supported | |
| 1.10.2.0 | 2022-10-14 | Hamiltonian ventilator supported | |
| 1.10.0.0 | 2022-09-15 | Minor bug fix Added input dialog for patient ID when intranet server is used | |
| 1.9.11.0 | 2022-07-29 | Minor bug fix Added input dialog for patient ID when intranet server is used | |
| 1.9.10.0 | 2022-06-26 | Minor bug fix Added input dialog for patient ID when intranet server is used | |
| 1.9.9.0 | 2022-06-03 | Minor bug fix Added input dialog for patient ID when intranet server is used | |
| 1.9.8.0 | 2022-05-17 | Minor bug fix VitalSync compatible with VitalDB Platform API | |
| 1.9.7.0 | 2022-03-25 | Display patient id on the title bar Support for barcode reader Minor bug fix | |
| 1.9.6.0 | 2022-03-25 | Additional Parameter (RPVI) for Masimo Root Bug fix for VitlaUtils | |
| 1.9.4.0 | 2022-03-10 | Batch running of filter on VitalUtils | |
| 1.9.3.0 | 2022-03-09 | Local filter server as default Python Installer | |
| 1.9.2.0 | 2022-02-17 | Added ABP montype to ClearSight | |
| 1.9.1.0 | 2022-02-16 | Supported MedibusX protocol | |
| 1.9.0.0 | 2022-02-14 | Supported LiDCO protocol | |
| 1.8.22.0 | 2022-02-10 | Added filter server running on localhost Moved VC setting dialog to the Setting dialog Fixed minor bugs | |
| 1.8.21.1 | 2022-02-07 | Fixed a bug with the Intellivue | |
| 1.8.21.0 | 2022-01-21 | Fixed a bug with the Intellivue | |
| 1.8.20.1 | 2022-01-15 | Fixed a bug with the Intellivue | |
| 1.8.20.0 | 2022-01-13 | Requesting O2 and N2O waves to the Bx50. Fixed a bug with the channel names of FIO2 and FIO2 of the Bx50. | |
| 1.8.19.5 | 2021-12-17 | Bug fix for Intellivue protorol | |
| 1.8.19.4 | 2021-12-09 | Bug fix for Intellivue protorol | |
| 1.8.19.0 | 2021-11-15 | Bug fix for Intellivue protorol | |
| 1.8.18.0 | 2021-10-09 | New Installer | |
| 1.8.17.2 | 2021-10-06 | New Installer |
| Version | Date | Changes | |
|---|---|---|---|
| 1.18.40 | 2026-05-12 | Hotfix: Drager Medibus 프로토콜 사용 베드에서 1.15.11 이후 vr.service 가 SIGSEGV 로 무한 재시작하던 문제. 1.15.10 (a8c7b2a) 의 "Sampling rate estimation" 리팩터링이 m_ptrk_awp/awf/co2 트랙을 add_trks() 에서의 eager 생성 (default srate=62.5Hz) 에서 realtime data 100 샘플 모인 후 lazy 생성으로 바꿨는데, Medibus 핸드셰이크 순서 (0x51 ICC → 0x52 DeviceID → 0x53 RealtimeConfig 응답 → 0x54 → realtime data 시작) 상 0x53 응답 핸들러 (Drager.cpp:295-307) 가 트랙들이 아직 nullptr 인 시점에 `ptrk->m_srate = 1000000.0/interval` 로 NULL 역참조 → 즉시 SEGV. 그 베드의 vr.service 가 systemd Restart=on-failure 로 재시작 → 다시 connect → 동일 0x53 패킷 받고 동일 SEGV → 무한 루프 (journald 로그상 restart counter 61 까지 누적, code=killed status=11/SEGV). 1.15.9 까진 트랙이 add_trks() 에서 생성되어 0x53 도착 시 항상 non-NULL 이라 안전했음. 수정: (a) add_trks() 에서 m_ptrk_awp/awf/co2 eager 생성 복원 (srate=62.5Hz default) — 0x53 는 ptrk->m_srate 만 갱신 (트랙 재생성 X). (b) on_received() 의 lazy creation + packet-timing srate 추정 블록 제거 — 0x53 가 권위 있는 srate 소스이고 packet 타이밍 기반 추정은 network jitter 에 약해 불안정. (c) 0x53 핸들러에 `if (interval > 0)` guard 추가 — 페이로드 깨졌을 때 div-by-zero / inf srate 방지. 영향 범위: Drager Primus / Apollo / Perseus / Fabius 등 Medibus 사용 마취기 전부. 우회 방법은 1.15.9 다운그레이드 외엔 없었음 (그 베드의 모든 데이터 유실 + 환자 모니터링 불가). MFC + Qt + Pi (RPI64) + Ubuntu Qt + AppImage 빌드 모두 통과. | |
| 1.18.37 | 2026-05-08 | Refactored: 필터 다이얼로그의 python 런타임 — pyvital → openvital 0.3.0 로 교체 + 동봉 python.zip 슬림화. 핵심 동기 셋: (1) pyvital 0.6.0 이 tensorflow+torch+keras 를 hard-dep 으로 끌어들여 PyPI upgrade 가 사실상 깨짐, (2) 기존 zip 121 MB 안에 botocore/aiobotocore/s3fs/pyarrow/pandas/aiohttp 등 vitaldb 클라우드 업로드용 deps 가 ~250 MB unpacked 차지 — 필터 서버는 한 번도 쓴 적 없는 dead weight, (3) sanic 25.x 의 multi-process worker 가 openvital __main__.py 를 fork 후 재import 하면서 worker 가 module-top app instance 를 못 찾아 "Sanic app name not found" 로 죽음 — 단순 endpoint 2개 로컬호스트 서버에 async stack 자체가 과함. 해결: openvital 0.3.0 (commit b6f445b) 가 sanic 을 stdlib http.server (BaseHTTPRequestHandler + HTTPServer) 로 50 LOC 재작성 — wire protocol (GET / 필터목록 JSON, POST /<modname> gzip 본문) 동일해 VR 클라이언트 변경 0줄. 단일 스레드로 기존 sanic event loop semantics (cfgs/default_cfgs lock-free 변경) 보존. build_python_zip.py (NEW) 가 Python 3.11.9 embed + numpy + openecg + openvital (local checkout 우선, fallback PyPI) 만으로 27 MB zip 빌드 — 기존 121 MB 대비 78% 감소. ML 필터 (sv_dlapco/abp_hpi/ecg_classifier/ecg_beat_noise_detector) 는 [Hybrid 모델] 사용자가 Add filter 다이얼로그의 "Install ML filters" 버튼 누를 때 silent pip install openvital[all] (cmd 창 미노출 — VRApp::install_pip_package + run_pip_silent 이 자체 progress 다이얼로그에 stdout heartbeat 흘림). 버튼 라벨은 "Upgrade pyvital" → "Install ML filters" 로 의도 명시. openvital __main__.py 가 missing extras 를 graceful skip 하므로 base 환경에서도 11개 필터 (ECG QRS detector, HRV, MTWA, annotator, eeg_fft, nirs_cox, pkpd_3comp, pleth_dpop/ptt/pvi, resp_compliance) 즉시 사용 가능. Migration: 기존 사용자의 user_dir/python 폴더는 pyvital + 옛 deps 가 그대로 남아있고 vitaldb.net/python.zip 만 새로 올리면 (a) 기존 폴더 삭제 후 install_python 으로 새 zip 받으면 깔끔, (b) 폴더 유지 시 server 가 openvital 못 찾아 필터 다이얼로그 에러 → release note 에 명시 필요. MFC + Qt 양쪽 코드: VRGui.rc 의 IDC_UPGRADE 버튼 라벨 + DlgAddFilter.cpp 의 dist-info prefix 매칭 (pyvital- 8자 → openvital- 10자) + QtDlgAddFilter.cpp 도 동일 변경 + onUpgrade 가 cmd /k QProcess::startDetached 대신 theApp.install_pip_package("openvital[all]") 호출 (Linux Qt 빌드는 #ifdef _WIN32 가드). VRApp.cpp:318 의 python -m pyvital → -m openvital. install_pip_package 는 anonymous namespace 의 is_safe_pkg_spec 화이트리스트 검증 + run_pip_silent (CreateProcessA + CREATE_NO_WINDOW + 자식 stdout pipe) + CDlgProgress / QtDlgProgress 자체 progress UI. 빌드 검증: python -m openvital 이 GET / 로 11 필터 반환, sanic import 부재 확인. zip 27.5 MB. | |
| 1.18.33 | 2026-04-25 | Fixed: Demo device never started since 1.18.11 (bed_manager skipped empty port_name as passive but Demo has no port and still needs lock_and_open); Fixed: Qt fit-to-window button rendered at top-left (0,0) instead of right-aligned in bottom nav bar; Fixed: Qt crash when closing tab / deleting device / deleting filter - paint dereferenced freed pointer before panel timer rebuilt items (added MFC-style stale-pointer guard to QtDeviceItem and QtFilterItem); Added: macOS DMG main window now shown on launch (restoreWindowPos had no Mac/Linux branch); Added: macOS Homebrew dylibs (openssl) bundled in Contents/Frameworks; Added: macOS .app bundle now notarized + stapled (drag-to-Applications no longer needs xattr -cr on first launch); Changed: Qt MSI ~1MB smaller (dropped unused iconengines and imageformats plugins) | |
| 1.18.31 | 2026-04-22 | Added: drug slot shows DOSE_AMOUNT (mg etc) in preference to VOL (mL) when available; Added: DOSE_RATE / DOSE_AMOUNT montypes bound to BBraun tracks for monitor view; Added: HL7 MDC_DOSE_DRUG_DELIV_TOTAL unit picked up dynamically from UCUM |
| Version | Date | Changes | |
|---|---|---|---|
| 1.15.10 | 2025-07-14 | Bug Fixed: Sampling rate estimation in Medibus protocol. | |
| 1.15.9 | 2025-06-03 | Bug Fixed: EEG waveform labels from Philips monitors. | |
| 1.15.8 | 2025-05-26 | Added: EM_UPLOAD option to upload vital files to server immediately | |
| 1.15.7 | 2025-05-26 | Bug Fixed: Websocket connection error | |
| 1.15.6 | 2025-05-19 | Bug Fixed: S5 checksum error | |
| 1.15.5 | 2025-04-17 | Bug Fixed: saving the raw data | |
| 1.15.4 | 2025-04-15 | Added: Recording alarms from Philips monitor | |
| 1.15.3 | 2025-04-14 | Bug fixed: saving the raw data | |
| 1.15.2 | 2025-03-12 | Fixed: bug in opening file | |
| 1.15.1 | 2025-01-30 | Supporting for Fresenius Kabi Conox, Minor Bug fixes | |
| 1.14.13 | 2025-01-18 | Fixed bug in handling non-ascii filepath | |
| 1.14.12 | 2024-11-20 | Fixed: bug in 64bit ARM Architecture | |
| 1.14.11 | 2024-10-24 | Added: feature for updating to a specific version | |
| 1.14.10 | 2024-10-22 | Fixed: bug in json streamming | |
| 1.14.9 | 2024-10-17 | Added: montype for drug rates | |
| 1.14.6 | 2024-09-20 | Added: montype for drug rates | |
| 1.14.5 | 2024-09-13 | Added: auto cutting by any of hr or spo2 | |
| 1.14.4 | 2024-09-12 | Fixed: bug in serial port mapping | |
| 1.14.3 | 2024-09-10 | Added: temperature monitoring Fixed: bug in cpu usage calculation | |
| 1.14.2 | 2024-08-29 | Added: DI-245 protocol | |
| 1.14.1 | 2024-07-26 | Added: GE cardiac output parameters | |
| 1.14.0 | 2024-07-25 | Added: GE Canvas device | |
| 1.13.10 | 2024-07-17 | Fixed: NirsitOn protocol bug | |
| 1.13.9 | 2024-06-27 | Bug fix for file loading | |
| 1.13.8 | 2024-06-05 | Bug fix for file loading | |
| 1.13.7 | 2024-06-04 | Bug fix for file loading | |
| 1.13.6 | 2024-06-03 | Bug fix for the NirsitOn+ protocols with 32Hz | |
| 1.13.5 | 2024-05-30 | Added support for the NirsitOn+ protocols with 32Hz | |
| 1.13.4 | 2024-05-23 | Added fetal heart rate and uterine activity | |
| 1.13.3 | 2024-05-23 | Tested for BIS vista protocol | |
| 1.13.2 | 2024-04-26 | Support for BIS vista protocol | |
| 1.13.1 | 2024-04-26 | Support for BIS vista protocol | |
| 1.12.12 | 2024-04-09 | Deployment script updated | |
| 1.12.11 | 2024-04-06 | Deployment script updated | |
| 1.12.10 | 2024-02-25 | Deployment script updated | |
| 1.12.8 | 2024-02-25 | Added support for GE Coro fetal monitor | |
| 1.12.7.0 | 2024-02-24 | Added support for GE Coro fetal monitor | |
| 1.12.6.0 | 2024-01-05 | Minor bug fix | |
| 1.12.5.0 | 2023-12-22 | Minor bug fix | |
| 1.12.4.0 | 2023-12-22 | Minor bug fix Added support of Pleth(Left, Right) and Aortic pressure for the Intellivue protocol Added option for the cut by heart rate Added option for setting the waveform by its name - Intellivue: I, II, III, V, aVR, aVL, avF, MCL, MCL1, V1, V2, V3, V4, V5, V6, PLETH, ABP, ART, CVP, FAP, PAP, ICP, EEG, CO2, RESP, AWF, AWP - S5: ECG1, ECG2, EEGBIS, EEGENT, EEGENT400, IABP1, IABP2, IABP3, IABP4, IABP5, IABP6, IABP7, IABP8, EEG1, EEG2, PLETH, CO2, O2, N2O, AWP | |
| 1.12.3.0 | 2023-12-12 | Minor bug fixes Improvement of the Bx50 protocol Improvement of the NIHONKOHDEN protocol | |
| 1.12.2.0 | 2023-12-11 | Minor bug fixes Improvement of the Bx50 protocol Improvement of the NIHONKOHDEN protocol | |
| 1.12.1.0 | 2023-12-11 | Minor bug fixes Improvement of the Bx50 protocol Improvement of the NIHONKOHDEN protocol | |
| 1.12.0.0 | 2023-12-05 | Supported protocol for Drager B1x5M. | |
| 1.11.20.0 | 2023-11-07 | Supported protocol for Daiwha TCI pump. | |
| 1.11.18.0 | 2023-09-14 | Bug fix for EGA protocol Minor Bug fixes | |
| 1.11.17.0 | 2023-09-08 | Bug fix for EGA protocol | |
| 1.11.11.0 | 2023-07-12 | Fixed bug for TOFCuff | |
| 1.11.10.0 | 2023-06-01 | Fixed bug for VitalConnect | |
| 1.11.9.0 | 2023-05-30 | Fixed bug for BUTTON | |
| 1.11.8.0 | 2023-05-27 | Fixed bug for MEKICS Fixed bug for remote update | |
| 1.11.7.0 | 2023-05-24 | Added support for NirsitOn+ | |
| 1.11.6.0 | 2023-05-23 | Added support for NirsitOn+ | |
| 1.11.4.0 | 2023-05-22 | Added support for NirsitOn+ | |
| 1.11.3.0 | 2023-05-21 | Added support for SNUSensor | |
| 1.11.1.0 | 2023-05-03 | Added support for TOFcuff | |
| 1.10.19.0 | 2023-01-12 | Added support for file cutting based on SpO2 | |
| 1.10.17.0 | 2022-12-16 | Added support for AU protocol | |
| 1.10.15.0 | 2022-12-12 | Added support for AU protocol | |
| 1.10.13.0 | 2022-12-07 | Added support for AU protocol | |
| 1.10.11.0 | 2022-12-05 | Added support for AU protocol | |
| 1.10.10.0 | 2022-12-02 | Added support for AU protocol | |
| 1.10.9.0 | 2022-12-01 | Fixed bug for Link+ protocol | |
| 1.10.8.0 | 2022-11-29 | Fixed bug for Servo-i Ventilator Fixed bug for Hamilton Ventilator | |
| 1.10.7.0 | 2022-11-08 | Improved support for Nihon kohden ADT | |
| 1.10.6.0 | 2022-11-04 | Improved support for Nihon kohden HL7GW | |
| 1.10.4.0 | 2022-11-02 | Hamiltonian ventilator supported | |
| 1.10.3.0 | 2022-10-21 | Hamiltonian ventilator supported | |
| 1.10.2.0 | 2022-10-14 | Hamiltonian ventilator supported | |
| 1.10.1.0 | 2022-10-07 | Minor bug fix Added input dialog for patient ID when intranet server is used | |
| 1.10.0.0 | 2022-09-15 | Minor bug fix Added input dialog for patient ID when intranet server is used | |
| 1.9.11.0 | 2022-07-29 | Minor bug fix Added input dialog for patient ID when intranet server is used | |
| 1.9.10.0 | 2022-06-26 | Minor bug fix Added input dialog for patient ID when intranet server is used | |
| 1.9.8.0 | 2022-05-17 | Minor bug fix VitalSync compatible with VitalDB Platform API | |
| 1.9.7.0 | 2022-03-25 | Display patient id on the title bar Support for barcode reader Minor bug fix | |
| 1.9.6.0 | 2022-03-25 | Additional Parameter (RPVI) for Masimo Root Bug fix for VitlaUtils | |
| 1.9.2.0 | 2022-02-17 | Added ABP montype to ClearSight | |
| 1.9.1.0 | 2022-02-16 | Supported MedibusX protocol | |
| 1.9.0.0 | 2022-02-14 | Supported LiDCO protocol | |
| 1.8.22.0 | 2022-02-10 | Added filter server running on localhost Moved VC setting dialog to the Setting dialog Fixed minor bugs | |
| 1.8.21.1 | 2022-02-07 | Fixed a bug with the Intellivue | |
| 1.8.21.0 | 2022-01-21 | Fixed a bug with the Intellivue | |
| 1.8.20.1 | 2022-01-15 | Fixed a bug with the Intellivue | |
| 1.8.20.0 | 2022-01-13 | Requesting O2 and N2O waves to the Bx50. Fixed a bug with the channel names of FIO2 and FIO2 of the Bx50. | |
| 1.8.19.6 | 2021-12-24 | ADT_SERVER_IP | |
| 1.8.19.5 | 2021-12-17 | Bug fix for Intellivue protorol |
| Version | Date | Changes | |
|---|---|---|---|
| 1.19.9 | 2026-08-14 | 1.19.9 Fixed: 큰 녹화 파일(약 100MB 이상)을 서버로 올릴 때 프로그램이 메모리 부족으로 강제 종료되고, 재시작 후 같은 파일을 다시 시도하다 무한 반복되던 문제 — 업로드 본문을 메모리에 통째로 만들지 않고 디스크에서 흘려보내도록 변경(100MB 파일 기준 최대 메모리 208MB → 14MB). 큰 파일이 한 개라도 쌓이면 그 장비의 이후 업로드가 전부 막히던 증상이 사라진다. 1.19.8 Fixed: 자동 파일 자르기(CUT_FILE) 를 쓰는 베드에서 환자가 계속 연결돼 있는데도 녹화 파일이 PT_WAITING_TIME(기본 5분) 마다 잘리던 문제 — 웹 모니터링 전송 후 메모리를 비우는 과정에서 환자 연결 판정에 쓰이는 최근 구간까지 지워져 "환자가 나갔다" 로 오판하던 것. 이제 판정에 필요한 구간은 보존한다. 파일이 5분마다 끊기고 그때마다 수 초가 유실되던 증상이 사라진다. 1.19.7 Added: GE Corometrics(분만 태아감시) 모체 vitals 지원 — 기존엔 태아 심박(HR1/HR2)·자궁수축(UACT)만 기록됐으나 이제 같은 장비가 함께 보내는 모체 SpO2/맥박(PR)/NIBP(수축·이완·평균)도 한 파일에 기록. Fixed: 프로세스 종료(서비스 매니저의 정상 종료 요청, SIGTERM) 시 즉시 종료되지 않고 강제 종료되던 문제 — 이제 SIGTERM 을 받아 녹화 버퍼를 flush 하고 스레드를 정리한 뒤 정상 종료. 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.8 | 2026-08-14 | 1.19.8 Fixed: 자동 파일 자르기(CUT_FILE) 를 쓰는 베드에서 환자가 계속 연결돼 있는데도 녹화 파일이 PT_WAITING_TIME(기본 5분) 마다 잘리던 문제 — 웹 모니터링 전송 후 메모리를 비우는 과정에서 환자 연결 판정에 쓰이는 최근 구간까지 지워져 "환자가 나갔다" 로 오판하던 것. 이제 판정에 필요한 구간은 보존한다. 파일이 5분마다 끊기고 그때마다 수 초가 유실되던 증상이 사라진다. 1.19.7 Added: GE Corometrics(분만 태아감시) 모체 vitals 지원 — 기존엔 태아 심박(HR1/HR2)·자궁수축(UACT)만 기록됐으나 이제 같은 장비가 함께 보내는 모체 SpO2/맥박(PR)/NIBP(수축·이완·평균)도 한 파일에 기록. Fixed: 프로세스 종료(서비스 매니저의 정상 종료 요청, SIGTERM) 시 즉시 종료되지 않고 강제 종료되던 문제 — 이제 SIGTERM 을 받아 녹화 버퍼를 flush 하고 스레드를 정리한 뒤 정상 종료. 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.7 | 2026-08-12 | 1.19.7 Added: GE Corometrics(분만 태아감시) 모체 vitals 지원 — 기존엔 태아 심박(HR1/HR2)·자궁수축(UACT)만 기록됐으나 이제 같은 장비가 함께 보내는 모체 SpO2/맥박(PR)/NIBP(수축·이완·평균)도 한 파일에 기록. Fixed: 프로세스 종료(서비스 매니저의 정상 종료 요청, SIGTERM) 시 즉시 종료되지 않고 강제 종료되던 문제 — 이제 SIGTERM 을 받아 녹화 버퍼를 flush 하고 스레드를 정리한 뒤 정상 종료. 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.5 | 2026-08-06 | 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.4 | 2026-08-05 | 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.3 | 2026-08-04 | 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.0 | 2026-08-03 | 1.19.0 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스 전체가 SIGSEGV 로 죽던 문제. accept_handoff 가 교체(replace) 시 옛 소켓을 즉시 closesocket 했는데, m_sock_mtx 는 fd 번호만 보호할 뿐 fd 의 수명은 보호하지 않아 reader 가 그 fd 로 recv 중일 수 있었다. close 는 blocking recv 를 깨우지도 않고 fd 번호를 즉시 재사용 가능하게 만들어, 다음 접속이 같은 번호를 받으면 reader 가 남의 세션을 읽었다. SNUH 회복실(VRN 15대, 최다 베드는 하루 785회 재접속)에서 실제로 크래시 후 9시간 48분 전 베드 무수집. 수정 세 겹: (1) tcp reader 를 poll(POLLIN,200ms) 게이트 뒤의 recv 로 재구성 - recv 가 무기한 블로킹되지 않으므로 close/replace 가 늦어도 200ms 안에, shutdown 이 동반되면 즉시 감지된다. fd 에 O_NONBLOCK 은 걸지 않는다(같은 fd 로 송신하는 write_transport 의 부분 전송 방지). (2) replace 는 closesocket 대신 shutdown 만 하고 옛 fd 를 retired 목록에 넣으며, 닫기는 깨어난 reader 또는 Serial::close 가 정확히 한 번 수행 - fd 번호 재사용 창이 사라진다. (3) Serial::close 가 join 전에 shutdown 으로 reader 를 깨우고(RCVTIMEO 는 설정된 적이 없어 종전에는 피어가 끊을 때까지 join 이 매달렸다), m_sock 접근을 전부 락으로 감싼다. has_socket 의 무락 읽기와 udp_thread_func 의 이중 close 도 같은 계열이라 함께 수정. Added: AUTO_DETECT=1 (vr.conf) - 시리얼 포트에 무엇이 물려 있는지 스스로 알아내 즉시 수집을 시작한다. 판별용 프로브 바이트를 따로 두지 않고 후보 드라이버를 실제로 열어 유효 레코드 도달 여부로 판정하므로 장비별 판별 코드가 없고 드라이버는 무수정이다. 4단계: 회선 측정(커널 프레이밍/패리티/브레이크 카운터로 보레이트와 프레임 폭 확정) -> 패리티 추론(10비트 프레임의 7E1/7O1 대 8N1 모호성을 MSB 상관으로 해소) -> 후보 필터(보레이트+프레임폭+데이터비트 3축 일치, 베드당 환자모니터 1대/마취기 1대 배타, 포트 위치 사전확률, 지난 판별 결과 캐시) -> 남으면 트랙 수확량으로 경쟁. SNUH 수술장 9방 + ICU 3베드 실측에서 Intellivue/MedibusX/Datex-Ohmeda/Primus 를 오탐 0 으로 판별했고, 표준과 다른 포트에 꽂힌 장비(P3/P4 의 Intellivue)도 찾아냈다. AUTO_DETECT 가 없으면 스캐너는 유휴 대기만 하므로 기존 설정에 영향이 없다. | |
| 1.18.52 | 2026-07-31 | 1.18.52 Changed: VR 기본 운영이 k3s 로 이관됨에 따라 서버→VR 하향 제어 경로를 제거. VitalServer 로는 데이터를 일방 push 만 하고, websocket(socket.io) 으로 내려오던 원격 명령 update/restart/reboot/edit_conf/new_bed/del_bed/edit_bed/dt/add_event 를 더 이상 처리하지 않는다 (원격 재부팅·바이너리 교체·conf 덮어쓰기·베드 구성변경 봉쇄). 구현: sendweb_thread_func 의 socket.io 42 이벤트 dispatch 와 on_sio_msg 함수를 통째로 제거. engine.io ping(2->3) 및 ws 레벨 ping/pong 은 연결 유지를 위해 그대로 응답하므로 데이터 업링크·웹모니터링은 종전과 동일. 서버는 VR 버전을 알므로 52+ 로는 명령을 보내지 않고, 받아도 무시. Fixed: --console 모드 (MFC) 에서 stdout 이 이미 유효한 파이프/파일 (> log.txt, ssh 파이프, cmd 상속 핸들) 인데도 무조건 _open_osfhandle+_dup2 로 재배선하면서 그 OS 핸들을 닫아버려, 초기화 이후 모든 로그가 조용히 사라지던 문제 (콘솔 모드 서버 배포에서 진단 불능의 원인, 1.18.7 부터 잠재). stdout 핸들이 유효하면 (GetFileType != UNKNOWN) 재배선하지 않고, 핸들이 없을 때만 (Explorer/schtasks 실행) AttachConsole 로 부모 콘솔에 붙인다. 실측: 종전 초기화 후 로그 침묵 -> 수정 후 지속 출력. (앞서 1.18.51 의 VRQt qDebug->stdout 통일과 함께 세 빌드의 --debug 로그 일관성 완성.) | |
| 1.18.51 | 2026-07-30 | 1.18.51 Fixed: VRQt (Qt 빌드) 에서 --debug 옵션을 켜도 콘솔에 로그가 전혀 출력되지 않던 문제. 원인: Qt 빌드의 TRACE 가 qDebug() 로 매핑되어 있었는데 (UTIL.h, 1.14.4 부터), Windows 에서 Qt 로깅은 GUI 앱이면 콘솔/파이프가 아니라 디버거 (OutputDebugString) 로만 보내므로 VRQt 만 --debug 콘솔 출력이 구조적으로 불가능했다 (MFC 는 stdafx.h 의 #undef TRACE 로 printf 버전을 사용해 정상). 수정: TRACE 를 세 빌드 (MFC/Qt/CLI) 공통 fprintf(stdout)+fflush 로 통일 - qDebug 직접 사용처는 코드에 없음을 확인. 이제 --debug 동작과 리다이렉트 (> log.txt) 가 전 빌드에서 일관되고, cmd 대화형 실행 시에는 기존 AttachConsole/AllocConsole 로직 (1.18.30) 이 콘솔을 만들어 준다. terapc 실측 검증: 종전 VRQt stdout 0 chars → 수정 후 4,690 chars 정상 출력, Vital.exe (MFC) 는 종전과 동일 정상. 추가 방어: --debug 의 콘솔 확보 검사에 GetFileType 추가 - 일부 런처/셸이 non-NULL 무효 stdout 핸들을 상속시키면 NULL 검사만으로는 AttachConsole 을 건너뛰어 로그가 사라질 수 있던 엣지 케이스 (MFC/Qt 공통). | |
| 1.18.50 | 2026-07-30 | 1.18.50 Fixed: 장비 연결이 한번 끊어지면 다시 붙지 않던 문제 (Link+ 현장 리포트, Intellivue 늦은 전원-온과 공통 근본 원인). 근본 원인: DEVICE::open() 의 초기화 호출 set_rx_time(0) 이 값 0 인데도 OPENING→ACTIVE 로 승격시키고 m_dtStatus 까지 0 으로 만들어, (1) 모든 장비가 open 즉시 가짜-ACTIVE (OPENING 상태가 사실상 부재), (2) restart 워치독의 dtlast(rx ?: status_time) 가 0 → read_timeout 조건이 영구 거짓 → 재연결 시 상대 무응답이면 (Link+ rack 재부팅/세션 잔존, 세션 프레임 1회 전송 후 무응답) 재시도 없이 영구 고착. 1.18.35 이하의 rec_timeout (상태 무관 60s 강제 재시작) 이 이 잠재 버그를 우연히 가리고 있었고 1.18.36 전환으로 노출된 것. 수정 3건: (1) set_rx_time 은 실수신 (dt>0) 일 때만 ACTIVE 승격 - 상태 표시도 정직해짐 (open 후 첫 수신까지 "con"), (2) restart 워치독에 OPENING-stuck 분기 추가 - open 후 read_timeout 내 첫 수신이 없으면 재시작해 협상 프레임 재전송, tx_time>0 (protocol-initiator) 조건으로 passive/keyword-sub 디바이스는 제외해 유휴 churn 없음, (3) tx_time 을 모든 transport (socket/fd/COM) 에서 갱신 (기존 Windows COM 만). 영향 장비: Link+/Agilia, Intellivue, S5, Datex, Hamilton 등 open 시 세션/assoc 을 시작하는 모든 protocol-initiator. E2E 검증: 무응답 서버 재현에서 종전 접속 1회 후 영구 침묵 → 수정 후 30초마다 재접속 반복, 장비 복구 시 자동 연결. Mindray 멀티베드 회귀 테스트 PASS (ACK 75/75, tier1 라우팅, probe 거절, stuck 오발동 0). Intellivue 1.18.48 assoc-retry (5s) 는 유지 - 이번 워치독은 30s 백스톱으로 공존. | |
| 1.18.49 | 2026-07-29 | 1.18.49 Fixed: (1) Mindray eGateway HL7 멀티베드 (catch-all + #키워드 sub) 에서 전 베드가 readonly=1 이면 데이터가 전혀 수집되지 않고 게이트웨이가 수초마다 재접속하던 문제. 원인 셋 - (a) readonly catch-all 이 open_socket 에서 조기 반환해 reader/parser 스레드가 아예 생성되지 않음 (아무도 안 읽는 소켓에 데이터가 쌓이다 게이트웨이가 연결 재수립 반복; PortRouter 리팩토링 시 readonly 체크가 blocking accept 앞으로 이동하며 생긴 1.18.36 회귀 - 1.18.31 은 accept 가 먼저라 우연히 동작), (b) readonly 가 HL7 ACK 송신까지 차단 (eGateway 는 메시지마다 ACK 요구), (c) sibling 베드 메시지는 broadcast 후 ACK 없이 return. 수정 - readonly 는 송신만 차단하고 수신은 정상화 (broadcast-only keyword sub 만 소켓 미보유), write_protocol 경로 신설로 ACK 는 readonly 우회, catch-all 이 sibling 메시지도 대신 ACK (R01/R40 타입 구분), broadcast 수신 sub 의 rx_time 갱신으로 상태표시 정상화, PortRouter takeover 는 기존 소켓이 stale (10초+ 무수신) 일 때만 허용해 게이트웨이 health-probe 접속이 live 데이터 소켓을 뺏지 못하게 함. 시뮬레이터 E2E 검증: ORU 75건 -> ACK 75건 (100%), probe 5회 전부 무해 거절, SU-2/SU-4 tier1 라우팅 정상. (2) Getinge/MAQUET Flow-i (FCI 프로토콜) tidal volume 이 실제의 절반으로 기록되던 문제. 원인: FCI 프로토콜 v0005 부터 TV 채널 (101/102) gain 이 0.1 -> 0.2 로 변경됐는데 (Flow-i communication interface v0007 문서), VR 은 open 시 SPVE0005 로 v5 를 요청하면서 파싱은 구버전 gain 0.1 을 적용. 병원 vital 파일 실측 (Flow-i 직결 TV 201.4/188.2 vs 필립스 경유 403.0/376.6, MV 는 정상) 으로 검증 - TV_INSP/TV_EXP gain 0.2 적용. MV 는 v3+ 스케일 (0.01) 이 이미 반영돼 있어 변경 없음. (3) Flow-i 설정 파라미터 미지원 (m_ch_settings 빈 맵) - v5+ 에 존재하는 수치 설정 채널 11종 등록: SET_RR(411), SET_INSP_TM(413), SET_IE(418), SET_MV(421), SET_PC(423), SET_PEEP(424), SET_FRESH_FLOW(428), SET_FRESH_O2(430), SET_AGENT_PERC(434), SET_PS(436), SET_SIMV_RR(437). gain 은 문서 표기 그대로. 주의: Set Tidal Volume (채널 422) 은 FCI v5 에서 프로토콜 자체에서 삭제되어 직접 수신 불가 - 볼륨 모드에서 SET_MV/SET_RR 로 파생 가능. 설정 채널은 문서 기반 구현으로 실기 검증 대기 상태. | |
| 1.18.48 | 2026-07-24 | 1.18.48 Fixed: VR 이 먼저 실행된 상태에서 Philips Intellivue 모니터를 나중에 켜면 웨이브가 영원히 수집되지 않던 문제 (VR 재시작 전까지 모니터로 아무 요청도 안 나감). 원인: Intellivue::open() 은 assoc_req 를 딱 한 번 보내는데, 시리얼(MIB)/UDP 는 모니터가 꺼져 있어도 포트가 열리므로 이 요청이 응답 없이 사라진다. Philips 는 자기가 먼저 말을 걸지 않는 프로토콜이고, 첫 수신이 있어야 디바이스 상태가 OPENING→ACTIVE 로 승격되는데 수신이 없으니 OPENING 에 고정 - DEVICE::restart_thread_func 워치독은 is_closed(재오픈)/is_active(read_timeout 30s) 두 분기만 있어 OPENING 은 어느 쪽에도 안 걸림 → assoc 재전송도 재시작도 영원히 없음. 수정: Intellivue 에 assoc 재시도 스레드 추가 - 세션 확립(MDS_CREAT 수신, m_bSessionUp atomic 플래그) 전까지 5초 간격으로 assoc_req 재전송, 최근 5초 내 수신이 있으면(assoc 진행 중) 재전송 보류. 모니터를 나중에 켜도 최대 5초 내 연결. 세션 확립 후에는 재시도 루프 종료 - 기존 동작(AB_SPDU 시 재연결, read_timeout 재시작)과 충돌 없음. 모니터가 켜져 있던 기존 시나리오는 첫 assoc_req 에 바로 응답하므로 동작 변화 없음 - 0-impact. | |
| 1.18.47 | 2026-07-24 | 1.18.47 Fixed: 파일 업로드(및 Http 클래스를 쓰는 모든 경로 - upload_vital, getvr 업데이트체크, ADT 조회, 필터서버)가 SERVER_IP/UPLOAD_SERVER_IP 에 "https://host:port" 풀 URL 형식을 넣으면 "Couldn't resolve host name" 으로 전부 실패하던 문제. 원인은 src/Https.cpp 의 Http 생성자 - 1.18.46 의 wss.cpp 수정과 동일한 버그의 나머지 반쪽. 첫 콜론을 포트 구분자로 오인해 "https://eicuvital.ywmc.or.kr:4432" 입력 시 m_strHost 가 문자 그대로 "https" 가 되어 DNS 조회 실패 (웹모니터링 wss 는 1.18.46 에서 이미 수정되어 정상, 파일 업로드만 실패하는 비대칭 발생 - 현장에서는 업로드 루프의 실패시 break 정책과 겹쳐 "베드명에 # 가 있으면 전송이 안 된다" 로 오인 관찰됨. 실제로는 # 파일이 디렉토리 순서상 항상 첫 시도라 거기서 host 오류로 중단 → 뒤 파일 전체가 막힌 것. 베드명 특수문자는 업로드 URL 과 무관). 수정: Http 생성자에 wss 와 동일한 선행 스킴(https/http) 파싱 추가 - 스킴이 TLS 여부를 결정 (포트 무관, 4432 같은 커스텀 포트에서 https 정상), "https://host" 포트 생략 시 443, 경로 붙은 입력도 host 만 추출. 기존 "host" / "host:443" 형식은 종전과 완전 동일 동작 - 0-impact. Windows(WinINet)/Linux(curl) 공통 생성자라 모든 플랫폼에 적용. | |
| 1.18.46 | 2026-07-23 | 1.18.46 Fixed: WSSClient 가 "https/wss 스킴 + 비표준 포트" 조합을 제대로 처리하지 못하던 문제 (커스텀 포트의 wss 서버에 연결 불가). 원인은 src/wss.cpp 의 WSSClient 생성자 두 곳 - (1) SERVER_IP 파싱이 첫 콜론을 포트 구분자로 써서 "https://host:4432" 를 넣으면 "https" 의 콜론을 먼저 잡아 m_host 가 "https" 가 되고 포트 파싱이 깨져 getaddrinfo 실패, (2) m_secure = (m_port == 443) 로 TLS 여부를 오직 포트 443 여부로만 판단해 4432 같은 커스텀 포트는 무조건 평문 ws 로 붙어 wss 전용 서버에서 실패. 수정: 생성자가 선행 URL 스킴(https/wss/http/ws)을 먼저 파싱해 스킴이 있으면 스킴이 TLS 를 결정(포트 무관)하고, 스킴이 없으면 기존 포트 443 휴리스틱을 유지(하위호환). 이제 SERVER_IP=https://eicuvital.ywmc.or.kr:4432 (또는 wss://...) 형식이 비표준 포트에서도 wss 로 정상 연결되며, SNI/인증서 검증은 도메인(m_host)을 쓰므로 와일드카드 인증서도 정상 매칭. 기존 host / host:443 설정은 종전대로 secure 유지 - 0-impact. | |
| 1.18.45 | 2026-06-16 | 1.18.45 Fixed: Windows VR 에서 사용자가 외부에서 vr.conf 를 멀티베드 설정으로 교체 후 VR 을 재실행하면, 첫 실행에서는 멀티탭이 정상 생성되지만 다음 실행에서 모든 탭이 사라지던 문제. 원인은 VRApp.cpp:1110 의 VRCODE 미설정 분기 — 새 vr.conf 가 VRCODE 라인을 포함하지 않으면 load_settings() 가 신규 VRCODE 를 issue_code() 로 발급하고 즉시 save_settings() 를 호출하는데, 이 시점은 load_settings() 가 파싱한 베드들이 로컬 newbeds 벡터에만 있고 아직 m_pdisps 로 옮겨지기 전 (set_beds(newbeds) 는 initialize() 가 load_settings 반환 후 라인 1328 에서 호출). 결과: save_settings() 가 빈 m_pdisps 를 iterate 하며 vr.conf 를 글로벌 설정만으로 덮어써 모든 [BED/...] 섹션이 디스크에서 사라짐. 첫 실행은 메모리의 newbeds 로 UI 멀티탭이 그려져 정상으로 보이지만 (사용자가 "멀티탭은 정상적으로 생성되지만" 으로 표현), VR 종료/재실행 시 디스크 conf 에는 BED 섹션이 없으므로 다음 load_settings 가 빈 베드 리스트를 반환 → 탭 전부 사라짐. 수정: VRApp.h 에 m_vrcode_newly_issued bool 플래그 추가, load_settings 의 즉시 save 를 플래그 set 만 으로 교체, initialize() 의 set_beds(newbeds) 직후 (m_pdisps 가 채워진 시점) 플래그가 켜져 있으면 한 번만 save_settings() 호출 → BED 섹션이 정상 직렬화됨. Linux/Pi 분기 (m_serial_number 가 채워져 있는 경우) 는 원래도 save 안 했으므로 영향 없음, VRCODE 가 이미 conf 에 있는 모든 기존 deployment 도 분기 미진입으로 0-impact. m_conf_from_stdin / m_bDebug 모드는 save_settings 내부에서 이미 early return 이라 그대로 안전. MFC + Qt 빌드 모두 통과 (이번 변경 관련 0 warning). | |
| 1.18.44 | 2026-06-12 | 1.18.44: vr.conf FHIR=1 모드 추가 (자체 FHIR 수신서버 대상 실시간 푸시) + VS 2026 / 새 OpenSSL 인스톨러 빌드 환경 호환 shim. (1) sendweb_thread_func 의 기존 HL7/JSON 양분 분기 (VRApp.cpp:2466) 옆에 bFHIR 분기 추가. 1초 tick 마다 한 vrcode 의 모든 방 Observation 을 하나의 FHIR R4 collection Bundle 로 묶어 기존 WSS 채널로 push (gzip + WSSClient 인프라 그대로 재사용). MLLP 같은 신규 transport 안 도입 — FHIR R5 Subscription 의 websocket channel type 과 동형이라 표준 부합. (2) DISPLAY::get_fhir + TRACK::get_fhir 신규. v2 (vitaldb-fhir-v2) 와 동일한 reference-only 패턴 (Patient.id = m_patient_id, Encounter.id = vrcode-bedname-startsec, Observation.subject/encounter = relative reference) + hybrid keyframe: 첫 tick / m_patient_id 변경 / get_start_time() 변경 / 5분 경과 중 하나라도 발생 시 다음 tick Bundle 에 Patient + Encounter resource 를 inline. 그 외 tick 은 Observation 만. 수신서버가 도중에 새로 connect 해도 최대 5분 안에 자동 동기화 (keyframe heartbeat). DISPLAY 에 m_fhir_pt_sent_at/m_fhir_pt_last_id/m_fhir_enc_last_start 3개 멤버 추가 (m_setting_sent 패턴 그대로 per-display). (3) Observation 인코딩: NUM 은 effectiveDateTime + valueQuantity 각 데이터 포인트당 한 entry, WAV 은 effectivePeriod + valueSampledData (period=1000/srate ms, factor=1, origin=0, data=공백구분 floats) 한 entry. CO2/AWP wav 는 기존 get_json 처럼 25Hz 다운샘플 유지. NUM 은 m_montype 기반 LOINC 매핑 15개 등록 (ECG_HR/PLETH_HR=8867-4, PLETH_SPO2=59408-5, NIBP/IABP SBP/DBP/MBP=8480-6/8462-4/8478-0, BT=8310-5, RR=9279-1, etCO2=19891-1, CVP=8556-0, FETAL_HR=11627-3) + VR 자체 코드 시스템 (https://vitaldb.net/fhir/CodeSystem/track, code = dname/tname) 항상 동반. m_patient_id 미설정 (PATIENT_ID_UNSET) 시 anon-vrcode-bedname identifier 로 fallback. (4) Bundle wrapper: VRApp 가 방별 entries 를 콤마로 합쳐 {resourceType:Bundle, type:collection, timestamp:iso8601Z, entry:[...]} 로 wrap, 기존 gzip → WSS pipeline 동일. ISO 8601 UTC 헬퍼는 std::gmtime_s/gmtime_r 인라인. FHIR id 안전화 (A-Za-z0-9-.) 64자 제한 lambda 각 함수에 inline. (5) 빌드 환경 호환성: VRQt/CMakeLists.txt 에 _SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS 추가 — VS 2026 (MSVC 14.51+) 의 STL 이 <experimental/coroutine> deprecation 을 warning → hard static_assert 로 바꾼 것이 cppwinrt 의 winrt::* (Serial.cpp 의 Bluetooth LE) 를 끌어오는 모든 TU 에서 컴파일 실패 시켜서, build_mfc.py 가 이미 CL env 로 주입하던 것과 동치 shim 을 Qt 쪽에도. (6) VRQt/CMakeLists.txt 의 OpenSSL link 경로 시프트. 새 OpenSSL 인스톨러가 libssl_static.lib / libcrypto_static.lib 를 lib/VC/x64/{MT,MTd}/ 에 두고 v1.x 의 libssl64MT.lib / libcrypto64MT.lib 네이밍과 lib/VC/static 경로를 깬다. build_mfc.py 의 %LOCALAPPDATA%/openssl-compat 패턴 (옛 이름으로 복사한 사본 폴더) 을 그대로 따라 CMakeLists 도 그 폴더가 있으면 우선, 없으면 옛 lib/VC/static 으로 fallback. 부트스트랩 스크립트는 build_mfc.py 의 docstring 참조 (한 줄). MFC + Qt + RPi + Ubuntu CLI + AppImage 빌드 모두 통과. 동작 영향: vr.conf 에 FHIR=1 추가 안 한 기존 deployment 는 sendweb 가 종전 JSON 모드 그대로 (HL7=1 만 분기보다 우선) — 0-impact migration. | |
| 1.18.43 | 2026-05-21 | 1.18.43: 멀티베드 console (PACU 10베드 Pi) syslog noise 80,500/hr → ~1,400/hr (-98.3%), cpu_usage 측정 1.16.12 (9f26e36) 이후 8년치 영구 -1 버그 수정, 멀티베드 환경 베드 식별용 [bedname / devicename] 로그 prefix 도입. 동기는 PACU production Pi (1.18.40) 4일치 syslog 분석 — 1.24M 라인 중 vr 라인 836K, idle 8시간 평균 시간당 80,500 라인 (베드 10 × 1초마다 "Waiting for next patient" × syslog 2x 중복). (1) VRApp.cpp:2696-2710 timer_thread_func 의 cpu usage 계산이 `if (totalcpu && m_cpu_total_last && totalcpu > m_cpu_total_last)` 가드 안에서만 m_cpu_total_last 갱신해 첫 호출 시 m_cpu_total_last=0 → else 분기 → -1 → m_cpu_total_last 갱신 안 됨 → 영원히 false → 영원히 -1 데드락. PACU 1,048 샘플 100% -1 실측 확인. m_cpu_total_last/m_cpu_used_last 갱신을 if 밖으로 빼서 첫 호출만 baseline 으로 -1, 두 번째 (60s 후) 부터 정상 측정. /proc/stat 의 iowait/irq/softirq 까지 7개 필드 fscanf 로 정확도 ±5% 개선. 부수: 같은 함수의 cpu_temp 가 `temp / 1000` int 나눗셈으로 소수점 절단되어 PACU 측정값이 정수 50/51/...,/58 9종만 출력 (millidegree 정밀도 손실) → `temp / 1000.0f` 로 fix. ram 계산은 `(totalram + totalswap) * mem_unit` 결과에 다시 mem_unit 곱하던 차원 혼용 (Linux 가 mem_unit=1 이라 우연히 정상 동작했지만 dimensionally wrong) → `(units1 + units2) * mem_unit` 로 정정 + underflow 가드. (2) DISPLAY::log(string/printf) + DEVICE::log(string/printf) 멤버 추가. DISPLAY::log 는 `[bedname] msg`, DEVICE::log 는 m_pdisp 위임 없이 직접 `[bedname / devicename] msg` 한 줄 prefix (nested `[bed] [dev]` 보다 가독성 우선). m_pdisp 또는 bedname null 케이스 safe fallback. (3) show_msg 의 console path TRACE 를 log() 로 교체 → "Waiting for next patient" 자동 [bedname] prefix. ADT patient id unset 도 log() 로. (4) 이미 수동으로 get_bedname() 을 인자로 넘기던 8군데 (DISPLAY ptcon_thread_func / add_trk / add_dev / del_dev / cut-recording, Device add_rec 콜백, FILT stop, VRApp add_tab/del_tab) 를 log() 로 전환 — 이중 prefix 방지 + 일관성. Serial::reading_thread / tcp_thread / open_socket waiting / timeout / interrupted / got-client 6군데 DEVICE::log() 전환 → `[bedname / Intellivue]` 등 자동 prefix, 멀티탭 환경에서 어느 베드 어느 device 출처인지 즉시 식별. (5) wss 재시도 폭주 throttle. WSSClient 에 m_verbose (debug step 로그 게이트) + m_dt_last_err_log (60s window) 추가. connect() 내부 step-by-step TRACE 4개 ("connecting" / "connect error" / "connected") 는 m_verbose 가드, 최종 "wss connect error" 는 60s window throttle (첫 줄 즉시, 그 다음 60s 1회). connect 성공 path 에서 throttle reset 으로 다음 disconnect → 첫 에러 즉시 로깅. sendweb_thread_func 의 "connecting websocket server" 호출자 라인도 같은 60s window, m_bDebug 면 verbose 활성화. PACU idle 8h 측정 7,024/hr (4종 × 1,756) → 약 60/hr (-99%). (6) "Waiting for next patient" / "ADT patient id unset" 의 60s throttle 자체는 feature/wav-mem-retention 브랜치 ptcon_thread_func 재구성 (b75928f 시리즈) 에서 이미 들어가 있어 이 릴리즈에 자동 포함. 종합 영향: idle 시간당 syslog 라인 80,500 → ~1,400 (-98.3%), active 시간당 14,900 → ~700 (-95%). 진단 측면: cpu_usage 가 부활하면서 웹모니터링 json 의 cpu_usage 필드도 한 번도 안 채워지던 게 정상 전송 (VRApp.cpp:2499 의 `if (m_cpu_usage > 0)` 가드가 영원히 false 였음). 별건: syslog 라인 2× 중복은 application 측이 아니라 rsyslog imuxsock + imjournal + journald.ForwardToSyslog=yes 의 Debian Bookworm 전형 misconfig — admin 측 fix (rsyslog.conf 에서 imjournal 주석 처리 + restart) 로 추가 50% 감소 가능 (별도 문서). MFC + Qt + Pi (RPI64) + Ubuntu CLI 빌드 모두 통과. | |
| 1.18.41 | 2026-05-13 | 1.18.41 Hotfix: TCP-server protocol-initiator device (Intellivue/S5/Datex/Hamilton/MPS/Agilia/IAP) 가 open() 직후 보내는 첫 패킷이 wire 에 안 나가던 회귀. 1.18.34 의 PortRouter refactor 가 Serial::open_socket() 의 8초 handoff blocking 을 제거하고 비동기 모델로 전환했는데, protocol-initiator 의 open() 코드가 m_sock=INVALID 상태에서 write 를 시도해 silent fail (Linux 는 m_fd=0 이라 false 반환). Intellivue 케이스 production journald 의 Pi → ESP TX 700K~3M bytes 는 monitor 측 abort 로 늦게 트리거된 AB_SPDU_SI recovery assoc_req 후 request_thread 의 정상 polling 트래픽이지만 일부 베드 (abort 없는 monitor) 는 recovery 도 안 되어 영구 silent (RX 300 bytes). 수정 방향: open() 의 옛 contract ("return 시점에 통신 가능") 복구. (1) Serial::open_socket() TCP-server 분기가 PortRouter::subscribe 후 m_sock_cv 로 첫 client handoff 까지 block (timeout = device read_timeout, 기본 30s) — 옛 모델 그대로. lock_and_open 이 false 면 다음 tick 에서 retry. close 신호로 abort 가능. (2) Serial::close() 는 PortRouter::unsubscribe 안 함 — subscribe 는 device 생애 동안 sticky 라 read_timeout 으로 인한 close→re-open 사이클 동안 listen socket 이 유지되어 1.18.29/1.18.34 의 anti-churn 이익 보존. unsubscribe 는 ~Serial() 에서만. (3) Per-device lifecycle thread (DEVICE::m_thread_restart): 각 device 가 자기 open/close/timeout 재시작을 별도 thread 에서 관리. 옛 모델의 DISPLAY::bed_manager 가 베드의 모든 device 를 sequential iterate 하던 구조에선 한 device 의 blocking open() 이 같은 베드의 다른 device 의 lifecycle 을 차단했는데, per-device thread 로 분리하여 멀티 device 베드도 병렬 진행. add_dev 에서 start_lifecycle, del_dev / ~DISPLAY 에서 stop_lifecycle. (4) Serial::tcp_thread_func 단순화 — outer m_sock_cv wait 패턴 (1.18.34/1.18.39 가 추가했던 것) 제거. open_socket 이 m_sock valid 보장하므로 reader thread 시작 시점에 simple inner recv loop 만. accept_handoff 의 replace (zombie cleanup) 케이스는 recv error 후 m_sock 변경 확인 + retry 로 처리. (5) reading_thread_func 의 `else if (m_sock != INVALID_SOCKET) tcp_thread_func();` 가드 복원 (1.18.39 가 제거). (6) Serial::write 의 silent-fail 케이스에 WARN trace 추가 — 미래 동종 회귀 즉시 노출. (7) PortRouter::subscribe 가 같은 (port, type) 에서 같은 IP filter 또는 둘 이상 catch-all 발견 시 reject (config 실수 원천 차단, 옛 WARN-only 동작 강화). (8) DISPLAY::bed_manager_thread_func 의 restart 블록 제거 — ptcon + adt 만 담당. "Waiting for next patient" TRACE 가 Linux/console 에서 1초 → 60초마다 throttle (별개 journald 부풀림 fix). 영향 범위: Intellivue / S5 / Datex / Hamilton / MPS / Agilia / IAP TCP-server 모드 베드 전부. 1.18.34 이후 우회 방법은 race-win 케이스 (VRN 이 PortRouter::subscribe 직후 connect) 외엔 없었음. MFC + Qt + Pi (RPI64) + Ubuntu Qt + AppImage 빌드 모두 통과. | |
| 1.18.40 | 2026-05-12 | Hotfix: Drager Medibus 프로토콜 사용 베드에서 1.15.11 이후 vr.service 가 SIGSEGV 로 무한 재시작하던 문제. 1.15.10 (a8c7b2a) 의 "Sampling rate estimation" 리팩터링이 m_ptrk_awp/awf/co2 트랙을 add_trks() 에서의 eager 생성 (default srate=62.5Hz) 에서 realtime data 100 샘플 모인 후 lazy 생성으로 바꿨는데, Medibus 핸드셰이크 순서 (0x51 ICC → 0x52 DeviceID → 0x53 RealtimeConfig 응답 → 0x54 → realtime data 시작) 상 0x53 응답 핸들러 (Drager.cpp:295-307) 가 트랙들이 아직 nullptr 인 시점에 `ptrk->m_srate = 1000000.0/interval` 로 NULL 역참조 → 즉시 SEGV. 그 베드의 vr.service 가 systemd Restart=on-failure 로 재시작 → 다시 connect → 동일 0x53 패킷 받고 동일 SEGV → 무한 루프 (journald 로그상 restart counter 61 까지 누적, code=killed status=11/SEGV). 1.15.9 까진 트랙이 add_trks() 에서 생성되어 0x53 도착 시 항상 non-NULL 이라 안전했음. 수정: (a) add_trks() 에서 m_ptrk_awp/awf/co2 eager 생성 복원 (srate=62.5Hz default) — 0x53 는 ptrk->m_srate 만 갱신 (트랙 재생성 X). (b) on_received() 의 lazy creation + packet-timing srate 추정 블록 제거 — 0x53 가 권위 있는 srate 소스이고 packet 타이밍 기반 추정은 network jitter 에 약해 불안정. (c) 0x53 핸들러에 `if (interval > 0)` guard 추가 — 페이로드 깨졌을 때 div-by-zero / inf srate 방지. 영향 범위: Drager Primus / Apollo / Perseus / Fabius 등 Medibus 사용 마취기 전부. 우회 방법은 1.15.9 다운그레이드 외엔 없었음 (그 베드의 모든 데이터 유실 + 환자 모니터링 불가). MFC + Qt + Pi (RPI64) + Ubuntu Qt + AppImage 빌드 모두 통과. | |
| 1.18.39 | 2026-05-08 | Fixed: PortRouter (TCP server) 모드에서 reader thread 가 startup 시점에 죽어 client 데이터를 영구히 못 받던 문제. 1.18.34 (9bd252f) 의 PortRouter refactor 가 m_sock 세팅을 dispatch 시점으로 옮겼는데 reading_thread_func (Serial.cpp:354) 의 가드 `else if (m_sock != INVALID_SOCKET) tcp_thread_func();` 는 옛 모델 (open 시점 이미 connect 완료) 가정 그대로라 — 시작 직후 m_sock=INVALID 면 tcp_thread_func 진입 안 하고 reader thread 즉시 종료. 이후 VRN 이 connect → PortRouter::dispatch → accept_handoff 가 m_sock 채우고 m_sock_cv.notify_all() → 그러나 wait 중인 reader 가 없음 (이미 죽음) → kernel recv buffer 에만 데이터 적재. 증상은 ss -tn state established 의 Recv-Q 가 0 이 아닌 값 (수백 bytes) 으로 누적, 베드 화면에는 데이터 안 들어옴. 그 베드의 VRN 이 Serial::open() 의 PortRouter::subscribe 직후~reader thread spawn 사이의 짧은 윈도우 안에 connect 한 경우만 우연히 정상 동작 (race-win). 수정: 가드 제거 — TCP 분기에 항상 진입. tcp_thread_func 자체에 이미 m_sock_cv outer-wait 루프가 있어 m_sock 이 INVALID 인 동안 dispatch 가 깨워줄 때까지 안전하게 대기. Pi 핫스팟에 VRN 여러 대 (e.g. 10.42.0.21~25) 를 같은 (4343, Philips:Intellivue) 키로 묶고 IP filter 로 라우팅하는 시나리오에서 가장 자주 노출. Added: 진단 로그 보강 — PortRouter::accept_loop 의 accept 이벤트, dispatch 의 라우팅 결정 (ip_specific/catch_all subs 수, 어느 dev 가 받았는지), subscribe/unsubscribe 의 entry 생성/append/destroy, reading_thread / tcp_thread 의 시작·wait·wake 라이프사이클 — 이전엔 모두 m_bDebug 게이트 안에 있어서 production journald 에서 안 보였는데, 이제 항상 출력 (이번 같은 dispatch routing 문제 진단을 production 에서 바로 가능). MFC + Qt + Pi (RPI64) + Ubuntu Qt + AppImage 빌드 모두 통과. | |
| 1.18.38 | 2026-05-08 | Fixed: 웹모니터링 우클릭 → 디바이스 세팅 (websocket edit_bed 명령) 으로 베드의 device/filter 를 변경할 때 베드 이름이 매번 "_2" suffix 로 자동 리네임되어 vr.conf (Pi: /data/vr.conf) 에 잘못된 이름으로 저장되던 문제. 원인은 1.18.22 (cda8aa5) 에서 add_bed 에 추가된 bedname dedup ("최후 방어선" 으로 forward_frame auto-create / 수동 편집된 conf 의 중복 이름 처리용). edit_bed 핸들러는 옛 pdel 이 m_pdisps 에 그대로 있는 상태에서 같은 bedname 으로 new DISPLAY 를 add_bed 하고 그 다음 del_bed(pdel) 하는 순서였는데, add_bed 가 dup 감지해 새 pdisp 를 bedname_2 로 자동 리네임 → save_settings 가 [BED/bedname_2] 로 직렬화 → 사용자 입장에선 "설정 적용 안됨" 으로 보였다. 추가로 옛/새 DISPLAY 가 잠깐 m_pdisps 에 공존하면서 같은 device 포트를 동시에 들고 있으려 해 로그가 dup 으로 찍히는 부수 증상도 있었음. 수정: del_bed(pdel) 를 add_bed(pdisp) 보다 앞으로 이동 — 옛 베드의 device 포트가 깨끗이 release 된 뒤 새 pdisp 가 같은 포트를 reopen, dedup 발동 안 함. 다른 베드의 recording 은 영향 없음 (인메모리 hot swap 설계 유지). 부수: edit_bed 핸들러의 데드 코드 (`break` 뒤의 unreachable olddevs populate 루프 + `if (olddevs.find(name) == olddevs.end())` 항상-true 래퍼) 정리. edit_conf 와 save_settings 는 손대지 않음. MFC + Qt + Pi (RPI64) 빌드 모두 통과. | |
| 1.18.37 | 2026-05-08 | Refactored: 필터 다이얼로그의 python 런타임 — pyvital → openvital 0.3.0 로 교체 + 동봉 python.zip 슬림화. 핵심 동기 셋: (1) pyvital 0.6.0 이 tensorflow+torch+keras 를 hard-dep 으로 끌어들여 PyPI upgrade 가 사실상 깨짐, (2) 기존 zip 121 MB 안에 botocore/aiobotocore/s3fs/pyarrow/pandas/aiohttp 등 vitaldb 클라우드 업로드용 deps 가 ~250 MB unpacked 차지 — 필터 서버는 한 번도 쓴 적 없는 dead weight, (3) sanic 25.x 의 multi-process worker 가 openvital __main__.py 를 fork 후 재import 하면서 worker 가 module-top app instance 를 못 찾아 "Sanic app name not found" 로 죽음 — 단순 endpoint 2개 로컬호스트 서버에 async stack 자체가 과함. 해결: openvital 0.3.0 (commit b6f445b) 가 sanic 을 stdlib http.server (BaseHTTPRequestHandler + HTTPServer) 로 50 LOC 재작성 — wire protocol (GET / 필터목록 JSON, POST /<modname> gzip 본문) 동일해 VR 클라이언트 변경 0줄. 단일 스레드로 기존 sanic event loop semantics (cfgs/default_cfgs lock-free 변경) 보존. build_python_zip.py (NEW) 가 Python 3.11.9 embed + numpy + openecg + openvital (local checkout 우선, fallback PyPI) 만으로 27 MB zip 빌드 — 기존 121 MB 대비 78% 감소. ML 필터 (sv_dlapco/abp_hpi/ecg_classifier/ecg_beat_noise_detector) 는 [Hybrid 모델] 사용자가 Add filter 다이얼로그의 "Install ML filters" 버튼 누를 때 silent pip install openvital[all] (cmd 창 미노출 — VRApp::install_pip_package + run_pip_silent 이 자체 progress 다이얼로그에 stdout heartbeat 흘림). 버튼 라벨은 "Upgrade pyvital" → "Install ML filters" 로 의도 명시. openvital __main__.py 가 missing extras 를 graceful skip 하므로 base 환경에서도 11개 필터 (ECG QRS detector, HRV, MTWA, annotator, eeg_fft, nirs_cox, pkpd_3comp, pleth_dpop/ptt/pvi, resp_compliance) 즉시 사용 가능. Migration: 기존 사용자의 user_dir/python 폴더는 pyvital + 옛 deps 가 그대로 남아있고 vitaldb.net/python.zip 만 새로 올리면 (a) 기존 폴더 삭제 후 install_python 으로 새 zip 받으면 깔끔, (b) 폴더 유지 시 server 가 openvital 못 찾아 필터 다이얼로그 에러 → release note 에 명시 필요. MFC + Qt 양쪽 코드: VRGui.rc 의 IDC_UPGRADE 버튼 라벨 + DlgAddFilter.cpp 의 dist-info prefix 매칭 (pyvital- 8자 → openvital- 10자) + QtDlgAddFilter.cpp 도 동일 변경 + onUpgrade 가 cmd /k QProcess::startDetached 대신 theApp.install_pip_package("openvital[all]") 호출 (Linux Qt 빌드는 #ifdef _WIN32 가드). VRApp.cpp:318 의 python -m pyvital → -m openvital. install_pip_package 는 anonymous namespace 의 is_safe_pkg_spec 화이트리스트 검증 + run_pip_silent (CreateProcessA + CREATE_NO_WINDOW + 자식 stdout pipe) + CDlgProgress / QtDlgProgress 자체 progress UI. 빌드 검증: python -m openvital 이 GET / 로 11 필터 반환, sanic import 부재 확인. zip 27.5 MB. | |
| 1.18.36 | 2026-05-07 | Fixed: 3-파티션 레이아웃 (boot/rootfs/data) 에서 websocket edit_conf / update 명령이 read-only fs 로 실패하던 문제. vr.conf 는 /data 폴더가 있으면 /data/vr.conf 로 자동 정착 — get_conf_path() 가 write target 으로 결정, load_settings() 는 primary 가 없으면 legacy /boot/firmware/vr.conf 또는 /boot/vr.conf 에서 fallback read 후 다음 save 부터 primary 로 자동 이전 (마이그레이션 step 없음, legacy 파일은 read-only fs 라 무해). binary update (upgrade_now) 는 RAII BootRwGuard 가 dirname(module_path) 를 잠시 rw remount, scope 종료 시 sync() + MS_REMOUNT|MS_RDONLY 로 자동 ro 복귀 — vfat 노출 윈도우 최소화 + exception/return 어디서든 복구 보장. statvfs ST_RDONLY 검사로 이미 rw 인 fs (x86 dev) 에서는 no-op, non-Linux (macOS) 는 stub 으로 통과. save_settings() 의 silent EROFS 실패가 TRACE 로 가시화 (이전엔 무시되어 conf 수정 안 먹는 원인 추적 불가). 부수: PortRouter.cpp 가 commit 9bd252f 이후 CMakeLists 의 RPI sources 목록에서 누락되어 Pi 빌드 link 에러 — 추가. | |
| 1.18.35 | 2026-05-01 | Added: alarm parsing for Mindray HL7 (eGateway ORU^R40) and Nihon Kohden (NealTime HL7GW + ORF, BSM serial D-port, EGA UDP) - emits ALARM_STATUS / ALARM_MESSAGE / ALARM_PRIORITY tracks (same 3-track schema already used by Philips IntelliVue, GE Solar/Dash, GE/Datex S5); type info preserved via [T]/[A]/[ARR] message prefixes; Mindray facet 1/5/6/7 walker with 92-entry MDC+MNDRY alarm code lookup; NK HL7GW parses OBR-4=EVENT with arrhythmia/technical disambiguation by OBX-5; NK BSM serial decodes per-parameter * flags (edge-triggered to avoid 1Hz flooding) plus 32-bit Table 9 arrhythmia bitmap, D19 alarm-suspend nibble, and the VPC CODE byte; NK EGA UDP extracts Priority + Tech Alarm + Arrhythmia from JSON Vital Sign packets with edge-trigger cache for start/end inference; ACK^R40 reply added for Mindray alert messages; simulators gained periodic alarm cycling for end-to-end smoke testing | |
| 1.18.34 | 2026-04-27 | Added: Linux Desktop Qt GUI build (VRQt) - first GUI build for Linux distributed as a self-contained AppImage (~32MB, Qt6 runtime + libGL/libEGL/libGLX/libOpenGL/libGLESv2/libGLdispatch bundled inside) so users on minimal Ubuntu/Debian/Fedora can run it without installing Qt or GL system packages; tested on AWS clean Ubuntu 22.04 cloud image; build via build_ubuntu_appimage.py which is now invoked by deploy.py; Fixed: Linux Qt right-side device/filter panels rendered with white background below cards because the inner listWidget did not autoFillBackground from parent palette on Linux Qt (Windows/macOS implicitly inherit, X11/Wayland do not); explicit stylesheet on listWidget; Added: Linux installation section in user manuals (EN/KO/ZH/ES) covering AppImage chmod+run flow and dialout group note for serial/USB device access | |
| 1.18.33 | 2026-04-25 | Fixed: Demo device never started since 1.18.11 (bed_manager skipped empty port_name as passive but Demo has no port and still needs lock_and_open); Fixed: Qt fit-to-window button rendered at top-left (0,0) instead of right-aligned in bottom nav bar; Fixed: Qt crash when closing tab / deleting device / deleting filter - paint dereferenced freed pointer before panel timer rebuilt items (added MFC-style stale-pointer guard to QtDeviceItem and QtFilterItem); Added: macOS DMG main window now shown on launch (restoreWindowPos had no Mac/Linux branch); Added: macOS Homebrew dylibs (openssl) bundled in Contents/Frameworks; Added: macOS .app bundle now notarized + stapled (drag-to-Applications no longer needs xattr -cr on first launch); Changed: Qt MSI ~1MB smaller (dropped unused iconengines and imageformats plugins) | |
| 1.18.32 | 2026-04-23 | Fixed: same-port multi-client TCP - primary now runs a continuous accept loop and hands off each connection to the matching sub by IP filter (previously second connection got rejected/listened closed); Added: @IP and #keyword port filters are now mutually exclusive per bed (IP wins, keyword dropped with WARN); Added: WARN log when two beds on the same port share an identical IP filter; Added: catch-all primary keeps its single connection and rejects extras (preserves broadcast mode) | |
| 1.18.31 | 2026-04-22 | Added: drug slot shows DOSE_AMOUNT (mg etc) in preference to VOL (mL) when available; Added: DOSE_RATE / DOSE_AMOUNT montypes bound to BBraun tracks for monitor view; Added: HL7 MDC_DOSE_DRUG_DELIV_TOTAL unit picked up dynamically from UCUM | |
| 1.18.30 | 2026-04-21 | Changed: monitor view pump layout - 8 slots with CE or RATE (CE-less pumps show infused volume as small annotation); Reverted: BBraun minimal=1 (1.18.29) - was dropping data from file, monitor view redesign handles display clutter instead | |
| 1.18.29 | 2026-04-21 | Fixed: BBraun multi-rack auto-tab data drop, primary TCP churn, save_settings lock race, TCP reader spin on peer close; Added: BBraun minimal=1 option (display only drug name / rate / volume) | |
| 1.18.28 | 2026-04-20 | Fixed: vr.conf on Pi/Linux now looked up next to executable (supports /boot/firmware); deploy.py --store auto-submits MSIX to Microsoft Store | |
| 1.18.27 | 2026-04-18 | Fixed: vital file periodic flush (5000s→5s bug), Fit-to-Window icon redesigned to match navigation button style | |
| 1.18.26 | 2026-04-18 | Fixed: periodic .vital file flush interval was 5000 seconds instead of 5 (unit-bug since comment said seconds but value was milliseconds). Data now actually reaches disk every 5 s during recording instead of only on close. | |
| 1.18.25 | 2026-04-18 | Fixed: BBraun HL7 rec_timeout raised 10s→60s to prevent primary churn during DoseLink R42→R01 phase transition and long idle periods | |
| 1.18.24 | 2026-04-18 | Fixed: GUI silently exits when another recorder running (now falls back to viewer mode), .vital file association written to HKCU (no admin required, auto-updates exe path on every run) | |
| 1.18.23 | 2026-04-18 | Fixed: atof decimal-separator bug on non-English Windows locales (rates <1.0 recorded as 0 in Norwegian/German/French etc.), BBraun HL7 pump limit raised from 8 to 16 | |
| 1.18.22 | 2026-04-18 | Fixed: multi-bed TCP port bind race on restart (Mindray/BBraun HL7 auto-reconnect without manual Add device) | |
| 1.18.21 | 2026-04-18 | Fixed: port name filter (#keyword@ip) lost on save, BBraun HL7 VMD-block parsing with single dt, Fit-to-Window button sizing (MFC) and added to Qt | |
| 1.18.20 | 2026-04-15 | BBraun: comprehensive protocol parsing (dose rate, infusion time, syringe, weight, drug amounts, delivery status) | |
| 1.18.19 | 2026-04-13 | Fixed: BBraun HL7 multi-pump identification (rack serial filtering), multi-bed forwarding, port filter parsing | |
| 1.18.18 | 2026-04-10 | Added: GE alarm limits, S5 alarm/ext3/ext1 parsing | |
| 1.18.17 | 2026-04-04 | Changed: TRACE debug-only guard, show_msg for file errors, deploy sequential builds | |
| 1.18.16 | 2026-04-04 | Added: TCP port filtering (#keyword@ip), frame forwarding for multi-bed. Fixed: --debug conf file mistaken as vital file, device type ambiguity in forwarding | |
| 1.18.15 | 2026-04-04 | Changed: Port to Port Name, keyword filter AND/OR (#kw1 kw2#kw3), IP postfix matching (@10.1), MEKICS SerialFramed | |
| 1.18.14 | 2026-04-03 | Added: TCP port filtering (#keyword@ip), Fixed: devtypes not sent on boot | |
| 1.18.13 | 2026-04-02 | Changed: Covidien company name to Medtronic (BIS, INVOS) | |
| 1.18.12 | 2026-04-02 | Bug Fixed: NihonKohden EGA SpO2 waveform not displayed (channel name mapping) | |
| 1.18.11 | 2026-03-31 | Added: Mindray HL7 multi-bed separation via PV1, auto-tab creation for EGA/Mindray | |
| 1.18.10 | 2026-03-31 | Added: HL7 ZBR segment now includes ver, os, arch, dgmt | |
| 1.18.8 | 2026-03-25 | Added: --demo mode for console operation with demo signals | |
| 1.18.7 | 2026-03-25 | Added: --console mode for headless operation on Windows | |
| 1.18.6 | 2026-03-25 | Added: Mindray eGateway HL7 protocol (IHE PCD-01, HL7 v2.6 over MLLP, waveform + numeric + infusion pump) | |
| 1.18.4 | 2026-03-24 | Added: BBraun SpaceCom HL7 protocol (HL7 v2.6 over MLLP, TCP server mode) | |
| 1.18.3 | 2026-03-24 | Added: HL7 OBX-14 per-track dtstart, ZBR segment with dtcase/dtapp timestamps | |
| 1.18.2 | 2026-03-24 | Added: HL7 ZBR segment with dtcase (recording start) and dtapp (app start) timestamps | |
| 1.18.1 | 2026-03-23 | Fixed: HL7 OBX-3 format (code^device/track@srate), OBX-7 refrange (min^max), NA null sample gaps | |
| 1.17.6 | 2026-03-22 | Bug fixed: Resource ID conflicts WiX installer cleanup | |
| 1.17.5 | 2026-03-21 | Bug fixed: Vital packed file loading MFC device button icon padding | |
| 1.17.4 | 2026-03-19 | Bug fixed: NirsitON EEG 250Hz VRQt device minimize/rename | |
| 1.17.3 | 2026-03-18 | Bug fixed: BBraun protocol NirsitON protocol VRQt | |
| 1.17.2 | 2026-03-14 | Added: NirsitOn NDI protocol support. | |
| 1.17.1 | 2026-03-12 | Added: VRQt | |
| 1.16.13 | 2026-03-07 | Added: New Setup File with VRQt | |
| 1.16.12 | 2026-03-06 | Added: Vector Type Icons | |
| 1.16.10 | 2026-03-02 | Bug fixed: Autodetecting BBraun Stuffing feature. | |
| 1.16.8 | 2026-02-27 | Bug fixed: Masimo IAP protocol. | |
| 1.16.6 | 2026-02-13 | Added: Servo-u ventilator protocol | |
| 1.16.5 | 2026-02-09 | Bug Fixed: NihonKohden HL7GW Pleth waveform gain correction. | |
| 1.16.4 | 2026-01-20 | Bug Fixed: Serial communication. | |
| 1.16.3 | 2026-01-14 | Bug Fixed: Masimo IAP Protocol. | |
| 1.16.2 | 2026-01-03 | Added: BLE Support for Movesense devices on Raspberry Pi | |
| 1.16.1 | 2026-01-01 | Added: BLE Support for Movesense devices on Raspberry Pi and Linux versions. | |
| 1.16.0 | 2025-12-31 | Added: BLE Support for Movesense devices | |
| 1.15.16 | 2025-09-18 | Bug Fixed: Restoring last window position. | |
| 1.15.15 | 2025-09-05 | Added: CPU optimization Bug fixed: Masimo IAP protocol. | |
| 1.15.14 | 2025-09-04 | Added: CPU optimization | |
| 1.15.13 | 2025-09-04 | Bug Fixed: Masimo IAP Protocol. | |
| 1.15.12 | 2025-09-04 | Bug Fixed: Hardware handshake for Link+ connection. | |
| 1.15.11 | 2025-09-01 | Added: Recording alarm messages for Drager anesthesia machines | |
| 1.15.10 | 2025-07-14 | Bug Fixed: Sampling rate estimation in Medibus protocol. | |
| 1.15.9 | 2025-06-03 | Bug Fixed: EEG waveform labels from Philips monitors. | |
| 1.15.8 | 2025-05-26 | Added: EM_UPLOAD option to upload vital files to server immediately | |
| 1.15.7 | 2025-05-26 | Bug Fixed: Websocket connection error | |
| 1.15.6 | 2025-05-19 | Bug Fixed: S5 checksum error | |
| 1.15.5 | 2025-04-17 | Bug Fixed: saving the raw data | |
| 1.15.4 | 2025-04-15 | Added: Recording alarms from Philips monitor | |
| 1.15.3 | 2025-04-14 | Bug fixed: saving the raw data | |
| 1.15.2 | 2025-03-12 | Fixed: bug in opening file | |
| 1.15.1 | 2025-01-30 | Supporting for Fresenius Kabi Conox, Minor Bug fixes | |
| 1.14.13 | 2025-01-18 | Fixed bug in handling non-ascii filepath | |
| 1.14.12 | 2024-11-20 | Fixed: bug in 64bit ARM Architecture | |
| 1.14.11 | 2024-10-24 | Added: feature for updating to a specific version | |
| 1.14.10 | 2024-10-22 | Fixed: bug in json streamming | |
| 1.14.9 | 2024-10-17 | Added: montype for drug rates |
| Version | Date | Changes | |
|---|---|---|---|
| 1.19.9 | 2026-08-14 | 1.19.9 Fixed: 큰 녹화 파일(약 100MB 이상)을 서버로 올릴 때 프로그램이 메모리 부족으로 강제 종료되고, 재시작 후 같은 파일을 다시 시도하다 무한 반복되던 문제 — 업로드 본문을 메모리에 통째로 만들지 않고 디스크에서 흘려보내도록 변경(100MB 파일 기준 최대 메모리 208MB → 14MB). 큰 파일이 한 개라도 쌓이면 그 장비의 이후 업로드가 전부 막히던 증상이 사라진다. 1.19.8 Fixed: 자동 파일 자르기(CUT_FILE) 를 쓰는 베드에서 환자가 계속 연결돼 있는데도 녹화 파일이 PT_WAITING_TIME(기본 5분) 마다 잘리던 문제 — 웹 모니터링 전송 후 메모리를 비우는 과정에서 환자 연결 판정에 쓰이는 최근 구간까지 지워져 "환자가 나갔다" 로 오판하던 것. 이제 판정에 필요한 구간은 보존한다. 파일이 5분마다 끊기고 그때마다 수 초가 유실되던 증상이 사라진다. 1.19.7 Added: GE Corometrics(분만 태아감시) 모체 vitals 지원 — 기존엔 태아 심박(HR1/HR2)·자궁수축(UACT)만 기록됐으나 이제 같은 장비가 함께 보내는 모체 SpO2/맥박(PR)/NIBP(수축·이완·평균)도 한 파일에 기록. Fixed: 프로세스 종료(서비스 매니저의 정상 종료 요청, SIGTERM) 시 즉시 종료되지 않고 강제 종료되던 문제 — 이제 SIGTERM 을 받아 녹화 버퍼를 flush 하고 스레드를 정리한 뒤 정상 종료. 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.8 | 2026-08-14 | 1.19.8 Fixed: 자동 파일 자르기(CUT_FILE) 를 쓰는 베드에서 환자가 계속 연결돼 있는데도 녹화 파일이 PT_WAITING_TIME(기본 5분) 마다 잘리던 문제 — 웹 모니터링 전송 후 메모리를 비우는 과정에서 환자 연결 판정에 쓰이는 최근 구간까지 지워져 "환자가 나갔다" 로 오판하던 것. 이제 판정에 필요한 구간은 보존한다. 파일이 5분마다 끊기고 그때마다 수 초가 유실되던 증상이 사라진다. 1.19.7 Added: GE Corometrics(분만 태아감시) 모체 vitals 지원 — 기존엔 태아 심박(HR1/HR2)·자궁수축(UACT)만 기록됐으나 이제 같은 장비가 함께 보내는 모체 SpO2/맥박(PR)/NIBP(수축·이완·평균)도 한 파일에 기록. Fixed: 프로세스 종료(서비스 매니저의 정상 종료 요청, SIGTERM) 시 즉시 종료되지 않고 강제 종료되던 문제 — 이제 SIGTERM 을 받아 녹화 버퍼를 flush 하고 스레드를 정리한 뒤 정상 종료. 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.7 | 2026-08-12 | 1.19.7 Added: GE Corometrics(분만 태아감시) 모체 vitals 지원 — 기존엔 태아 심박(HR1/HR2)·자궁수축(UACT)만 기록됐으나 이제 같은 장비가 함께 보내는 모체 SpO2/맥박(PR)/NIBP(수축·이완·평균)도 한 파일에 기록. Fixed: 프로세스 종료(서비스 매니저의 정상 종료 요청, SIGTERM) 시 즉시 종료되지 않고 강제 종료되던 문제 — 이제 SIGTERM 을 받아 녹화 버퍼를 flush 하고 스레드를 정리한 뒤 정상 종료. 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.5 | 2026-08-06 | 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.4 | 2026-08-05 | 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.3 | 2026-08-04 | 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.0 | 2026-08-03 | 1.19.0 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스 전체가 SIGSEGV 로 죽던 문제. accept_handoff 가 교체(replace) 시 옛 소켓을 즉시 closesocket 했는데, m_sock_mtx 는 fd 번호만 보호할 뿐 fd 의 수명은 보호하지 않아 reader 가 그 fd 로 recv 중일 수 있었다. close 는 blocking recv 를 깨우지도 않고 fd 번호를 즉시 재사용 가능하게 만들어, 다음 접속이 같은 번호를 받으면 reader 가 남의 세션을 읽었다. SNUH 회복실(VRN 15대, 최다 베드는 하루 785회 재접속)에서 실제로 크래시 후 9시간 48분 전 베드 무수집. 수정 세 겹: (1) tcp reader 를 poll(POLLIN,200ms) 게이트 뒤의 recv 로 재구성 - recv 가 무기한 블로킹되지 않으므로 close/replace 가 늦어도 200ms 안에, shutdown 이 동반되면 즉시 감지된다. fd 에 O_NONBLOCK 은 걸지 않는다(같은 fd 로 송신하는 write_transport 의 부분 전송 방지). (2) replace 는 closesocket 대신 shutdown 만 하고 옛 fd 를 retired 목록에 넣으며, 닫기는 깨어난 reader 또는 Serial::close 가 정확히 한 번 수행 - fd 번호 재사용 창이 사라진다. (3) Serial::close 가 join 전에 shutdown 으로 reader 를 깨우고(RCVTIMEO 는 설정된 적이 없어 종전에는 피어가 끊을 때까지 join 이 매달렸다), m_sock 접근을 전부 락으로 감싼다. has_socket 의 무락 읽기와 udp_thread_func 의 이중 close 도 같은 계열이라 함께 수정. Added: AUTO_DETECT=1 (vr.conf) - 시리얼 포트에 무엇이 물려 있는지 스스로 알아내 즉시 수집을 시작한다. 판별용 프로브 바이트를 따로 두지 않고 후보 드라이버를 실제로 열어 유효 레코드 도달 여부로 판정하므로 장비별 판별 코드가 없고 드라이버는 무수정이다. 4단계: 회선 측정(커널 프레이밍/패리티/브레이크 카운터로 보레이트와 프레임 폭 확정) -> 패리티 추론(10비트 프레임의 7E1/7O1 대 8N1 모호성을 MSB 상관으로 해소) -> 후보 필터(보레이트+프레임폭+데이터비트 3축 일치, 베드당 환자모니터 1대/마취기 1대 배타, 포트 위치 사전확률, 지난 판별 결과 캐시) -> 남으면 트랙 수확량으로 경쟁. SNUH 수술장 9방 + ICU 3베드 실측에서 Intellivue/MedibusX/Datex-Ohmeda/Primus 를 오탐 0 으로 판별했고, 표준과 다른 포트에 꽂힌 장비(P3/P4 의 Intellivue)도 찾아냈다. AUTO_DETECT 가 없으면 스캐너는 유휴 대기만 하므로 기존 설정에 영향이 없다. | |
| 1.18.52 | 2026-07-31 | 1.18.52 Changed: VR 기본 운영이 k3s 로 이관됨에 따라 서버→VR 하향 제어 경로를 제거. VitalServer 로는 데이터를 일방 push 만 하고, websocket(socket.io) 으로 내려오던 원격 명령 update/restart/reboot/edit_conf/new_bed/del_bed/edit_bed/dt/add_event 를 더 이상 처리하지 않는다 (원격 재부팅·바이너리 교체·conf 덮어쓰기·베드 구성변경 봉쇄). 구현: sendweb_thread_func 의 socket.io 42 이벤트 dispatch 와 on_sio_msg 함수를 통째로 제거. engine.io ping(2->3) 및 ws 레벨 ping/pong 은 연결 유지를 위해 그대로 응답하므로 데이터 업링크·웹모니터링은 종전과 동일. 서버는 VR 버전을 알므로 52+ 로는 명령을 보내지 않고, 받아도 무시. Fixed: --console 모드 (MFC) 에서 stdout 이 이미 유효한 파이프/파일 (> log.txt, ssh 파이프, cmd 상속 핸들) 인데도 무조건 _open_osfhandle+_dup2 로 재배선하면서 그 OS 핸들을 닫아버려, 초기화 이후 모든 로그가 조용히 사라지던 문제 (콘솔 모드 서버 배포에서 진단 불능의 원인, 1.18.7 부터 잠재). stdout 핸들이 유효하면 (GetFileType != UNKNOWN) 재배선하지 않고, 핸들이 없을 때만 (Explorer/schtasks 실행) AttachConsole 로 부모 콘솔에 붙인다. 실측: 종전 초기화 후 로그 침묵 -> 수정 후 지속 출력. (앞서 1.18.51 의 VRQt qDebug->stdout 통일과 함께 세 빌드의 --debug 로그 일관성 완성.) | |
| 1.18.51 | 2026-07-30 | 1.18.51 Fixed: VRQt (Qt 빌드) 에서 --debug 옵션을 켜도 콘솔에 로그가 전혀 출력되지 않던 문제. 원인: Qt 빌드의 TRACE 가 qDebug() 로 매핑되어 있었는데 (UTIL.h, 1.14.4 부터), Windows 에서 Qt 로깅은 GUI 앱이면 콘솔/파이프가 아니라 디버거 (OutputDebugString) 로만 보내므로 VRQt 만 --debug 콘솔 출력이 구조적으로 불가능했다 (MFC 는 stdafx.h 의 #undef TRACE 로 printf 버전을 사용해 정상). 수정: TRACE 를 세 빌드 (MFC/Qt/CLI) 공통 fprintf(stdout)+fflush 로 통일 - qDebug 직접 사용처는 코드에 없음을 확인. 이제 --debug 동작과 리다이렉트 (> log.txt) 가 전 빌드에서 일관되고, cmd 대화형 실행 시에는 기존 AttachConsole/AllocConsole 로직 (1.18.30) 이 콘솔을 만들어 준다. terapc 실측 검증: 종전 VRQt stdout 0 chars → 수정 후 4,690 chars 정상 출력, Vital.exe (MFC) 는 종전과 동일 정상. 추가 방어: --debug 의 콘솔 확보 검사에 GetFileType 추가 - 일부 런처/셸이 non-NULL 무효 stdout 핸들을 상속시키면 NULL 검사만으로는 AttachConsole 을 건너뛰어 로그가 사라질 수 있던 엣지 케이스 (MFC/Qt 공통). | |
| 1.18.50 | 2026-07-30 | 1.18.50 Fixed: 장비 연결이 한번 끊어지면 다시 붙지 않던 문제 (Link+ 현장 리포트, Intellivue 늦은 전원-온과 공통 근본 원인). 근본 원인: DEVICE::open() 의 초기화 호출 set_rx_time(0) 이 값 0 인데도 OPENING→ACTIVE 로 승격시키고 m_dtStatus 까지 0 으로 만들어, (1) 모든 장비가 open 즉시 가짜-ACTIVE (OPENING 상태가 사실상 부재), (2) restart 워치독의 dtlast(rx ?: status_time) 가 0 → read_timeout 조건이 영구 거짓 → 재연결 시 상대 무응답이면 (Link+ rack 재부팅/세션 잔존, 세션 프레임 1회 전송 후 무응답) 재시도 없이 영구 고착. 1.18.35 이하의 rec_timeout (상태 무관 60s 강제 재시작) 이 이 잠재 버그를 우연히 가리고 있었고 1.18.36 전환으로 노출된 것. 수정 3건: (1) set_rx_time 은 실수신 (dt>0) 일 때만 ACTIVE 승격 - 상태 표시도 정직해짐 (open 후 첫 수신까지 "con"), (2) restart 워치독에 OPENING-stuck 분기 추가 - open 후 read_timeout 내 첫 수신이 없으면 재시작해 협상 프레임 재전송, tx_time>0 (protocol-initiator) 조건으로 passive/keyword-sub 디바이스는 제외해 유휴 churn 없음, (3) tx_time 을 모든 transport (socket/fd/COM) 에서 갱신 (기존 Windows COM 만). 영향 장비: Link+/Agilia, Intellivue, S5, Datex, Hamilton 등 open 시 세션/assoc 을 시작하는 모든 protocol-initiator. E2E 검증: 무응답 서버 재현에서 종전 접속 1회 후 영구 침묵 → 수정 후 30초마다 재접속 반복, 장비 복구 시 자동 연결. Mindray 멀티베드 회귀 테스트 PASS (ACK 75/75, tier1 라우팅, probe 거절, stuck 오발동 0). Intellivue 1.18.48 assoc-retry (5s) 는 유지 - 이번 워치독은 30s 백스톱으로 공존. | |
| 1.18.49 | 2026-07-29 | 1.18.49 Fixed: (1) Mindray eGateway HL7 멀티베드 (catch-all + #키워드 sub) 에서 전 베드가 readonly=1 이면 데이터가 전혀 수집되지 않고 게이트웨이가 수초마다 재접속하던 문제. 원인 셋 - (a) readonly catch-all 이 open_socket 에서 조기 반환해 reader/parser 스레드가 아예 생성되지 않음 (아무도 안 읽는 소켓에 데이터가 쌓이다 게이트웨이가 연결 재수립 반복; PortRouter 리팩토링 시 readonly 체크가 blocking accept 앞으로 이동하며 생긴 1.18.36 회귀 - 1.18.31 은 accept 가 먼저라 우연히 동작), (b) readonly 가 HL7 ACK 송신까지 차단 (eGateway 는 메시지마다 ACK 요구), (c) sibling 베드 메시지는 broadcast 후 ACK 없이 return. 수정 - readonly 는 송신만 차단하고 수신은 정상화 (broadcast-only keyword sub 만 소켓 미보유), write_protocol 경로 신설로 ACK 는 readonly 우회, catch-all 이 sibling 메시지도 대신 ACK (R01/R40 타입 구분), broadcast 수신 sub 의 rx_time 갱신으로 상태표시 정상화, PortRouter takeover 는 기존 소켓이 stale (10초+ 무수신) 일 때만 허용해 게이트웨이 health-probe 접속이 live 데이터 소켓을 뺏지 못하게 함. 시뮬레이터 E2E 검증: ORU 75건 -> ACK 75건 (100%), probe 5회 전부 무해 거절, SU-2/SU-4 tier1 라우팅 정상. (2) Getinge/MAQUET Flow-i (FCI 프로토콜) tidal volume 이 실제의 절반으로 기록되던 문제. 원인: FCI 프로토콜 v0005 부터 TV 채널 (101/102) gain 이 0.1 -> 0.2 로 변경됐는데 (Flow-i communication interface v0007 문서), VR 은 open 시 SPVE0005 로 v5 를 요청하면서 파싱은 구버전 gain 0.1 을 적용. 병원 vital 파일 실측 (Flow-i 직결 TV 201.4/188.2 vs 필립스 경유 403.0/376.6, MV 는 정상) 으로 검증 - TV_INSP/TV_EXP gain 0.2 적용. MV 는 v3+ 스케일 (0.01) 이 이미 반영돼 있어 변경 없음. (3) Flow-i 설정 파라미터 미지원 (m_ch_settings 빈 맵) - v5+ 에 존재하는 수치 설정 채널 11종 등록: SET_RR(411), SET_INSP_TM(413), SET_IE(418), SET_MV(421), SET_PC(423), SET_PEEP(424), SET_FRESH_FLOW(428), SET_FRESH_O2(430), SET_AGENT_PERC(434), SET_PS(436), SET_SIMV_RR(437). gain 은 문서 표기 그대로. 주의: Set Tidal Volume (채널 422) 은 FCI v5 에서 프로토콜 자체에서 삭제되어 직접 수신 불가 - 볼륨 모드에서 SET_MV/SET_RR 로 파생 가능. 설정 채널은 문서 기반 구현으로 실기 검증 대기 상태. | |
| 1.18.48 | 2026-07-24 | 1.18.48 Fixed: VR 이 먼저 실행된 상태에서 Philips Intellivue 모니터를 나중에 켜면 웨이브가 영원히 수집되지 않던 문제 (VR 재시작 전까지 모니터로 아무 요청도 안 나감). 원인: Intellivue::open() 은 assoc_req 를 딱 한 번 보내는데, 시리얼(MIB)/UDP 는 모니터가 꺼져 있어도 포트가 열리므로 이 요청이 응답 없이 사라진다. Philips 는 자기가 먼저 말을 걸지 않는 프로토콜이고, 첫 수신이 있어야 디바이스 상태가 OPENING→ACTIVE 로 승격되는데 수신이 없으니 OPENING 에 고정 - DEVICE::restart_thread_func 워치독은 is_closed(재오픈)/is_active(read_timeout 30s) 두 분기만 있어 OPENING 은 어느 쪽에도 안 걸림 → assoc 재전송도 재시작도 영원히 없음. 수정: Intellivue 에 assoc 재시도 스레드 추가 - 세션 확립(MDS_CREAT 수신, m_bSessionUp atomic 플래그) 전까지 5초 간격으로 assoc_req 재전송, 최근 5초 내 수신이 있으면(assoc 진행 중) 재전송 보류. 모니터를 나중에 켜도 최대 5초 내 연결. 세션 확립 후에는 재시도 루프 종료 - 기존 동작(AB_SPDU 시 재연결, read_timeout 재시작)과 충돌 없음. 모니터가 켜져 있던 기존 시나리오는 첫 assoc_req 에 바로 응답하므로 동작 변화 없음 - 0-impact. | |
| 1.18.47 | 2026-07-24 | 1.18.47 Fixed: 파일 업로드(및 Http 클래스를 쓰는 모든 경로 - upload_vital, getvr 업데이트체크, ADT 조회, 필터서버)가 SERVER_IP/UPLOAD_SERVER_IP 에 "https://host:port" 풀 URL 형식을 넣으면 "Couldn't resolve host name" 으로 전부 실패하던 문제. 원인은 src/Https.cpp 의 Http 생성자 - 1.18.46 의 wss.cpp 수정과 동일한 버그의 나머지 반쪽. 첫 콜론을 포트 구분자로 오인해 "https://eicuvital.ywmc.or.kr:4432" 입력 시 m_strHost 가 문자 그대로 "https" 가 되어 DNS 조회 실패 (웹모니터링 wss 는 1.18.46 에서 이미 수정되어 정상, 파일 업로드만 실패하는 비대칭 발생 - 현장에서는 업로드 루프의 실패시 break 정책과 겹쳐 "베드명에 # 가 있으면 전송이 안 된다" 로 오인 관찰됨. 실제로는 # 파일이 디렉토리 순서상 항상 첫 시도라 거기서 host 오류로 중단 → 뒤 파일 전체가 막힌 것. 베드명 특수문자는 업로드 URL 과 무관). 수정: Http 생성자에 wss 와 동일한 선행 스킴(https/http) 파싱 추가 - 스킴이 TLS 여부를 결정 (포트 무관, 4432 같은 커스텀 포트에서 https 정상), "https://host" 포트 생략 시 443, 경로 붙은 입력도 host 만 추출. 기존 "host" / "host:443" 형식은 종전과 완전 동일 동작 - 0-impact. Windows(WinINet)/Linux(curl) 공통 생성자라 모든 플랫폼에 적용. | |
| 1.18.46 | 2026-07-23 | 1.18.46 Fixed: WSSClient 가 "https/wss 스킴 + 비표준 포트" 조합을 제대로 처리하지 못하던 문제 (커스텀 포트의 wss 서버에 연결 불가). 원인은 src/wss.cpp 의 WSSClient 생성자 두 곳 - (1) SERVER_IP 파싱이 첫 콜론을 포트 구분자로 써서 "https://host:4432" 를 넣으면 "https" 의 콜론을 먼저 잡아 m_host 가 "https" 가 되고 포트 파싱이 깨져 getaddrinfo 실패, (2) m_secure = (m_port == 443) 로 TLS 여부를 오직 포트 443 여부로만 판단해 4432 같은 커스텀 포트는 무조건 평문 ws 로 붙어 wss 전용 서버에서 실패. 수정: 생성자가 선행 URL 스킴(https/wss/http/ws)을 먼저 파싱해 스킴이 있으면 스킴이 TLS 를 결정(포트 무관)하고, 스킴이 없으면 기존 포트 443 휴리스틱을 유지(하위호환). 이제 SERVER_IP=https://eicuvital.ywmc.or.kr:4432 (또는 wss://...) 형식이 비표준 포트에서도 wss 로 정상 연결되며, SNI/인증서 검증은 도메인(m_host)을 쓰므로 와일드카드 인증서도 정상 매칭. 기존 host / host:443 설정은 종전대로 secure 유지 - 0-impact. | |
| 1.18.45 | 2026-06-16 | 1.18.45 Fixed: Windows VR 에서 사용자가 외부에서 vr.conf 를 멀티베드 설정으로 교체 후 VR 을 재실행하면, 첫 실행에서는 멀티탭이 정상 생성되지만 다음 실행에서 모든 탭이 사라지던 문제. 원인은 VRApp.cpp:1110 의 VRCODE 미설정 분기 — 새 vr.conf 가 VRCODE 라인을 포함하지 않으면 load_settings() 가 신규 VRCODE 를 issue_code() 로 발급하고 즉시 save_settings() 를 호출하는데, 이 시점은 load_settings() 가 파싱한 베드들이 로컬 newbeds 벡터에만 있고 아직 m_pdisps 로 옮겨지기 전 (set_beds(newbeds) 는 initialize() 가 load_settings 반환 후 라인 1328 에서 호출). 결과: save_settings() 가 빈 m_pdisps 를 iterate 하며 vr.conf 를 글로벌 설정만으로 덮어써 모든 [BED/...] 섹션이 디스크에서 사라짐. 첫 실행은 메모리의 newbeds 로 UI 멀티탭이 그려져 정상으로 보이지만 (사용자가 "멀티탭은 정상적으로 생성되지만" 으로 표현), VR 종료/재실행 시 디스크 conf 에는 BED 섹션이 없으므로 다음 load_settings 가 빈 베드 리스트를 반환 → 탭 전부 사라짐. 수정: VRApp.h 에 m_vrcode_newly_issued bool 플래그 추가, load_settings 의 즉시 save 를 플래그 set 만 으로 교체, initialize() 의 set_beds(newbeds) 직후 (m_pdisps 가 채워진 시점) 플래그가 켜져 있으면 한 번만 save_settings() 호출 → BED 섹션이 정상 직렬화됨. Linux/Pi 분기 (m_serial_number 가 채워져 있는 경우) 는 원래도 save 안 했으므로 영향 없음, VRCODE 가 이미 conf 에 있는 모든 기존 deployment 도 분기 미진입으로 0-impact. m_conf_from_stdin / m_bDebug 모드는 save_settings 내부에서 이미 early return 이라 그대로 안전. MFC + Qt 빌드 모두 통과 (이번 변경 관련 0 warning). | |
| 1.18.44 | 2026-06-12 | 1.18.44: vr.conf FHIR=1 모드 추가 (자체 FHIR 수신서버 대상 실시간 푸시) + VS 2026 / 새 OpenSSL 인스톨러 빌드 환경 호환 shim. (1) sendweb_thread_func 의 기존 HL7/JSON 양분 분기 (VRApp.cpp:2466) 옆에 bFHIR 분기 추가. 1초 tick 마다 한 vrcode 의 모든 방 Observation 을 하나의 FHIR R4 collection Bundle 로 묶어 기존 WSS 채널로 push (gzip + WSSClient 인프라 그대로 재사용). MLLP 같은 신규 transport 안 도입 — FHIR R5 Subscription 의 websocket channel type 과 동형이라 표준 부합. (2) DISPLAY::get_fhir + TRACK::get_fhir 신규. v2 (vitaldb-fhir-v2) 와 동일한 reference-only 패턴 (Patient.id = m_patient_id, Encounter.id = vrcode-bedname-startsec, Observation.subject/encounter = relative reference) + hybrid keyframe: 첫 tick / m_patient_id 변경 / get_start_time() 변경 / 5분 경과 중 하나라도 발생 시 다음 tick Bundle 에 Patient + Encounter resource 를 inline. 그 외 tick 은 Observation 만. 수신서버가 도중에 새로 connect 해도 최대 5분 안에 자동 동기화 (keyframe heartbeat). DISPLAY 에 m_fhir_pt_sent_at/m_fhir_pt_last_id/m_fhir_enc_last_start 3개 멤버 추가 (m_setting_sent 패턴 그대로 per-display). (3) Observation 인코딩: NUM 은 effectiveDateTime + valueQuantity 각 데이터 포인트당 한 entry, WAV 은 effectivePeriod + valueSampledData (period=1000/srate ms, factor=1, origin=0, data=공백구분 floats) 한 entry. CO2/AWP wav 는 기존 get_json 처럼 25Hz 다운샘플 유지. NUM 은 m_montype 기반 LOINC 매핑 15개 등록 (ECG_HR/PLETH_HR=8867-4, PLETH_SPO2=59408-5, NIBP/IABP SBP/DBP/MBP=8480-6/8462-4/8478-0, BT=8310-5, RR=9279-1, etCO2=19891-1, CVP=8556-0, FETAL_HR=11627-3) + VR 자체 코드 시스템 (https://vitaldb.net/fhir/CodeSystem/track, code = dname/tname) 항상 동반. m_patient_id 미설정 (PATIENT_ID_UNSET) 시 anon-vrcode-bedname identifier 로 fallback. (4) Bundle wrapper: VRApp 가 방별 entries 를 콤마로 합쳐 {resourceType:Bundle, type:collection, timestamp:iso8601Z, entry:[...]} 로 wrap, 기존 gzip → WSS pipeline 동일. ISO 8601 UTC 헬퍼는 std::gmtime_s/gmtime_r 인라인. FHIR id 안전화 (A-Za-z0-9-.) 64자 제한 lambda 각 함수에 inline. (5) 빌드 환경 호환성: VRQt/CMakeLists.txt 에 _SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS 추가 — VS 2026 (MSVC 14.51+) 의 STL 이 <experimental/coroutine> deprecation 을 warning → hard static_assert 로 바꾼 것이 cppwinrt 의 winrt::* (Serial.cpp 의 Bluetooth LE) 를 끌어오는 모든 TU 에서 컴파일 실패 시켜서, build_mfc.py 가 이미 CL env 로 주입하던 것과 동치 shim 을 Qt 쪽에도. (6) VRQt/CMakeLists.txt 의 OpenSSL link 경로 시프트. 새 OpenSSL 인스톨러가 libssl_static.lib / libcrypto_static.lib 를 lib/VC/x64/{MT,MTd}/ 에 두고 v1.x 의 libssl64MT.lib / libcrypto64MT.lib 네이밍과 lib/VC/static 경로를 깬다. build_mfc.py 의 %LOCALAPPDATA%/openssl-compat 패턴 (옛 이름으로 복사한 사본 폴더) 을 그대로 따라 CMakeLists 도 그 폴더가 있으면 우선, 없으면 옛 lib/VC/static 으로 fallback. 부트스트랩 스크립트는 build_mfc.py 의 docstring 참조 (한 줄). MFC + Qt + RPi + Ubuntu CLI + AppImage 빌드 모두 통과. 동작 영향: vr.conf 에 FHIR=1 추가 안 한 기존 deployment 는 sendweb 가 종전 JSON 모드 그대로 (HL7=1 만 분기보다 우선) — 0-impact migration. | |
| 1.18.43 | 2026-05-21 | 1.18.43: 멀티베드 console (PACU 10베드 Pi) syslog noise 80,500/hr → ~1,400/hr (-98.3%), cpu_usage 측정 1.16.12 (9f26e36) 이후 8년치 영구 -1 버그 수정, 멀티베드 환경 베드 식별용 [bedname / devicename] 로그 prefix 도입. 동기는 PACU production Pi (1.18.40) 4일치 syslog 분석 — 1.24M 라인 중 vr 라인 836K, idle 8시간 평균 시간당 80,500 라인 (베드 10 × 1초마다 "Waiting for next patient" × syslog 2x 중복). (1) VRApp.cpp:2696-2710 timer_thread_func 의 cpu usage 계산이 `if (totalcpu && m_cpu_total_last && totalcpu > m_cpu_total_last)` 가드 안에서만 m_cpu_total_last 갱신해 첫 호출 시 m_cpu_total_last=0 → else 분기 → -1 → m_cpu_total_last 갱신 안 됨 → 영원히 false → 영원히 -1 데드락. PACU 1,048 샘플 100% -1 실측 확인. m_cpu_total_last/m_cpu_used_last 갱신을 if 밖으로 빼서 첫 호출만 baseline 으로 -1, 두 번째 (60s 후) 부터 정상 측정. /proc/stat 의 iowait/irq/softirq 까지 7개 필드 fscanf 로 정확도 ±5% 개선. 부수: 같은 함수의 cpu_temp 가 `temp / 1000` int 나눗셈으로 소수점 절단되어 PACU 측정값이 정수 50/51/...,/58 9종만 출력 (millidegree 정밀도 손실) → `temp / 1000.0f` 로 fix. ram 계산은 `(totalram + totalswap) * mem_unit` 결과에 다시 mem_unit 곱하던 차원 혼용 (Linux 가 mem_unit=1 이라 우연히 정상 동작했지만 dimensionally wrong) → `(units1 + units2) * mem_unit` 로 정정 + underflow 가드. (2) DISPLAY::log(string/printf) + DEVICE::log(string/printf) 멤버 추가. DISPLAY::log 는 `[bedname] msg`, DEVICE::log 는 m_pdisp 위임 없이 직접 `[bedname / devicename] msg` 한 줄 prefix (nested `[bed] [dev]` 보다 가독성 우선). m_pdisp 또는 bedname null 케이스 safe fallback. (3) show_msg 의 console path TRACE 를 log() 로 교체 → "Waiting for next patient" 자동 [bedname] prefix. ADT patient id unset 도 log() 로. (4) 이미 수동으로 get_bedname() 을 인자로 넘기던 8군데 (DISPLAY ptcon_thread_func / add_trk / add_dev / del_dev / cut-recording, Device add_rec 콜백, FILT stop, VRApp add_tab/del_tab) 를 log() 로 전환 — 이중 prefix 방지 + 일관성. Serial::reading_thread / tcp_thread / open_socket waiting / timeout / interrupted / got-client 6군데 DEVICE::log() 전환 → `[bedname / Intellivue]` 등 자동 prefix, 멀티탭 환경에서 어느 베드 어느 device 출처인지 즉시 식별. (5) wss 재시도 폭주 throttle. WSSClient 에 m_verbose (debug step 로그 게이트) + m_dt_last_err_log (60s window) 추가. connect() 내부 step-by-step TRACE 4개 ("connecting" / "connect error" / "connected") 는 m_verbose 가드, 최종 "wss connect error" 는 60s window throttle (첫 줄 즉시, 그 다음 60s 1회). connect 성공 path 에서 throttle reset 으로 다음 disconnect → 첫 에러 즉시 로깅. sendweb_thread_func 의 "connecting websocket server" 호출자 라인도 같은 60s window, m_bDebug 면 verbose 활성화. PACU idle 8h 측정 7,024/hr (4종 × 1,756) → 약 60/hr (-99%). (6) "Waiting for next patient" / "ADT patient id unset" 의 60s throttle 자체는 feature/wav-mem-retention 브랜치 ptcon_thread_func 재구성 (b75928f 시리즈) 에서 이미 들어가 있어 이 릴리즈에 자동 포함. 종합 영향: idle 시간당 syslog 라인 80,500 → ~1,400 (-98.3%), active 시간당 14,900 → ~700 (-95%). 진단 측면: cpu_usage 가 부활하면서 웹모니터링 json 의 cpu_usage 필드도 한 번도 안 채워지던 게 정상 전송 (VRApp.cpp:2499 의 `if (m_cpu_usage > 0)` 가드가 영원히 false 였음). 별건: syslog 라인 2× 중복은 application 측이 아니라 rsyslog imuxsock + imjournal + journald.ForwardToSyslog=yes 의 Debian Bookworm 전형 misconfig — admin 측 fix (rsyslog.conf 에서 imjournal 주석 처리 + restart) 로 추가 50% 감소 가능 (별도 문서). MFC + Qt + Pi (RPI64) + Ubuntu CLI 빌드 모두 통과. | |
| 1.18.41 | 2026-05-13 | 1.18.41 Hotfix: TCP-server protocol-initiator device (Intellivue/S5/Datex/Hamilton/MPS/Agilia/IAP) 가 open() 직후 보내는 첫 패킷이 wire 에 안 나가던 회귀. 1.18.34 의 PortRouter refactor 가 Serial::open_socket() 의 8초 handoff blocking 을 제거하고 비동기 모델로 전환했는데, protocol-initiator 의 open() 코드가 m_sock=INVALID 상태에서 write 를 시도해 silent fail (Linux 는 m_fd=0 이라 false 반환). Intellivue 케이스 production journald 의 Pi → ESP TX 700K~3M bytes 는 monitor 측 abort 로 늦게 트리거된 AB_SPDU_SI recovery assoc_req 후 request_thread 의 정상 polling 트래픽이지만 일부 베드 (abort 없는 monitor) 는 recovery 도 안 되어 영구 silent (RX 300 bytes). 수정 방향: open() 의 옛 contract ("return 시점에 통신 가능") 복구. (1) Serial::open_socket() TCP-server 분기가 PortRouter::subscribe 후 m_sock_cv 로 첫 client handoff 까지 block (timeout = device read_timeout, 기본 30s) — 옛 모델 그대로. lock_and_open 이 false 면 다음 tick 에서 retry. close 신호로 abort 가능. (2) Serial::close() 는 PortRouter::unsubscribe 안 함 — subscribe 는 device 생애 동안 sticky 라 read_timeout 으로 인한 close→re-open 사이클 동안 listen socket 이 유지되어 1.18.29/1.18.34 의 anti-churn 이익 보존. unsubscribe 는 ~Serial() 에서만. (3) Per-device lifecycle thread (DEVICE::m_thread_restart): 각 device 가 자기 open/close/timeout 재시작을 별도 thread 에서 관리. 옛 모델의 DISPLAY::bed_manager 가 베드의 모든 device 를 sequential iterate 하던 구조에선 한 device 의 blocking open() 이 같은 베드의 다른 device 의 lifecycle 을 차단했는데, per-device thread 로 분리하여 멀티 device 베드도 병렬 진행. add_dev 에서 start_lifecycle, del_dev / ~DISPLAY 에서 stop_lifecycle. (4) Serial::tcp_thread_func 단순화 — outer m_sock_cv wait 패턴 (1.18.34/1.18.39 가 추가했던 것) 제거. open_socket 이 m_sock valid 보장하므로 reader thread 시작 시점에 simple inner recv loop 만. accept_handoff 의 replace (zombie cleanup) 케이스는 recv error 후 m_sock 변경 확인 + retry 로 처리. (5) reading_thread_func 의 `else if (m_sock != INVALID_SOCKET) tcp_thread_func();` 가드 복원 (1.18.39 가 제거). (6) Serial::write 의 silent-fail 케이스에 WARN trace 추가 — 미래 동종 회귀 즉시 노출. (7) PortRouter::subscribe 가 같은 (port, type) 에서 같은 IP filter 또는 둘 이상 catch-all 발견 시 reject (config 실수 원천 차단, 옛 WARN-only 동작 강화). (8) DISPLAY::bed_manager_thread_func 의 restart 블록 제거 — ptcon + adt 만 담당. "Waiting for next patient" TRACE 가 Linux/console 에서 1초 → 60초마다 throttle (별개 journald 부풀림 fix). 영향 범위: Intellivue / S5 / Datex / Hamilton / MPS / Agilia / IAP TCP-server 모드 베드 전부. 1.18.34 이후 우회 방법은 race-win 케이스 (VRN 이 PortRouter::subscribe 직후 connect) 외엔 없었음. MFC + Qt + Pi (RPI64) + Ubuntu Qt + AppImage 빌드 모두 통과. | |
| 1.18.40 | 2026-05-12 | Hotfix: Drager Medibus 프로토콜 사용 베드에서 1.15.11 이후 vr.service 가 SIGSEGV 로 무한 재시작하던 문제. 1.15.10 (a8c7b2a) 의 "Sampling rate estimation" 리팩터링이 m_ptrk_awp/awf/co2 트랙을 add_trks() 에서의 eager 생성 (default srate=62.5Hz) 에서 realtime data 100 샘플 모인 후 lazy 생성으로 바꿨는데, Medibus 핸드셰이크 순서 (0x51 ICC → 0x52 DeviceID → 0x53 RealtimeConfig 응답 → 0x54 → realtime data 시작) 상 0x53 응답 핸들러 (Drager.cpp:295-307) 가 트랙들이 아직 nullptr 인 시점에 `ptrk->m_srate = 1000000.0/interval` 로 NULL 역참조 → 즉시 SEGV. 그 베드의 vr.service 가 systemd Restart=on-failure 로 재시작 → 다시 connect → 동일 0x53 패킷 받고 동일 SEGV → 무한 루프 (journald 로그상 restart counter 61 까지 누적, code=killed status=11/SEGV). 1.15.9 까진 트랙이 add_trks() 에서 생성되어 0x53 도착 시 항상 non-NULL 이라 안전했음. 수정: (a) add_trks() 에서 m_ptrk_awp/awf/co2 eager 생성 복원 (srate=62.5Hz default) — 0x53 는 ptrk->m_srate 만 갱신 (트랙 재생성 X). (b) on_received() 의 lazy creation + packet-timing srate 추정 블록 제거 — 0x53 가 권위 있는 srate 소스이고 packet 타이밍 기반 추정은 network jitter 에 약해 불안정. (c) 0x53 핸들러에 `if (interval > 0)` guard 추가 — 페이로드 깨졌을 때 div-by-zero / inf srate 방지. 영향 범위: Drager Primus / Apollo / Perseus / Fabius 등 Medibus 사용 마취기 전부. 우회 방법은 1.15.9 다운그레이드 외엔 없었음 (그 베드의 모든 데이터 유실 + 환자 모니터링 불가). MFC + Qt + Pi (RPI64) + Ubuntu Qt + AppImage 빌드 모두 통과. | |
| 1.18.39 | 2026-05-08 | Fixed: PortRouter (TCP server) 모드에서 reader thread 가 startup 시점에 죽어 client 데이터를 영구히 못 받던 문제. 1.18.34 (9bd252f) 의 PortRouter refactor 가 m_sock 세팅을 dispatch 시점으로 옮겼는데 reading_thread_func (Serial.cpp:354) 의 가드 `else if (m_sock != INVALID_SOCKET) tcp_thread_func();` 는 옛 모델 (open 시점 이미 connect 완료) 가정 그대로라 — 시작 직후 m_sock=INVALID 면 tcp_thread_func 진입 안 하고 reader thread 즉시 종료. 이후 VRN 이 connect → PortRouter::dispatch → accept_handoff 가 m_sock 채우고 m_sock_cv.notify_all() → 그러나 wait 중인 reader 가 없음 (이미 죽음) → kernel recv buffer 에만 데이터 적재. 증상은 ss -tn state established 의 Recv-Q 가 0 이 아닌 값 (수백 bytes) 으로 누적, 베드 화면에는 데이터 안 들어옴. 그 베드의 VRN 이 Serial::open() 의 PortRouter::subscribe 직후~reader thread spawn 사이의 짧은 윈도우 안에 connect 한 경우만 우연히 정상 동작 (race-win). 수정: 가드 제거 — TCP 분기에 항상 진입. tcp_thread_func 자체에 이미 m_sock_cv outer-wait 루프가 있어 m_sock 이 INVALID 인 동안 dispatch 가 깨워줄 때까지 안전하게 대기. Pi 핫스팟에 VRN 여러 대 (e.g. 10.42.0.21~25) 를 같은 (4343, Philips:Intellivue) 키로 묶고 IP filter 로 라우팅하는 시나리오에서 가장 자주 노출. Added: 진단 로그 보강 — PortRouter::accept_loop 의 accept 이벤트, dispatch 의 라우팅 결정 (ip_specific/catch_all subs 수, 어느 dev 가 받았는지), subscribe/unsubscribe 의 entry 생성/append/destroy, reading_thread / tcp_thread 의 시작·wait·wake 라이프사이클 — 이전엔 모두 m_bDebug 게이트 안에 있어서 production journald 에서 안 보였는데, 이제 항상 출력 (이번 같은 dispatch routing 문제 진단을 production 에서 바로 가능). MFC + Qt + Pi (RPI64) + Ubuntu Qt + AppImage 빌드 모두 통과. | |
| 1.18.38 | 2026-05-08 | Fixed: 웹모니터링 우클릭 → 디바이스 세팅 (websocket edit_bed 명령) 으로 베드의 device/filter 를 변경할 때 베드 이름이 매번 "_2" suffix 로 자동 리네임되어 vr.conf (Pi: /data/vr.conf) 에 잘못된 이름으로 저장되던 문제. 원인은 1.18.22 (cda8aa5) 에서 add_bed 에 추가된 bedname dedup ("최후 방어선" 으로 forward_frame auto-create / 수동 편집된 conf 의 중복 이름 처리용). edit_bed 핸들러는 옛 pdel 이 m_pdisps 에 그대로 있는 상태에서 같은 bedname 으로 new DISPLAY 를 add_bed 하고 그 다음 del_bed(pdel) 하는 순서였는데, add_bed 가 dup 감지해 새 pdisp 를 bedname_2 로 자동 리네임 → save_settings 가 [BED/bedname_2] 로 직렬화 → 사용자 입장에선 "설정 적용 안됨" 으로 보였다. 추가로 옛/새 DISPLAY 가 잠깐 m_pdisps 에 공존하면서 같은 device 포트를 동시에 들고 있으려 해 로그가 dup 으로 찍히는 부수 증상도 있었음. 수정: del_bed(pdel) 를 add_bed(pdisp) 보다 앞으로 이동 — 옛 베드의 device 포트가 깨끗이 release 된 뒤 새 pdisp 가 같은 포트를 reopen, dedup 발동 안 함. 다른 베드의 recording 은 영향 없음 (인메모리 hot swap 설계 유지). 부수: edit_bed 핸들러의 데드 코드 (`break` 뒤의 unreachable olddevs populate 루프 + `if (olddevs.find(name) == olddevs.end())` 항상-true 래퍼) 정리. edit_conf 와 save_settings 는 손대지 않음. MFC + Qt + Pi (RPI64) 빌드 모두 통과. | |
| 1.18.37 | 2026-05-08 | Refactored: 필터 다이얼로그의 python 런타임 — pyvital → openvital 0.3.0 로 교체 + 동봉 python.zip 슬림화. 핵심 동기 셋: (1) pyvital 0.6.0 이 tensorflow+torch+keras 를 hard-dep 으로 끌어들여 PyPI upgrade 가 사실상 깨짐, (2) 기존 zip 121 MB 안에 botocore/aiobotocore/s3fs/pyarrow/pandas/aiohttp 등 vitaldb 클라우드 업로드용 deps 가 ~250 MB unpacked 차지 — 필터 서버는 한 번도 쓴 적 없는 dead weight, (3) sanic 25.x 의 multi-process worker 가 openvital __main__.py 를 fork 후 재import 하면서 worker 가 module-top app instance 를 못 찾아 "Sanic app name not found" 로 죽음 — 단순 endpoint 2개 로컬호스트 서버에 async stack 자체가 과함. 해결: openvital 0.3.0 (commit b6f445b) 가 sanic 을 stdlib http.server (BaseHTTPRequestHandler + HTTPServer) 로 50 LOC 재작성 — wire protocol (GET / 필터목록 JSON, POST /<modname> gzip 본문) 동일해 VR 클라이언트 변경 0줄. 단일 스레드로 기존 sanic event loop semantics (cfgs/default_cfgs lock-free 변경) 보존. build_python_zip.py (NEW) 가 Python 3.11.9 embed + numpy + openecg + openvital (local checkout 우선, fallback PyPI) 만으로 27 MB zip 빌드 — 기존 121 MB 대비 78% 감소. ML 필터 (sv_dlapco/abp_hpi/ecg_classifier/ecg_beat_noise_detector) 는 [Hybrid 모델] 사용자가 Add filter 다이얼로그의 "Install ML filters" 버튼 누를 때 silent pip install openvital[all] (cmd 창 미노출 — VRApp::install_pip_package + run_pip_silent 이 자체 progress 다이얼로그에 stdout heartbeat 흘림). 버튼 라벨은 "Upgrade pyvital" → "Install ML filters" 로 의도 명시. openvital __main__.py 가 missing extras 를 graceful skip 하므로 base 환경에서도 11개 필터 (ECG QRS detector, HRV, MTWA, annotator, eeg_fft, nirs_cox, pkpd_3comp, pleth_dpop/ptt/pvi, resp_compliance) 즉시 사용 가능. Migration: 기존 사용자의 user_dir/python 폴더는 pyvital + 옛 deps 가 그대로 남아있고 vitaldb.net/python.zip 만 새로 올리면 (a) 기존 폴더 삭제 후 install_python 으로 새 zip 받으면 깔끔, (b) 폴더 유지 시 server 가 openvital 못 찾아 필터 다이얼로그 에러 → release note 에 명시 필요. MFC + Qt 양쪽 코드: VRGui.rc 의 IDC_UPGRADE 버튼 라벨 + DlgAddFilter.cpp 의 dist-info prefix 매칭 (pyvital- 8자 → openvital- 10자) + QtDlgAddFilter.cpp 도 동일 변경 + onUpgrade 가 cmd /k QProcess::startDetached 대신 theApp.install_pip_package("openvital[all]") 호출 (Linux Qt 빌드는 #ifdef _WIN32 가드). VRApp.cpp:318 의 python -m pyvital → -m openvital. install_pip_package 는 anonymous namespace 의 is_safe_pkg_spec 화이트리스트 검증 + run_pip_silent (CreateProcessA + CREATE_NO_WINDOW + 자식 stdout pipe) + CDlgProgress / QtDlgProgress 자체 progress UI. 빌드 검증: python -m openvital 이 GET / 로 11 필터 반환, sanic import 부재 확인. zip 27.5 MB. | |
| 1.18.36 | 2026-05-07 | Fixed: 3-파티션 레이아웃 (boot/rootfs/data) 에서 websocket edit_conf / update 명령이 read-only fs 로 실패하던 문제. vr.conf 는 /data 폴더가 있으면 /data/vr.conf 로 자동 정착 — get_conf_path() 가 write target 으로 결정, load_settings() 는 primary 가 없으면 legacy /boot/firmware/vr.conf 또는 /boot/vr.conf 에서 fallback read 후 다음 save 부터 primary 로 자동 이전 (마이그레이션 step 없음, legacy 파일은 read-only fs 라 무해). binary update (upgrade_now) 는 RAII BootRwGuard 가 dirname(module_path) 를 잠시 rw remount, scope 종료 시 sync() + MS_REMOUNT|MS_RDONLY 로 자동 ro 복귀 — vfat 노출 윈도우 최소화 + exception/return 어디서든 복구 보장. statvfs ST_RDONLY 검사로 이미 rw 인 fs (x86 dev) 에서는 no-op, non-Linux (macOS) 는 stub 으로 통과. save_settings() 의 silent EROFS 실패가 TRACE 로 가시화 (이전엔 무시되어 conf 수정 안 먹는 원인 추적 불가). 부수: PortRouter.cpp 가 commit 9bd252f 이후 CMakeLists 의 RPI sources 목록에서 누락되어 Pi 빌드 link 에러 — 추가. | |
| 1.18.35 | 2026-05-01 | Added: alarm parsing for Mindray HL7 (eGateway ORU^R40) and Nihon Kohden (NealTime HL7GW + ORF, BSM serial D-port, EGA UDP) - emits ALARM_STATUS / ALARM_MESSAGE / ALARM_PRIORITY tracks (same 3-track schema already used by Philips IntelliVue, GE Solar/Dash, GE/Datex S5); type info preserved via [T]/[A]/[ARR] message prefixes; Mindray facet 1/5/6/7 walker with 92-entry MDC+MNDRY alarm code lookup; NK HL7GW parses OBR-4=EVENT with arrhythmia/technical disambiguation by OBX-5; NK BSM serial decodes per-parameter * flags (edge-triggered to avoid 1Hz flooding) plus 32-bit Table 9 arrhythmia bitmap, D19 alarm-suspend nibble, and the VPC CODE byte; NK EGA UDP extracts Priority + Tech Alarm + Arrhythmia from JSON Vital Sign packets with edge-trigger cache for start/end inference; ACK^R40 reply added for Mindray alert messages; simulators gained periodic alarm cycling for end-to-end smoke testing | |
| 1.18.34 | 2026-04-27 | Added: Linux Desktop Qt GUI build (VRQt) - first GUI build for Linux distributed as a self-contained AppImage (~32MB, Qt6 runtime + libGL/libEGL/libGLX/libOpenGL/libGLESv2/libGLdispatch bundled inside) so users on minimal Ubuntu/Debian/Fedora can run it without installing Qt or GL system packages; tested on AWS clean Ubuntu 22.04 cloud image; build via build_ubuntu_appimage.py which is now invoked by deploy.py; Fixed: Linux Qt right-side device/filter panels rendered with white background below cards because the inner listWidget did not autoFillBackground from parent palette on Linux Qt (Windows/macOS implicitly inherit, X11/Wayland do not); explicit stylesheet on listWidget; Added: Linux installation section in user manuals (EN/KO/ZH/ES) covering AppImage chmod+run flow and dialout group note for serial/USB device access | |
| 1.18.33 | 2026-04-25 | Fixed: Demo device never started since 1.18.11 (bed_manager skipped empty port_name as passive but Demo has no port and still needs lock_and_open); Fixed: Qt fit-to-window button rendered at top-left (0,0) instead of right-aligned in bottom nav bar; Fixed: Qt crash when closing tab / deleting device / deleting filter - paint dereferenced freed pointer before panel timer rebuilt items (added MFC-style stale-pointer guard to QtDeviceItem and QtFilterItem); Added: macOS DMG main window now shown on launch (restoreWindowPos had no Mac/Linux branch); Added: macOS Homebrew dylibs (openssl) bundled in Contents/Frameworks; Added: macOS .app bundle now notarized + stapled (drag-to-Applications no longer needs xattr -cr on first launch); Changed: Qt MSI ~1MB smaller (dropped unused iconengines and imageformats plugins) | |
| 1.18.32 | 2026-04-23 | Fixed: same-port multi-client TCP - primary now runs a continuous accept loop and hands off each connection to the matching sub by IP filter (previously second connection got rejected/listened closed); Added: @IP and #keyword port filters are now mutually exclusive per bed (IP wins, keyword dropped with WARN); Added: WARN log when two beds on the same port share an identical IP filter; Added: catch-all primary keeps its single connection and rejects extras (preserves broadcast mode) | |
| 1.18.31 | 2026-04-22 | Added: drug slot shows DOSE_AMOUNT (mg etc) in preference to VOL (mL) when available; Added: DOSE_RATE / DOSE_AMOUNT montypes bound to BBraun tracks for monitor view; Added: HL7 MDC_DOSE_DRUG_DELIV_TOTAL unit picked up dynamically from UCUM | |
| 1.18.30 | 2026-04-21 | Changed: monitor view pump layout - 8 slots with CE or RATE (CE-less pumps show infused volume as small annotation); Reverted: BBraun minimal=1 (1.18.29) - was dropping data from file, monitor view redesign handles display clutter instead | |
| 1.18.29 | 2026-04-21 | Fixed: BBraun multi-rack auto-tab data drop, primary TCP churn, save_settings lock race, TCP reader spin on peer close; Added: BBraun minimal=1 option (display only drug name / rate / volume) | |
| 1.18.28 | 2026-04-20 | Fixed: vr.conf on Pi/Linux now looked up next to executable (supports /boot/firmware); deploy.py --store auto-submits MSIX to Microsoft Store | |
| 1.18.27 | 2026-04-18 | Fixed: vital file periodic flush (5000s→5s bug), Fit-to-Window icon redesigned to match navigation button style | |
| 1.18.26 | 2026-04-18 | Fixed: periodic .vital file flush interval was 5000 seconds instead of 5 (unit-bug since comment said seconds but value was milliseconds). Data now actually reaches disk every 5 s during recording instead of only on close. | |
| 1.18.25 | 2026-04-18 | Fixed: BBraun HL7 rec_timeout raised 10s→60s to prevent primary churn during DoseLink R42→R01 phase transition and long idle periods | |
| 1.18.24 | 2026-04-18 | Fixed: GUI silently exits when another recorder running (now falls back to viewer mode), .vital file association written to HKCU (no admin required, auto-updates exe path on every run) | |
| 1.18.23 | 2026-04-18 | Fixed: atof decimal-separator bug on non-English Windows locales (rates <1.0 recorded as 0 in Norwegian/German/French etc.), BBraun HL7 pump limit raised from 8 to 16 | |
| 1.18.22 | 2026-04-18 | Fixed: multi-bed TCP port bind race on restart (Mindray/BBraun HL7 auto-reconnect without manual Add device) | |
| 1.18.21 | 2026-04-18 | Fixed: port name filter (#keyword@ip) lost on save, BBraun HL7 VMD-block parsing with single dt, Fit-to-Window button sizing (MFC) and added to Qt | |
| 1.18.20 | 2026-04-15 | BBraun: comprehensive protocol parsing (dose rate, infusion time, syringe, weight, drug amounts, delivery status) | |
| 1.18.19 | 2026-04-13 | Fixed: BBraun HL7 multi-pump identification (rack serial filtering), multi-bed forwarding, port filter parsing | |
| 1.18.18 | 2026-04-10 | Added: GE alarm limits, S5 alarm/ext3/ext1 parsing | |
| 1.18.17 | 2026-04-04 | Changed: TRACE debug-only guard, show_msg for file errors, deploy sequential builds | |
| 1.18.16 | 2026-04-04 | Added: TCP port filtering (#keyword@ip), frame forwarding for multi-bed. Fixed: --debug conf file mistaken as vital file, device type ambiguity in forwarding | |
| 1.18.15 | 2026-04-04 | Changed: Port to Port Name, keyword filter AND/OR (#kw1 kw2#kw3), IP postfix matching (@10.1), MEKICS SerialFramed | |
| 1.18.14 | 2026-04-03 | Added: TCP port filtering (#keyword@ip), Fixed: devtypes not sent on boot | |
| 1.18.13 | 2026-04-02 | Changed: Covidien company name to Medtronic (BIS, INVOS) | |
| 1.18.12 | 2026-04-02 | Bug Fixed: NihonKohden EGA SpO2 waveform not displayed (channel name mapping) | |
| 1.18.11 | 2026-03-31 | Added: Mindray HL7 multi-bed separation via PV1, auto-tab creation for EGA/Mindray | |
| 1.18.10 | 2026-03-31 | Added: HL7 ZBR segment now includes ver, os, arch, dgmt | |
| 1.18.8 | 2026-03-25 | Added: --demo mode for console operation with demo signals | |
| 1.18.7 | 2026-03-25 | Added: --console mode for headless operation on Windows | |
| 1.18.6 | 2026-03-25 | Added: Mindray eGateway HL7 protocol (IHE PCD-01, HL7 v2.6 over MLLP, waveform + numeric + infusion pump) | |
| 1.18.4 | 2026-03-24 | Added: BBraun SpaceCom HL7 protocol (HL7 v2.6 over MLLP, TCP server mode) | |
| 1.18.3 | 2026-03-24 | Added: HL7 OBX-14 per-track dtstart, ZBR segment with dtcase/dtapp timestamps | |
| 1.18.2 | 2026-03-24 | Added: HL7 ZBR segment with dtcase (recording start) and dtapp (app start) timestamps | |
| 1.18.1 | 2026-03-23 | Fixed: HL7 OBX-3 format (code^device/track@srate), OBX-7 refrange (min^max), NA null sample gaps | |
| 1.17.6 | 2026-03-22 | Bug fixed: Resource ID conflicts WiX installer cleanup | |
| 1.17.5 | 2026-03-21 | Bug fixed: Vital packed file loading MFC device button icon padding | |
| 1.17.4 | 2026-03-19 | Bug fixed: NirsitON EEG 250Hz VRQt device minimize/rename | |
| 1.17.3 | 2026-03-18 | Bug fixed: BBraun protocol NirsitON protocol VRQt | |
| 1.17.2 | 2026-03-14 | Added: NirsitOn NDI protocol support. | |
| 1.17.1 | 2026-03-12 | Added: VRQt | |
| 1.16.13 | 2026-03-07 | Added: New Setup File with VRQt | |
| 1.16.12 | 2026-03-06 | Added: Vector Type Icons | |
| 1.16.10 | 2026-03-02 | Bug fixed: Autodetecting BBraun Stuffing feature. | |
| 1.16.8 | 2026-02-27 | Bug fixed: Masimo IAP protocol. | |
| 1.16.4 | 2026-01-20 | Bug Fixed: Serial communication. | |
| 1.16.3 | 2026-01-14 | Bug Fixed: Masimo IAP Protocol. | |
| 1.16.2 | 2026-01-03 | Added: BLE Support for Movesense devices on Raspberry Pi | |
| 1.16.1 | 2026-01-01 | Added: BLE Support for Movesense devices on Raspberry Pi and Linux versions. | |
| 1.16.0 | 2025-12-31 | Added: BLE Support for Movesense devices | |
| 1.15.16 | 2025-09-18 | Bug Fixed: Restoring last window position. | |
| 1.15.15 | 2025-09-05 | Added: CPU optimization Bug fixed: Masimo IAP protocol. | |
| 1.15.14 | 2025-09-04 | Added: CPU optimization | |
| 1.15.13 | 2025-09-04 | Bug Fixed: Masimo IAP Protocol. | |
| 1.15.12 | 2025-09-04 | Bug Fixed: Hardware handshake for Link+ connection. | |
| 1.15.11 | 2025-09-01 | Added: Recording alarm messages for Drager anesthesia machines | |
| 1.15.10 | 2025-07-14 | Bug Fixed: Sampling rate estimation in Medibus protocol. | |
| 1.15.9 | 2025-06-03 | Bug Fixed: EEG waveform labels from Philips monitors. | |
| 1.15.8 | 2025-05-26 | Added: EM_UPLOAD option to upload vital files to server immediately | |
| 1.15.7 | 2025-05-26 | Bug Fixed: Websocket connection error | |
| 1.15.6 | 2025-05-19 | Bug Fixed: S5 checksum error | |
| 1.15.5 | 2025-04-17 | Bug Fixed: saving the raw data | |
| 1.15.4 | 2025-04-15 | Added: Recording alarms from Philips monitor | |
| 1.15.3 | 2025-04-14 | Bug fixed: saving the raw data | |
| 1.15.2 | 2025-03-12 | Fixed: bug in opening file | |
| 1.15.1 | 2025-01-30 | Supporting for Fresenius Kabi Conox, Minor Bug fixes | |
| 1.14.13 | 2025-01-18 | Fixed bug in handling non-ascii filepath | |
| 1.14.12 | 2024-11-20 | Fixed: bug in 64bit ARM Architecture | |
| 1.14.11 | 2024-10-24 | Added: feature for updating to a specific version | |
| 1.14.10 | 2024-10-22 | Fixed: bug in json streamming | |
| 1.14.9 | 2024-10-17 | Added: montype for drug rates | |
| 1.14.6 | 2024-09-20 | Added: montype for drug rates | |
| 1.14.5 | 2024-09-13 | Added: auto cutting by any of hr or spo2 | |
| 1.14.4 | 2024-09-12 | Fixed: bug in serial port mapping |
| Version | Date | Changes | |
|---|---|---|---|
| 1.19.9 | 2026-08-14 | 1.19.9 Fixed: 큰 녹화 파일(약 100MB 이상)을 서버로 올릴 때 프로그램이 메모리 부족으로 강제 종료되고, 재시작 후 같은 파일을 다시 시도하다 무한 반복되던 문제 — 업로드 본문을 메모리에 통째로 만들지 않고 디스크에서 흘려보내도록 변경(100MB 파일 기준 최대 메모리 208MB → 14MB). 큰 파일이 한 개라도 쌓이면 그 장비의 이후 업로드가 전부 막히던 증상이 사라진다. 1.19.8 Fixed: 자동 파일 자르기(CUT_FILE) 를 쓰는 베드에서 환자가 계속 연결돼 있는데도 녹화 파일이 PT_WAITING_TIME(기본 5분) 마다 잘리던 문제 — 웹 모니터링 전송 후 메모리를 비우는 과정에서 환자 연결 판정에 쓰이는 최근 구간까지 지워져 "환자가 나갔다" 로 오판하던 것. 이제 판정에 필요한 구간은 보존한다. 파일이 5분마다 끊기고 그때마다 수 초가 유실되던 증상이 사라진다. 1.19.7 Added: GE Corometrics(분만 태아감시) 모체 vitals 지원 — 기존엔 태아 심박(HR1/HR2)·자궁수축(UACT)만 기록됐으나 이제 같은 장비가 함께 보내는 모체 SpO2/맥박(PR)/NIBP(수축·이완·평균)도 한 파일에 기록. Fixed: 프로세스 종료(서비스 매니저의 정상 종료 요청, SIGTERM) 시 즉시 종료되지 않고 강제 종료되던 문제 — 이제 SIGTERM 을 받아 녹화 버퍼를 flush 하고 스레드를 정리한 뒤 정상 종료. 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.8 | 2026-08-14 | 1.19.8 Fixed: 자동 파일 자르기(CUT_FILE) 를 쓰는 베드에서 환자가 계속 연결돼 있는데도 녹화 파일이 PT_WAITING_TIME(기본 5분) 마다 잘리던 문제 — 웹 모니터링 전송 후 메모리를 비우는 과정에서 환자 연결 판정에 쓰이는 최근 구간까지 지워져 "환자가 나갔다" 로 오판하던 것. 이제 판정에 필요한 구간은 보존한다. 파일이 5분마다 끊기고 그때마다 수 초가 유실되던 증상이 사라진다. 1.19.7 Added: GE Corometrics(분만 태아감시) 모체 vitals 지원 — 기존엔 태아 심박(HR1/HR2)·자궁수축(UACT)만 기록됐으나 이제 같은 장비가 함께 보내는 모체 SpO2/맥박(PR)/NIBP(수축·이완·평균)도 한 파일에 기록. Fixed: 프로세스 종료(서비스 매니저의 정상 종료 요청, SIGTERM) 시 즉시 종료되지 않고 강제 종료되던 문제 — 이제 SIGTERM 을 받아 녹화 버퍼를 flush 하고 스레드를 정리한 뒤 정상 종료. 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.7 | 2026-08-12 | 1.19.7 Added: GE Corometrics(분만 태아감시) 모체 vitals 지원 — 기존엔 태아 심박(HR1/HR2)·자궁수축(UACT)만 기록됐으나 이제 같은 장비가 함께 보내는 모체 SpO2/맥박(PR)/NIBP(수축·이완·평균)도 한 파일에 기록. Fixed: 프로세스 종료(서비스 매니저의 정상 종료 요청, SIGTERM) 시 즉시 종료되지 않고 강제 종료되던 문제 — 이제 SIGTERM 을 받아 녹화 버퍼를 flush 하고 스레드를 정리한 뒤 정상 종료. 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.5 | 2026-08-06 | 1.19.5 Fixed: CUT_HOURLY 가 동작하지 않는 것처럼 보이던 문제의 실체 — k3s 컨테이너에서 기본 저장 경로가 $HOME/recordings(임시 레이어)로 잡혀, 매일 자정 재시작 때 업로드 안 된 하루치 녹화가 통째로 사라짐. hostPath 마운트 /recordings 가 있으면 그것을 기본 저장 경로로 사용. Fixed: 종료(daily restart) 시 멀티베드 TCP 노드가 terminate(exit=133)/SIGSEGV(139) 로 죽던 문제. Linux finalize 가 device 스레드를 정지하지 않고 반환해 exit() 전역 소멸자가 살아있는 PortRouter accept_thread 를 파괴하며 std::terminate — DISPLAY::quiesce_threads + PortRouter::shutdown 으로 정리 후 종료 (코어 백트레이스로 확정, 로컬 재현 후 수정 검증). Fixed: Link+/Agilia 파서가 시리얼 노이즈(size byte 0 또는 헤더 없는 잡음 8byte)에 역전된 반복자 범위로 vector 를 생성하다 std::length_error 로 죽던 문제(or-110) — sentinel 상태 파싱 봉쇄 + 재동기화. Fixed: FILENAME_TEMPLATE 의 %Y(4자리 연도)가 "0126" 으로 나오던 문제 (tm_year+1900 누락). 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.4 | 2026-08-05 | 1.19.4 Fixed: Link+ 등 재접속이 잦은 장비에서 시리얼 포트를 재오픈할 때 "terminate called without an active exception" 으로 프로세스가 죽던 문제(exit=133). reading_thread_func 이 스스로 반환해도 std::thread 객체는 joinable 로 남는데, open() 이 join 없이 새 스레드를 대입해 std::terminate 가 호출됨 → 스레드 생성 전에 이전 reading/parsing 스레드를 join 후 재생성. Fixed: headless/AUTO_DETECT 모드에서 실제 장비가 없을 때 Demo 장비가 생성돼 가짜 vital 트랙 38종이 업로드되던 문제 → --console 또는 AUTO_DETECT 시 Demo 미생성. 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.3 | 2026-08-04 | 1.19.3 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스가 SIGSEGV 로 죽던 문제(accept_handoff 가 replace 시 옛 소켓을 즉시 close 해 recv 중인 reader 와 경합, fd 번호 재사용). tcp reader 를 poll(200ms) 게이트 뒤의 recv 로 재구성하고 replace 는 shutdown 후 소유권을 reader/close 로 단일화. SNUH 회복실 VRN 15대에서 재현. Fixed: ICU 등 Intellivue 다수 베드에서 반복되던 SIGSEGV(139) 두 계열의 use-after-free. (1) DISPLAY::add_rec 이 레코드를 트랙에 push 한 뒤 저장했는데, push 직후 p 가 다른 스레드에 공유되고 Linux 의 메모리 보존 루프(600초 이전 레코드 delete)가 시계 어긋난 모니터에서 방금 넣은 레코드를 즉시 evict 해, 이어지는 p->save() 가 해제된 메모리에 가상 호출 → 저장을 push 앞으로 옮겨 공유 후 p 미접근. (2) DISPLAY::del_trk 이 트랙을 목록에서 뺀 직후 raw delete 해, 파싱 스레드의 캐시 포인터·장비 이름맵·recorded_trks 가 죽은 포인터를 참조 (특히 IAP 채널변경의 del 직후 add 가 해제 포인터 반환) → DEVICE 규칙처럼 살아있는 동안 삭제 금지, ~DISPLAY 에서 일괄 해제. picu-11 코어 덤프로 크래시 지점(add_rec vtable 간접호출) 확정. SNUH fleet ~100 노드 실배포에서 기대 크래시 다수 대비 실제 0, 미배포 노드는 계속 크래시. Fixed: Linux from_utf16 이 locale 의존·비스레드안전 wcstombs 를 쓰고 UTF-16 을 UTF-32 wchar_t 로 오해석하던 문제 → 수동 UTF-8 인코더(서로게이트 페어 재조립, 무효값 U+FFFD)로 교체. Added: AUTO_DETECT=1(vr.conf) 시리얼 장비 자동 판별. 회선 측정(보레이트·프레임폭·패리티) → 후보 필터(3축 일치, 베드당 클래스 배타, 포트 사전확률, 판별캐시) → 트랙 수확량 경쟁. 드라이버 무수정, 미설정 시 유휴 대기라 기존 설정 무영향. SNUH 수술장·ICU 실측 오탐 0. | |
| 1.19.0 | 2026-08-03 | 1.19.0 Fixed: 여러 베드를 한 프로세스가 TCP 로 수집하는 구성에서 클라이언트 재접속이 잦으면 프로세스 전체가 SIGSEGV 로 죽던 문제. accept_handoff 가 교체(replace) 시 옛 소켓을 즉시 closesocket 했는데, m_sock_mtx 는 fd 번호만 보호할 뿐 fd 의 수명은 보호하지 않아 reader 가 그 fd 로 recv 중일 수 있었다. close 는 blocking recv 를 깨우지도 않고 fd 번호를 즉시 재사용 가능하게 만들어, 다음 접속이 같은 번호를 받으면 reader 가 남의 세션을 읽었다. SNUH 회복실(VRN 15대, 최다 베드는 하루 785회 재접속)에서 실제로 크래시 후 9시간 48분 전 베드 무수집. 수정 세 겹: (1) tcp reader 를 poll(POLLIN,200ms) 게이트 뒤의 recv 로 재구성 - recv 가 무기한 블로킹되지 않으므로 close/replace 가 늦어도 200ms 안에, shutdown 이 동반되면 즉시 감지된다. fd 에 O_NONBLOCK 은 걸지 않는다(같은 fd 로 송신하는 write_transport 의 부분 전송 방지). (2) replace 는 closesocket 대신 shutdown 만 하고 옛 fd 를 retired 목록에 넣으며, 닫기는 깨어난 reader 또는 Serial::close 가 정확히 한 번 수행 - fd 번호 재사용 창이 사라진다. (3) Serial::close 가 join 전에 shutdown 으로 reader 를 깨우고(RCVTIMEO 는 설정된 적이 없어 종전에는 피어가 끊을 때까지 join 이 매달렸다), m_sock 접근을 전부 락으로 감싼다. has_socket 의 무락 읽기와 udp_thread_func 의 이중 close 도 같은 계열이라 함께 수정. Added: AUTO_DETECT=1 (vr.conf) - 시리얼 포트에 무엇이 물려 있는지 스스로 알아내 즉시 수집을 시작한다. 판별용 프로브 바이트를 따로 두지 않고 후보 드라이버를 실제로 열어 유효 레코드 도달 여부로 판정하므로 장비별 판별 코드가 없고 드라이버는 무수정이다. 4단계: 회선 측정(커널 프레이밍/패리티/브레이크 카운터로 보레이트와 프레임 폭 확정) -> 패리티 추론(10비트 프레임의 7E1/7O1 대 8N1 모호성을 MSB 상관으로 해소) -> 후보 필터(보레이트+프레임폭+데이터비트 3축 일치, 베드당 환자모니터 1대/마취기 1대 배타, 포트 위치 사전확률, 지난 판별 결과 캐시) -> 남으면 트랙 수확량으로 경쟁. SNUH 수술장 9방 + ICU 3베드 실측에서 Intellivue/MedibusX/Datex-Ohmeda/Primus 를 오탐 0 으로 판별했고, 표준과 다른 포트에 꽂힌 장비(P3/P4 의 Intellivue)도 찾아냈다. AUTO_DETECT 가 없으면 스캐너는 유휴 대기만 하므로 기존 설정에 영향이 없다. | |
| 1.18.52 | 2026-07-31 | 1.18.52 Changed: VR 기본 운영이 k3s 로 이관됨에 따라 서버→VR 하향 제어 경로를 제거. VitalServer 로는 데이터를 일방 push 만 하고, websocket(socket.io) 으로 내려오던 원격 명령 update/restart/reboot/edit_conf/new_bed/del_bed/edit_bed/dt/add_event 를 더 이상 처리하지 않는다 (원격 재부팅·바이너리 교체·conf 덮어쓰기·베드 구성변경 봉쇄). 구현: sendweb_thread_func 의 socket.io 42 이벤트 dispatch 와 on_sio_msg 함수를 통째로 제거. engine.io ping(2->3) 및 ws 레벨 ping/pong 은 연결 유지를 위해 그대로 응답하므로 데이터 업링크·웹모니터링은 종전과 동일. 서버는 VR 버전을 알므로 52+ 로는 명령을 보내지 않고, 받아도 무시. Fixed: --console 모드 (MFC) 에서 stdout 이 이미 유효한 파이프/파일 (> log.txt, ssh 파이프, cmd 상속 핸들) 인데도 무조건 _open_osfhandle+_dup2 로 재배선하면서 그 OS 핸들을 닫아버려, 초기화 이후 모든 로그가 조용히 사라지던 문제 (콘솔 모드 서버 배포에서 진단 불능의 원인, 1.18.7 부터 잠재). stdout 핸들이 유효하면 (GetFileType != UNKNOWN) 재배선하지 않고, 핸들이 없을 때만 (Explorer/schtasks 실행) AttachConsole 로 부모 콘솔에 붙인다. 실측: 종전 초기화 후 로그 침묵 -> 수정 후 지속 출력. (앞서 1.18.51 의 VRQt qDebug->stdout 통일과 함께 세 빌드의 --debug 로그 일관성 완성.) | |
| 1.18.51 | 2026-07-30 | 1.18.51 Fixed: VRQt (Qt 빌드) 에서 --debug 옵션을 켜도 콘솔에 로그가 전혀 출력되지 않던 문제. 원인: Qt 빌드의 TRACE 가 qDebug() 로 매핑되어 있었는데 (UTIL.h, 1.14.4 부터), Windows 에서 Qt 로깅은 GUI 앱이면 콘솔/파이프가 아니라 디버거 (OutputDebugString) 로만 보내므로 VRQt 만 --debug 콘솔 출력이 구조적으로 불가능했다 (MFC 는 stdafx.h 의 #undef TRACE 로 printf 버전을 사용해 정상). 수정: TRACE 를 세 빌드 (MFC/Qt/CLI) 공통 fprintf(stdout)+fflush 로 통일 - qDebug 직접 사용처는 코드에 없음을 확인. 이제 --debug 동작과 리다이렉트 (> log.txt) 가 전 빌드에서 일관되고, cmd 대화형 실행 시에는 기존 AttachConsole/AllocConsole 로직 (1.18.30) 이 콘솔을 만들어 준다. terapc 실측 검증: 종전 VRQt stdout 0 chars → 수정 후 4,690 chars 정상 출력, Vital.exe (MFC) 는 종전과 동일 정상. 추가 방어: --debug 의 콘솔 확보 검사에 GetFileType 추가 - 일부 런처/셸이 non-NULL 무효 stdout 핸들을 상속시키면 NULL 검사만으로는 AttachConsole 을 건너뛰어 로그가 사라질 수 있던 엣지 케이스 (MFC/Qt 공통). | |
| 1.18.50 | 2026-07-30 | 1.18.50 Fixed: 장비 연결이 한번 끊어지면 다시 붙지 않던 문제 (Link+ 현장 리포트, Intellivue 늦은 전원-온과 공통 근본 원인). 근본 원인: DEVICE::open() 의 초기화 호출 set_rx_time(0) 이 값 0 인데도 OPENING→ACTIVE 로 승격시키고 m_dtStatus 까지 0 으로 만들어, (1) 모든 장비가 open 즉시 가짜-ACTIVE (OPENING 상태가 사실상 부재), (2) restart 워치독의 dtlast(rx ?: status_time) 가 0 → read_timeout 조건이 영구 거짓 → 재연결 시 상대 무응답이면 (Link+ rack 재부팅/세션 잔존, 세션 프레임 1회 전송 후 무응답) 재시도 없이 영구 고착. 1.18.35 이하의 rec_timeout (상태 무관 60s 강제 재시작) 이 이 잠재 버그를 우연히 가리고 있었고 1.18.36 전환으로 노출된 것. 수정 3건: (1) set_rx_time 은 실수신 (dt>0) 일 때만 ACTIVE 승격 - 상태 표시도 정직해짐 (open 후 첫 수신까지 "con"), (2) restart 워치독에 OPENING-stuck 분기 추가 - open 후 read_timeout 내 첫 수신이 없으면 재시작해 협상 프레임 재전송, tx_time>0 (protocol-initiator) 조건으로 passive/keyword-sub 디바이스는 제외해 유휴 churn 없음, (3) tx_time 을 모든 transport (socket/fd/COM) 에서 갱신 (기존 Windows COM 만). 영향 장비: Link+/Agilia, Intellivue, S5, Datex, Hamilton 등 open 시 세션/assoc 을 시작하는 모든 protocol-initiator. E2E 검증: 무응답 서버 재현에서 종전 접속 1회 후 영구 침묵 → 수정 후 30초마다 재접속 반복, 장비 복구 시 자동 연결. Mindray 멀티베드 회귀 테스트 PASS (ACK 75/75, tier1 라우팅, probe 거절, stuck 오발동 0). Intellivue 1.18.48 assoc-retry (5s) 는 유지 - 이번 워치독은 30s 백스톱으로 공존. | |
| 1.18.49 | 2026-07-29 | 1.18.49 Fixed: (1) Mindray eGateway HL7 멀티베드 (catch-all + #키워드 sub) 에서 전 베드가 readonly=1 이면 데이터가 전혀 수집되지 않고 게이트웨이가 수초마다 재접속하던 문제. 원인 셋 - (a) readonly catch-all 이 open_socket 에서 조기 반환해 reader/parser 스레드가 아예 생성되지 않음 (아무도 안 읽는 소켓에 데이터가 쌓이다 게이트웨이가 연결 재수립 반복; PortRouter 리팩토링 시 readonly 체크가 blocking accept 앞으로 이동하며 생긴 1.18.36 회귀 - 1.18.31 은 accept 가 먼저라 우연히 동작), (b) readonly 가 HL7 ACK 송신까지 차단 (eGateway 는 메시지마다 ACK 요구), (c) sibling 베드 메시지는 broadcast 후 ACK 없이 return. 수정 - readonly 는 송신만 차단하고 수신은 정상화 (broadcast-only keyword sub 만 소켓 미보유), write_protocol 경로 신설로 ACK 는 readonly 우회, catch-all 이 sibling 메시지도 대신 ACK (R01/R40 타입 구분), broadcast 수신 sub 의 rx_time 갱신으로 상태표시 정상화, PortRouter takeover 는 기존 소켓이 stale (10초+ 무수신) 일 때만 허용해 게이트웨이 health-probe 접속이 live 데이터 소켓을 뺏지 못하게 함. 시뮬레이터 E2E 검증: ORU 75건 -> ACK 75건 (100%), probe 5회 전부 무해 거절, SU-2/SU-4 tier1 라우팅 정상. (2) Getinge/MAQUET Flow-i (FCI 프로토콜) tidal volume 이 실제의 절반으로 기록되던 문제. 원인: FCI 프로토콜 v0005 부터 TV 채널 (101/102) gain 이 0.1 -> 0.2 로 변경됐는데 (Flow-i communication interface v0007 문서), VR 은 open 시 SPVE0005 로 v5 를 요청하면서 파싱은 구버전 gain 0.1 을 적용. 병원 vital 파일 실측 (Flow-i 직결 TV 201.4/188.2 vs 필립스 경유 403.0/376.6, MV 는 정상) 으로 검증 - TV_INSP/TV_EXP gain 0.2 적용. MV 는 v3+ 스케일 (0.01) 이 이미 반영돼 있어 변경 없음. (3) Flow-i 설정 파라미터 미지원 (m_ch_settings 빈 맵) - v5+ 에 존재하는 수치 설정 채널 11종 등록: SET_RR(411), SET_INSP_TM(413), SET_IE(418), SET_MV(421), SET_PC(423), SET_PEEP(424), SET_FRESH_FLOW(428), SET_FRESH_O2(430), SET_AGENT_PERC(434), SET_PS(436), SET_SIMV_RR(437). gain 은 문서 표기 그대로. 주의: Set Tidal Volume (채널 422) 은 FCI v5 에서 프로토콜 자체에서 삭제되어 직접 수신 불가 - 볼륨 모드에서 SET_MV/SET_RR 로 파생 가능. 설정 채널은 문서 기반 구현으로 실기 검증 대기 상태. | |
| 1.18.48 | 2026-07-24 | 1.18.48 Fixed: VR 이 먼저 실행된 상태에서 Philips Intellivue 모니터를 나중에 켜면 웨이브가 영원히 수집되지 않던 문제 (VR 재시작 전까지 모니터로 아무 요청도 안 나감). 원인: Intellivue::open() 은 assoc_req 를 딱 한 번 보내는데, 시리얼(MIB)/UDP 는 모니터가 꺼져 있어도 포트가 열리므로 이 요청이 응답 없이 사라진다. Philips 는 자기가 먼저 말을 걸지 않는 프로토콜이고, 첫 수신이 있어야 디바이스 상태가 OPENING→ACTIVE 로 승격되는데 수신이 없으니 OPENING 에 고정 - DEVICE::restart_thread_func 워치독은 is_closed(재오픈)/is_active(read_timeout 30s) 두 분기만 있어 OPENING 은 어느 쪽에도 안 걸림 → assoc 재전송도 재시작도 영원히 없음. 수정: Intellivue 에 assoc 재시도 스레드 추가 - 세션 확립(MDS_CREAT 수신, m_bSessionUp atomic 플래그) 전까지 5초 간격으로 assoc_req 재전송, 최근 5초 내 수신이 있으면(assoc 진행 중) 재전송 보류. 모니터를 나중에 켜도 최대 5초 내 연결. 세션 확립 후에는 재시도 루프 종료 - 기존 동작(AB_SPDU 시 재연결, read_timeout 재시작)과 충돌 없음. 모니터가 켜져 있던 기존 시나리오는 첫 assoc_req 에 바로 응답하므로 동작 변화 없음 - 0-impact. | |
| 1.18.47 | 2026-07-24 | 1.18.47 Fixed: 파일 업로드(및 Http 클래스를 쓰는 모든 경로 - upload_vital, getvr 업데이트체크, ADT 조회, 필터서버)가 SERVER_IP/UPLOAD_SERVER_IP 에 "https://host:port" 풀 URL 형식을 넣으면 "Couldn't resolve host name" 으로 전부 실패하던 문제. 원인은 src/Https.cpp 의 Http 생성자 - 1.18.46 의 wss.cpp 수정과 동일한 버그의 나머지 반쪽. 첫 콜론을 포트 구분자로 오인해 "https://eicuvital.ywmc.or.kr:4432" 입력 시 m_strHost 가 문자 그대로 "https" 가 되어 DNS 조회 실패 (웹모니터링 wss 는 1.18.46 에서 이미 수정되어 정상, 파일 업로드만 실패하는 비대칭 발생 - 현장에서는 업로드 루프의 실패시 break 정책과 겹쳐 "베드명에 # 가 있으면 전송이 안 된다" 로 오인 관찰됨. 실제로는 # 파일이 디렉토리 순서상 항상 첫 시도라 거기서 host 오류로 중단 → 뒤 파일 전체가 막힌 것. 베드명 특수문자는 업로드 URL 과 무관). 수정: Http 생성자에 wss 와 동일한 선행 스킴(https/http) 파싱 추가 - 스킴이 TLS 여부를 결정 (포트 무관, 4432 같은 커스텀 포트에서 https 정상), "https://host" 포트 생략 시 443, 경로 붙은 입력도 host 만 추출. 기존 "host" / "host:443" 형식은 종전과 완전 동일 동작 - 0-impact. Windows(WinINet)/Linux(curl) 공통 생성자라 모든 플랫폼에 적용. | |
| 1.18.46 | 2026-07-23 | 1.18.46 Fixed: WSSClient 가 "https/wss 스킴 + 비표준 포트" 조합을 제대로 처리하지 못하던 문제 (커스텀 포트의 wss 서버에 연결 불가). 원인은 src/wss.cpp 의 WSSClient 생성자 두 곳 - (1) SERVER_IP 파싱이 첫 콜론을 포트 구분자로 써서 "https://host:4432" 를 넣으면 "https" 의 콜론을 먼저 잡아 m_host 가 "https" 가 되고 포트 파싱이 깨져 getaddrinfo 실패, (2) m_secure = (m_port == 443) 로 TLS 여부를 오직 포트 443 여부로만 판단해 4432 같은 커스텀 포트는 무조건 평문 ws 로 붙어 wss 전용 서버에서 실패. 수정: 생성자가 선행 URL 스킴(https/wss/http/ws)을 먼저 파싱해 스킴이 있으면 스킴이 TLS 를 결정(포트 무관)하고, 스킴이 없으면 기존 포트 443 휴리스틱을 유지(하위호환). 이제 SERVER_IP=https://eicuvital.ywmc.or.kr:4432 (또는 wss://...) 형식이 비표준 포트에서도 wss 로 정상 연결되며, SNI/인증서 검증은 도메인(m_host)을 쓰므로 와일드카드 인증서도 정상 매칭. 기존 host / host:443 설정은 종전대로 secure 유지 - 0-impact. | |
| 1.18.45 | 2026-06-16 | 1.18.45 Fixed: Windows VR 에서 사용자가 외부에서 vr.conf 를 멀티베드 설정으로 교체 후 VR 을 재실행하면, 첫 실행에서는 멀티탭이 정상 생성되지만 다음 실행에서 모든 탭이 사라지던 문제. 원인은 VRApp.cpp:1110 의 VRCODE 미설정 분기 — 새 vr.conf 가 VRCODE 라인을 포함하지 않으면 load_settings() 가 신규 VRCODE 를 issue_code() 로 발급하고 즉시 save_settings() 를 호출하는데, 이 시점은 load_settings() 가 파싱한 베드들이 로컬 newbeds 벡터에만 있고 아직 m_pdisps 로 옮겨지기 전 (set_beds(newbeds) 는 initialize() 가 load_settings 반환 후 라인 1328 에서 호출). 결과: save_settings() 가 빈 m_pdisps 를 iterate 하며 vr.conf 를 글로벌 설정만으로 덮어써 모든 [BED/...] 섹션이 디스크에서 사라짐. 첫 실행은 메모리의 newbeds 로 UI 멀티탭이 그려져 정상으로 보이지만 (사용자가 "멀티탭은 정상적으로 생성되지만" 으로 표현), VR 종료/재실행 시 디스크 conf 에는 BED 섹션이 없으므로 다음 load_settings 가 빈 베드 리스트를 반환 → 탭 전부 사라짐. 수정: VRApp.h 에 m_vrcode_newly_issued bool 플래그 추가, load_settings 의 즉시 save 를 플래그 set 만 으로 교체, initialize() 의 set_beds(newbeds) 직후 (m_pdisps 가 채워진 시점) 플래그가 켜져 있으면 한 번만 save_settings() 호출 → BED 섹션이 정상 직렬화됨. Linux/Pi 분기 (m_serial_number 가 채워져 있는 경우) 는 원래도 save 안 했으므로 영향 없음, VRCODE 가 이미 conf 에 있는 모든 기존 deployment 도 분기 미진입으로 0-impact. m_conf_from_stdin / m_bDebug 모드는 save_settings 내부에서 이미 early return 이라 그대로 안전. MFC + Qt 빌드 모두 통과 (이번 변경 관련 0 warning). | |
| 1.18.44 | 2026-06-12 | 1.18.44: vr.conf FHIR=1 모드 추가 (자체 FHIR 수신서버 대상 실시간 푸시) + VS 2026 / 새 OpenSSL 인스톨러 빌드 환경 호환 shim. (1) sendweb_thread_func 의 기존 HL7/JSON 양분 분기 (VRApp.cpp:2466) 옆에 bFHIR 분기 추가. 1초 tick 마다 한 vrcode 의 모든 방 Observation 을 하나의 FHIR R4 collection Bundle 로 묶어 기존 WSS 채널로 push (gzip + WSSClient 인프라 그대로 재사용). MLLP 같은 신규 transport 안 도입 — FHIR R5 Subscription 의 websocket channel type 과 동형이라 표준 부합. (2) DISPLAY::get_fhir + TRACK::get_fhir 신규. v2 (vitaldb-fhir-v2) 와 동일한 reference-only 패턴 (Patient.id = m_patient_id, Encounter.id = vrcode-bedname-startsec, Observation.subject/encounter = relative reference) + hybrid keyframe: 첫 tick / m_patient_id 변경 / get_start_time() 변경 / 5분 경과 중 하나라도 발생 시 다음 tick Bundle 에 Patient + Encounter resource 를 inline. 그 외 tick 은 Observation 만. 수신서버가 도중에 새로 connect 해도 최대 5분 안에 자동 동기화 (keyframe heartbeat). DISPLAY 에 m_fhir_pt_sent_at/m_fhir_pt_last_id/m_fhir_enc_last_start 3개 멤버 추가 (m_setting_sent 패턴 그대로 per-display). (3) Observation 인코딩: NUM 은 effectiveDateTime + valueQuantity 각 데이터 포인트당 한 entry, WAV 은 effectivePeriod + valueSampledData (period=1000/srate ms, factor=1, origin=0, data=공백구분 floats) 한 entry. CO2/AWP wav 는 기존 get_json 처럼 25Hz 다운샘플 유지. NUM 은 m_montype 기반 LOINC 매핑 15개 등록 (ECG_HR/PLETH_HR=8867-4, PLETH_SPO2=59408-5, NIBP/IABP SBP/DBP/MBP=8480-6/8462-4/8478-0, BT=8310-5, RR=9279-1, etCO2=19891-1, CVP=8556-0, FETAL_HR=11627-3) + VR 자체 코드 시스템 (https://vitaldb.net/fhir/CodeSystem/track, code = dname/tname) 항상 동반. m_patient_id 미설정 (PATIENT_ID_UNSET) 시 anon-vrcode-bedname identifier 로 fallback. (4) Bundle wrapper: VRApp 가 방별 entries 를 콤마로 합쳐 {resourceType:Bundle, type:collection, timestamp:iso8601Z, entry:[...]} 로 wrap, 기존 gzip → WSS pipeline 동일. ISO 8601 UTC 헬퍼는 std::gmtime_s/gmtime_r 인라인. FHIR id 안전화 (A-Za-z0-9-.) 64자 제한 lambda 각 함수에 inline. (5) 빌드 환경 호환성: VRQt/CMakeLists.txt 에 _SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS 추가 — VS 2026 (MSVC 14.51+) 의 STL 이 <experimental/coroutine> deprecation 을 warning → hard static_assert 로 바꾼 것이 cppwinrt 의 winrt::* (Serial.cpp 의 Bluetooth LE) 를 끌어오는 모든 TU 에서 컴파일 실패 시켜서, build_mfc.py 가 이미 CL env 로 주입하던 것과 동치 shim 을 Qt 쪽에도. (6) VRQt/CMakeLists.txt 의 OpenSSL link 경로 시프트. 새 OpenSSL 인스톨러가 libssl_static.lib / libcrypto_static.lib 를 lib/VC/x64/{MT,MTd}/ 에 두고 v1.x 의 libssl64MT.lib / libcrypto64MT.lib 네이밍과 lib/VC/static 경로를 깬다. build_mfc.py 의 %LOCALAPPDATA%/openssl-compat 패턴 (옛 이름으로 복사한 사본 폴더) 을 그대로 따라 CMakeLists 도 그 폴더가 있으면 우선, 없으면 옛 lib/VC/static 으로 fallback. 부트스트랩 스크립트는 build_mfc.py 의 docstring 참조 (한 줄). MFC + Qt + RPi + Ubuntu CLI + AppImage 빌드 모두 통과. 동작 영향: vr.conf 에 FHIR=1 추가 안 한 기존 deployment 는 sendweb 가 종전 JSON 모드 그대로 (HL7=1 만 분기보다 우선) — 0-impact migration. | |
| 1.18.43 | 2026-05-21 | 1.18.43: 멀티베드 console (PACU 10베드 Pi) syslog noise 80,500/hr → ~1,400/hr (-98.3%), cpu_usage 측정 1.16.12 (9f26e36) 이후 8년치 영구 -1 버그 수정, 멀티베드 환경 베드 식별용 [bedname / devicename] 로그 prefix 도입. 동기는 PACU production Pi (1.18.40) 4일치 syslog 분석 — 1.24M 라인 중 vr 라인 836K, idle 8시간 평균 시간당 80,500 라인 (베드 10 × 1초마다 "Waiting for next patient" × syslog 2x 중복). (1) VRApp.cpp:2696-2710 timer_thread_func 의 cpu usage 계산이 `if (totalcpu && m_cpu_total_last && totalcpu > m_cpu_total_last)` 가드 안에서만 m_cpu_total_last 갱신해 첫 호출 시 m_cpu_total_last=0 → else 분기 → -1 → m_cpu_total_last 갱신 안 됨 → 영원히 false → 영원히 -1 데드락. PACU 1,048 샘플 100% -1 실측 확인. m_cpu_total_last/m_cpu_used_last 갱신을 if 밖으로 빼서 첫 호출만 baseline 으로 -1, 두 번째 (60s 후) 부터 정상 측정. /proc/stat 의 iowait/irq/softirq 까지 7개 필드 fscanf 로 정확도 ±5% 개선. 부수: 같은 함수의 cpu_temp 가 `temp / 1000` int 나눗셈으로 소수점 절단되어 PACU 측정값이 정수 50/51/...,/58 9종만 출력 (millidegree 정밀도 손실) → `temp / 1000.0f` 로 fix. ram 계산은 `(totalram + totalswap) * mem_unit` 결과에 다시 mem_unit 곱하던 차원 혼용 (Linux 가 mem_unit=1 이라 우연히 정상 동작했지만 dimensionally wrong) → `(units1 + units2) * mem_unit` 로 정정 + underflow 가드. (2) DISPLAY::log(string/printf) + DEVICE::log(string/printf) 멤버 추가. DISPLAY::log 는 `[bedname] msg`, DEVICE::log 는 m_pdisp 위임 없이 직접 `[bedname / devicename] msg` 한 줄 prefix (nested `[bed] [dev]` 보다 가독성 우선). m_pdisp 또는 bedname null 케이스 safe fallback. (3) show_msg 의 console path TRACE 를 log() 로 교체 → "Waiting for next patient" 자동 [bedname] prefix. ADT patient id unset 도 log() 로. (4) 이미 수동으로 get_bedname() 을 인자로 넘기던 8군데 (DISPLAY ptcon_thread_func / add_trk / add_dev / del_dev / cut-recording, Device add_rec 콜백, FILT stop, VRApp add_tab/del_tab) 를 log() 로 전환 — 이중 prefix 방지 + 일관성. Serial::reading_thread / tcp_thread / open_socket waiting / timeout / interrupted / got-client 6군데 DEVICE::log() 전환 → `[bedname / Intellivue]` 등 자동 prefix, 멀티탭 환경에서 어느 베드 어느 device 출처인지 즉시 식별. (5) wss 재시도 폭주 throttle. WSSClient 에 m_verbose (debug step 로그 게이트) + m_dt_last_err_log (60s window) 추가. connect() 내부 step-by-step TRACE 4개 ("connecting" / "connect error" / "connected") 는 m_verbose 가드, 최종 "wss connect error" 는 60s window throttle (첫 줄 즉시, 그 다음 60s 1회). connect 성공 path 에서 throttle reset 으로 다음 disconnect → 첫 에러 즉시 로깅. sendweb_thread_func 의 "connecting websocket server" 호출자 라인도 같은 60s window, m_bDebug 면 verbose 활성화. PACU idle 8h 측정 7,024/hr (4종 × 1,756) → 약 60/hr (-99%). (6) "Waiting for next patient" / "ADT patient id unset" 의 60s throttle 자체는 feature/wav-mem-retention 브랜치 ptcon_thread_func 재구성 (b75928f 시리즈) 에서 이미 들어가 있어 이 릴리즈에 자동 포함. 종합 영향: idle 시간당 syslog 라인 80,500 → ~1,400 (-98.3%), active 시간당 14,900 → ~700 (-95%). 진단 측면: cpu_usage 가 부활하면서 웹모니터링 json 의 cpu_usage 필드도 한 번도 안 채워지던 게 정상 전송 (VRApp.cpp:2499 의 `if (m_cpu_usage > 0)` 가드가 영원히 false 였음). 별건: syslog 라인 2× 중복은 application 측이 아니라 rsyslog imuxsock + imjournal + journald.ForwardToSyslog=yes 의 Debian Bookworm 전형 misconfig — admin 측 fix (rsyslog.conf 에서 imjournal 주석 처리 + restart) 로 추가 50% 감소 가능 (별도 문서). MFC + Qt + Pi (RPI64) + Ubuntu CLI 빌드 모두 통과. | |
| 1.18.41 | 2026-05-13 | 1.18.41 Hotfix: TCP-server protocol-initiator device (Intellivue/S5/Datex/Hamilton/MPS/Agilia/IAP) 가 open() 직후 보내는 첫 패킷이 wire 에 안 나가던 회귀. 1.18.34 의 PortRouter refactor 가 Serial::open_socket() 의 8초 handoff blocking 을 제거하고 비동기 모델로 전환했는데, protocol-initiator 의 open() 코드가 m_sock=INVALID 상태에서 write 를 시도해 silent fail (Linux 는 m_fd=0 이라 false 반환). Intellivue 케이스 production journald 의 Pi → ESP TX 700K~3M bytes 는 monitor 측 abort 로 늦게 트리거된 AB_SPDU_SI recovery assoc_req 후 request_thread 의 정상 polling 트래픽이지만 일부 베드 (abort 없는 monitor) 는 recovery 도 안 되어 영구 silent (RX 300 bytes). 수정 방향: open() 의 옛 contract ("return 시점에 통신 가능") 복구. (1) Serial::open_socket() TCP-server 분기가 PortRouter::subscribe 후 m_sock_cv 로 첫 client handoff 까지 block (timeout = device read_timeout, 기본 30s) — 옛 모델 그대로. lock_and_open 이 false 면 다음 tick 에서 retry. close 신호로 abort 가능. (2) Serial::close() 는 PortRouter::unsubscribe 안 함 — subscribe 는 device 생애 동안 sticky 라 read_timeout 으로 인한 close→re-open 사이클 동안 listen socket 이 유지되어 1.18.29/1.18.34 의 anti-churn 이익 보존. unsubscribe 는 ~Serial() 에서만. (3) Per-device lifecycle thread (DEVICE::m_thread_restart): 각 device 가 자기 open/close/timeout 재시작을 별도 thread 에서 관리. 옛 모델의 DISPLAY::bed_manager 가 베드의 모든 device 를 sequential iterate 하던 구조에선 한 device 의 blocking open() 이 같은 베드의 다른 device 의 lifecycle 을 차단했는데, per-device thread 로 분리하여 멀티 device 베드도 병렬 진행. add_dev 에서 start_lifecycle, del_dev / ~DISPLAY 에서 stop_lifecycle. (4) Serial::tcp_thread_func 단순화 — outer m_sock_cv wait 패턴 (1.18.34/1.18.39 가 추가했던 것) 제거. open_socket 이 m_sock valid 보장하므로 reader thread 시작 시점에 simple inner recv loop 만. accept_handoff 의 replace (zombie cleanup) 케이스는 recv error 후 m_sock 변경 확인 + retry 로 처리. (5) reading_thread_func 의 `else if (m_sock != INVALID_SOCKET) tcp_thread_func();` 가드 복원 (1.18.39 가 제거). (6) Serial::write 의 silent-fail 케이스에 WARN trace 추가 — 미래 동종 회귀 즉시 노출. (7) PortRouter::subscribe 가 같은 (port, type) 에서 같은 IP filter 또는 둘 이상 catch-all 발견 시 reject (config 실수 원천 차단, 옛 WARN-only 동작 강화). (8) DISPLAY::bed_manager_thread_func 의 restart 블록 제거 — ptcon + adt 만 담당. "Waiting for next patient" TRACE 가 Linux/console 에서 1초 → 60초마다 throttle (별개 journald 부풀림 fix). 영향 범위: Intellivue / S5 / Datex / Hamilton / MPS / Agilia / IAP TCP-server 모드 베드 전부. 1.18.34 이후 우회 방법은 race-win 케이스 (VRN 이 PortRouter::subscribe 직후 connect) 외엔 없었음. MFC + Qt + Pi (RPI64) + Ubuntu Qt + AppImage 빌드 모두 통과. | |
| 1.18.40 | 2026-05-12 | Hotfix: Drager Medibus 프로토콜 사용 베드에서 1.15.11 이후 vr.service 가 SIGSEGV 로 무한 재시작하던 문제. 1.15.10 (a8c7b2a) 의 "Sampling rate estimation" 리팩터링이 m_ptrk_awp/awf/co2 트랙을 add_trks() 에서의 eager 생성 (default srate=62.5Hz) 에서 realtime data 100 샘플 모인 후 lazy 생성으로 바꿨는데, Medibus 핸드셰이크 순서 (0x51 ICC → 0x52 DeviceID → 0x53 RealtimeConfig 응답 → 0x54 → realtime data 시작) 상 0x53 응답 핸들러 (Drager.cpp:295-307) 가 트랙들이 아직 nullptr 인 시점에 `ptrk->m_srate = 1000000.0/interval` 로 NULL 역참조 → 즉시 SEGV. 그 베드의 vr.service 가 systemd Restart=on-failure 로 재시작 → 다시 connect → 동일 0x53 패킷 받고 동일 SEGV → 무한 루프 (journald 로그상 restart counter 61 까지 누적, code=killed status=11/SEGV). 1.15.9 까진 트랙이 add_trks() 에서 생성되어 0x53 도착 시 항상 non-NULL 이라 안전했음. 수정: (a) add_trks() 에서 m_ptrk_awp/awf/co2 eager 생성 복원 (srate=62.5Hz default) — 0x53 는 ptrk->m_srate 만 갱신 (트랙 재생성 X). (b) on_received() 의 lazy creation + packet-timing srate 추정 블록 제거 — 0x53 가 권위 있는 srate 소스이고 packet 타이밍 기반 추정은 network jitter 에 약해 불안정. (c) 0x53 핸들러에 `if (interval > 0)` guard 추가 — 페이로드 깨졌을 때 div-by-zero / inf srate 방지. 영향 범위: Drager Primus / Apollo / Perseus / Fabius 등 Medibus 사용 마취기 전부. 우회 방법은 1.15.9 다운그레이드 외엔 없었음 (그 베드의 모든 데이터 유실 + 환자 모니터링 불가). MFC + Qt + Pi (RPI64) + Ubuntu Qt + AppImage 빌드 모두 통과. | |
| 1.18.39 | 2026-05-08 | Fixed: PortRouter (TCP server) 모드에서 reader thread 가 startup 시점에 죽어 client 데이터를 영구히 못 받던 문제. 1.18.34 (9bd252f) 의 PortRouter refactor 가 m_sock 세팅을 dispatch 시점으로 옮겼는데 reading_thread_func (Serial.cpp:354) 의 가드 `else if (m_sock != INVALID_SOCKET) tcp_thread_func();` 는 옛 모델 (open 시점 이미 connect 완료) 가정 그대로라 — 시작 직후 m_sock=INVALID 면 tcp_thread_func 진입 안 하고 reader thread 즉시 종료. 이후 VRN 이 connect → PortRouter::dispatch → accept_handoff 가 m_sock 채우고 m_sock_cv.notify_all() → 그러나 wait 중인 reader 가 없음 (이미 죽음) → kernel recv buffer 에만 데이터 적재. 증상은 ss -tn state established 의 Recv-Q 가 0 이 아닌 값 (수백 bytes) 으로 누적, 베드 화면에는 데이터 안 들어옴. 그 베드의 VRN 이 Serial::open() 의 PortRouter::subscribe 직후~reader thread spawn 사이의 짧은 윈도우 안에 connect 한 경우만 우연히 정상 동작 (race-win). 수정: 가드 제거 — TCP 분기에 항상 진입. tcp_thread_func 자체에 이미 m_sock_cv outer-wait 루프가 있어 m_sock 이 INVALID 인 동안 dispatch 가 깨워줄 때까지 안전하게 대기. Pi 핫스팟에 VRN 여러 대 (e.g. 10.42.0.21~25) 를 같은 (4343, Philips:Intellivue) 키로 묶고 IP filter 로 라우팅하는 시나리오에서 가장 자주 노출. Added: 진단 로그 보강 — PortRouter::accept_loop 의 accept 이벤트, dispatch 의 라우팅 결정 (ip_specific/catch_all subs 수, 어느 dev 가 받았는지), subscribe/unsubscribe 의 entry 생성/append/destroy, reading_thread / tcp_thread 의 시작·wait·wake 라이프사이클 — 이전엔 모두 m_bDebug 게이트 안에 있어서 production journald 에서 안 보였는데, 이제 항상 출력 (이번 같은 dispatch routing 문제 진단을 production 에서 바로 가능). MFC + Qt + Pi (RPI64) + Ubuntu Qt + AppImage 빌드 모두 통과. | |
| 1.18.38 | 2026-05-08 | Fixed: 웹모니터링 우클릭 → 디바이스 세팅 (websocket edit_bed 명령) 으로 베드의 device/filter 를 변경할 때 베드 이름이 매번 "_2" suffix 로 자동 리네임되어 vr.conf (Pi: /data/vr.conf) 에 잘못된 이름으로 저장되던 문제. 원인은 1.18.22 (cda8aa5) 에서 add_bed 에 추가된 bedname dedup ("최후 방어선" 으로 forward_frame auto-create / 수동 편집된 conf 의 중복 이름 처리용). edit_bed 핸들러는 옛 pdel 이 m_pdisps 에 그대로 있는 상태에서 같은 bedname 으로 new DISPLAY 를 add_bed 하고 그 다음 del_bed(pdel) 하는 순서였는데, add_bed 가 dup 감지해 새 pdisp 를 bedname_2 로 자동 리네임 → save_settings 가 [BED/bedname_2] 로 직렬화 → 사용자 입장에선 "설정 적용 안됨" 으로 보였다. 추가로 옛/새 DISPLAY 가 잠깐 m_pdisps 에 공존하면서 같은 device 포트를 동시에 들고 있으려 해 로그가 dup 으로 찍히는 부수 증상도 있었음. 수정: del_bed(pdel) 를 add_bed(pdisp) 보다 앞으로 이동 — 옛 베드의 device 포트가 깨끗이 release 된 뒤 새 pdisp 가 같은 포트를 reopen, dedup 발동 안 함. 다른 베드의 recording 은 영향 없음 (인메모리 hot swap 설계 유지). 부수: edit_bed 핸들러의 데드 코드 (`break` 뒤의 unreachable olddevs populate 루프 + `if (olddevs.find(name) == olddevs.end())` 항상-true 래퍼) 정리. edit_conf 와 save_settings 는 손대지 않음. MFC + Qt + Pi (RPI64) 빌드 모두 통과. | |
| 1.18.37 | 2026-05-08 | Refactored: 필터 다이얼로그의 python 런타임 — pyvital → openvital 0.3.0 로 교체 + 동봉 python.zip 슬림화. 핵심 동기 셋: (1) pyvital 0.6.0 이 tensorflow+torch+keras 를 hard-dep 으로 끌어들여 PyPI upgrade 가 사실상 깨짐, (2) 기존 zip 121 MB 안에 botocore/aiobotocore/s3fs/pyarrow/pandas/aiohttp 등 vitaldb 클라우드 업로드용 deps 가 ~250 MB unpacked 차지 — 필터 서버는 한 번도 쓴 적 없는 dead weight, (3) sanic 25.x 의 multi-process worker 가 openvital __main__.py 를 fork 후 재import 하면서 worker 가 module-top app instance 를 못 찾아 "Sanic app name not found" 로 죽음 — 단순 endpoint 2개 로컬호스트 서버에 async stack 자체가 과함. 해결: openvital 0.3.0 (commit b6f445b) 가 sanic 을 stdlib http.server (BaseHTTPRequestHandler + HTTPServer) 로 50 LOC 재작성 — wire protocol (GET / 필터목록 JSON, POST /<modname> gzip 본문) 동일해 VR 클라이언트 변경 0줄. 단일 스레드로 기존 sanic event loop semantics (cfgs/default_cfgs lock-free 변경) 보존. build_python_zip.py (NEW) 가 Python 3.11.9 embed + numpy + openecg + openvital (local checkout 우선, fallback PyPI) 만으로 27 MB zip 빌드 — 기존 121 MB 대비 78% 감소. ML 필터 (sv_dlapco/abp_hpi/ecg_classifier/ecg_beat_noise_detector) 는 [Hybrid 모델] 사용자가 Add filter 다이얼로그의 "Install ML filters" 버튼 누를 때 silent pip install openvital[all] (cmd 창 미노출 — VRApp::install_pip_package + run_pip_silent 이 자체 progress 다이얼로그에 stdout heartbeat 흘림). 버튼 라벨은 "Upgrade pyvital" → "Install ML filters" 로 의도 명시. openvital __main__.py 가 missing extras 를 graceful skip 하므로 base 환경에서도 11개 필터 (ECG QRS detector, HRV, MTWA, annotator, eeg_fft, nirs_cox, pkpd_3comp, pleth_dpop/ptt/pvi, resp_compliance) 즉시 사용 가능. Migration: 기존 사용자의 user_dir/python 폴더는 pyvital + 옛 deps 가 그대로 남아있고 vitaldb.net/python.zip 만 새로 올리면 (a) 기존 폴더 삭제 후 install_python 으로 새 zip 받으면 깔끔, (b) 폴더 유지 시 server 가 openvital 못 찾아 필터 다이얼로그 에러 → release note 에 명시 필요. MFC + Qt 양쪽 코드: VRGui.rc 의 IDC_UPGRADE 버튼 라벨 + DlgAddFilter.cpp 의 dist-info prefix 매칭 (pyvital- 8자 → openvital- 10자) + QtDlgAddFilter.cpp 도 동일 변경 + onUpgrade 가 cmd /k QProcess::startDetached 대신 theApp.install_pip_package("openvital[all]") 호출 (Linux Qt 빌드는 #ifdef _WIN32 가드). VRApp.cpp:318 의 python -m pyvital → -m openvital. install_pip_package 는 anonymous namespace 의 is_safe_pkg_spec 화이트리스트 검증 + run_pip_silent (CreateProcessA + CREATE_NO_WINDOW + 자식 stdout pipe) + CDlgProgress / QtDlgProgress 자체 progress UI. 빌드 검증: python -m openvital 이 GET / 로 11 필터 반환, sanic import 부재 확인. zip 27.5 MB. | |
| 1.18.36 | 2026-05-07 | Fixed: 3-파티션 레이아웃 (boot/rootfs/data) 에서 websocket edit_conf / update 명령이 read-only fs 로 실패하던 문제. vr.conf 는 /data 폴더가 있으면 /data/vr.conf 로 자동 정착 — get_conf_path() 가 write target 으로 결정, load_settings() 는 primary 가 없으면 legacy /boot/firmware/vr.conf 또는 /boot/vr.conf 에서 fallback read 후 다음 save 부터 primary 로 자동 이전 (마이그레이션 step 없음, legacy 파일은 read-only fs 라 무해). binary update (upgrade_now) 는 RAII BootRwGuard 가 dirname(module_path) 를 잠시 rw remount, scope 종료 시 sync() + MS_REMOUNT|MS_RDONLY 로 자동 ro 복귀 — vfat 노출 윈도우 최소화 + exception/return 어디서든 복구 보장. statvfs ST_RDONLY 검사로 이미 rw 인 fs (x86 dev) 에서는 no-op, non-Linux (macOS) 는 stub 으로 통과. save_settings() 의 silent EROFS 실패가 TRACE 로 가시화 (이전엔 무시되어 conf 수정 안 먹는 원인 추적 불가). 부수: PortRouter.cpp 가 commit 9bd252f 이후 CMakeLists 의 RPI sources 목록에서 누락되어 Pi 빌드 link 에러 — 추가. | |
| 1.18.35 | 2026-05-01 | Added: alarm parsing for Mindray HL7 (eGateway ORU^R40) and Nihon Kohden (NealTime HL7GW + ORF, BSM serial D-port, EGA UDP) - emits ALARM_STATUS / ALARM_MESSAGE / ALARM_PRIORITY tracks (same 3-track schema already used by Philips IntelliVue, GE Solar/Dash, GE/Datex S5); type info preserved via [T]/[A]/[ARR] message prefixes; Mindray facet 1/5/6/7 walker with 92-entry MDC+MNDRY alarm code lookup; NK HL7GW parses OBR-4=EVENT with arrhythmia/technical disambiguation by OBX-5; NK BSM serial decodes per-parameter * flags (edge-triggered to avoid 1Hz flooding) plus 32-bit Table 9 arrhythmia bitmap, D19 alarm-suspend nibble, and the VPC CODE byte; NK EGA UDP extracts Priority + Tech Alarm + Arrhythmia from JSON Vital Sign packets with edge-trigger cache for start/end inference; ACK^R40 reply added for Mindray alert messages; simulators gained periodic alarm cycling for end-to-end smoke testing | |
| 1.18.34 | 2026-04-27 | Added: Linux Desktop Qt GUI build (VRQt) - first GUI build for Linux distributed as a self-contained AppImage (~32MB, Qt6 runtime + libGL/libEGL/libGLX/libOpenGL/libGLESv2/libGLdispatch bundled inside) so users on minimal Ubuntu/Debian/Fedora can run it without installing Qt or GL system packages; tested on AWS clean Ubuntu 22.04 cloud image; build via build_ubuntu_appimage.py which is now invoked by deploy.py; Fixed: Linux Qt right-side device/filter panels rendered with white background below cards because the inner listWidget did not autoFillBackground from parent palette on Linux Qt (Windows/macOS implicitly inherit, X11/Wayland do not); explicit stylesheet on listWidget; Added: Linux installation section in user manuals (EN/KO/ZH/ES) covering AppImage chmod+run flow and dialout group note for serial/USB device access | |
| 1.18.33 | 2026-04-25 | Fixed: Demo device never started since 1.18.11 (bed_manager skipped empty port_name as passive but Demo has no port and still needs lock_and_open); Fixed: Qt fit-to-window button rendered at top-left (0,0) instead of right-aligned in bottom nav bar; Fixed: Qt crash when closing tab / deleting device / deleting filter - paint dereferenced freed pointer before panel timer rebuilt items (added MFC-style stale-pointer guard to QtDeviceItem and QtFilterItem); Added: macOS DMG main window now shown on launch (restoreWindowPos had no Mac/Linux branch); Added: macOS Homebrew dylibs (openssl) bundled in Contents/Frameworks; Added: macOS .app bundle now notarized + stapled (drag-to-Applications no longer needs xattr -cr on first launch); Changed: Qt MSI ~1MB smaller (dropped unused iconengines and imageformats plugins) |