fix(hospital): 재부착 확인의 기준을 좌우 확인 창(부착 위치와 같은 높이)으로

재부착 확인은 부착 위치(N+1cm)에서 재는데 기준은 확인 측정(N cm)이었다. 오프셋 자리는
설계상 지표가 다른 쪽이라(오프셋 위치는 측정하지 않는 이유) N cm 기준으로 대조하면
멀쩡한 재부착도 "채널 없음"이 될 수 있다. 좌우 확인 완료 순간의 마지막 프레임 창을
같은 조건(2.3MHz·c3·supine 분기)으로 mean-scan 해 잡힌 채널을 기준으로 쓴다 — 떼기
직전, 같은 높이의 신호다. 좌우를 안 하고 진행하면 종전대로 확인 측정(N cm)이 기준이다.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-30 17:06:01 +09:00
parent e939159026
commit 4d1f6ea680
@@ -207,6 +207,12 @@ fun AnchorAlignView(appState: AppState) {
var lrStreamIdx by remember { mutableIntStateOf(0) } var lrStreamIdx by remember { mutableIntStateOf(0) }
/** [처음부터 다시 정렬] 횟수 + 1. 스트리밍 기록의 `attempt` 열. */ /** [처음부터 다시 정렬] 횟수 + 1. 스트리밍 기록의 `attempt` 열. */
var lrAttempt by remember { mutableIntStateOf(1) } var lrAttempt by remember { mutableIntStateOf(1) }
/**
* 좌우 확인 완료 순간의 창(부착 위치 N+1cm)에서 잡힌 채널 CH0~3 — 재부착 확인의 기준.
* 확인 측정(N cm)이 아니라 **같은 높이**의 마지막 프레임으로 잡는다. 오프셋 자리는 설계상
* 지표가 다른 쪽이라 N cm 기준으로 대조하면 멀쩡한 재부착도 "채널 없음"이 될 수 있다.
*/
var lrRefChannels by remember { mutableStateOf<List<Boolean>?>(null) }
/** 간호사가 "좌우 확인 완료"를 누른 적이 있는가. 저장·다음 단계 안내에 쓴다. */ /** 간호사가 "좌우 확인 완료"를 누른 적이 있는가. 저장·다음 단계 안내에 쓴다. */
var lrConfirmed by remember { mutableStateOf(false) } var lrConfirmed by remember { mutableStateOf(false) }
@@ -900,6 +906,14 @@ fun AnchorAlignView(appState: AppState) {
hasAlignData = true hasAlignData = true
alignDirty = true alignDirty = true
} }
// 같은 창의 mean-scan 으로 채널 검출 — 재부착 확인이 이것과 대조한다.
// 조건(2.3MHz·c3·supine 분기)이 재부착 확인 측정과 같다.
runCatching {
ClinicalBv.compute(
ClinicalBv.meanScan(lrRecent.map { it.first }),
supine = appState.clinicalPosture == ClinicalPosture.SUPINE,
).walls.take(ReattachCheck.CENTER_CHANNELS).map { it != null }
}.getOrNull()?.let { lrRefChannels = it }
} }
// 좌우 결과를 요약에 남긴다. 나중에 "이 세션은 좌우까지 맞춘 // 좌우 결과를 요약에 남긴다. 나중에 "이 세션은 좌우까지 맞춘
// 것인가"를 파일만 보고 답할 수 있어야 한다 — 재분석 때 // 것인가"를 파일만 보고 답할 수 있어야 한다 — 재분석 때
@@ -947,8 +961,9 @@ fun AnchorAlignView(appState: AppState) {
appState.anchorBasis = basis appState.anchorBasis = basis
// 재부착 확인의 기준 — 확인 측정에서 잡힌 채널(CH0~3). 확인 측정 없이 // 재부착 확인의 기준 — 확인 측정에서 잡힌 채널(CH0~3). 확인 측정 없이
// 진행하면 기준 없음(null)이다. 새 위치이므로 지난 재부착 결과는 비운다. // 진행하면 기준 없음(null)이다. 새 위치이므로 지난 재부착 결과는 비운다.
appState.anchorRefChannels = // 기준은 좌우 확인 창(부착 위치와 같은 높이)이 우선, 없으면 확인 측정(N cm).
if (confirmDone) bv?.walls?.take(ReattachCheck.CENTER_CHANNELS)?.map { it != null } else null appState.anchorRefChannels = lrRefChannels
?: if (confirmDone) bv?.walls?.take(ReattachCheck.CENTER_CHANNELS)?.map { it != null } else null
appState.anchorReattach = null appState.anchorReattach = null
// 근거를 요약에도 박는다. 화면에만 있으면 나중에 파일을 보는 사람이 // 근거를 요약에도 박는다. 화면에만 있으면 나중에 파일을 보는 사람이
// 이 위치가 어떻게 정해졌는지 알 길이 없다. // 이 위치가 어떻게 정해졌는지 알 길이 없다.
@@ -1005,6 +1020,7 @@ fun AnchorAlignView(appState: AppState) {
runStartedAt = Date() runStartedAt = Date()
guide.reset(); cm = 0; last = null; records = emptyList(); error = null guide.reset(); cm = 0; last = null; records = emptyList(); error = null
lateral.reset(); lrCommit = null; lrAccum = 0; lrRecent.clear(); lrAttempt++ lateral.reset(); lrCommit = null; lrAccum = 0; lrRecent.clear(); lrAttempt++
lrRefChannels = null
lrConfirmed = false; lrError = null; lrRunning = false lrConfirmed = false; lrError = null; lrRunning = false
disconnectedMidRun = false disconnectedMidRun = false
decidedAtCm = null; sweepStopped = false; confirmDone = false decidedAtCm = null; sweepStopped = false; confirmDone = false