VitalRecorder
A free software to record and analyze biosignal waveforms and vital signs from GE® (Solar 8000®, Dash, Bx50®), Philips® (Intellivue®), Drager® (Infinity®, Nihon Kohden® (PSM®, Primus®), Maquet® (Flow-i®, Servo-i®) ventilators, Fresenius Kabi® (Orchestra®, Agilia®, and Link+®), BBraun® syringe pumps, Medtronic® BIS®, Masimo® Root®, DataQ® ADCs, etc. (Full list of supported devices)The use of the VitalRecorder program is completely free. Download and try the latest version.
(Downloaded 182,273 times since Feb 2017)
OS:
Version:
View All Versions
1.19.5
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
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
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
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
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.19.5
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
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
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
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
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.19.5
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
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
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
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
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.40
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
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
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
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.19.5
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
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
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
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
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 로그 일관성 완성.)
Apple Silicon (arm64) only · Requires macOS 11 or later · Developer ID signed & notarized
Download Raspberry Pi Image
Preinstalled SD card image (.img.xz) · Flash to an SD card with Raspberry Pi Imager or balenaEtcher
If you use the VitalRecorder in your research, please cite the following publication:
Lee HC, Jung CW. VitalRecorder-a free research tool for automatic recording of
high-resolution time-synchronised physiological data from multiple anaesthesia devices. Sci Rep.
2018 Jan 24;8(1):1527. doi:
10.1038/s41598-018-20062-4. Available from: https://www.nature.com/articles/s41598-018-20062-4