Task 1-1: Fix Commented-Out machineSocketClose trong handleSocketClose(SocketClose)
Phase: 1 Priority: High Module:
ConnectedClientDepends on: Không có Reference: docs/BountyHunter-ControlServer/details/feature-iot-bridge/SPEC.md
Background
ConnectedClient có 2 overload của handleSocketClose: overload không tham số gọi adminRMCCraneMachineClient.machineSocketClose(macIp) bình thường, nhưng overload nhận tham số SocketClose socketClose (triggered khi socket đóng kèm reason từ binary protocol) lại có dòng này bị comment out. Hậu quả là khi máy gửi close packet với reason code (ví dụ: lỗi nguồn, lỗi cơ học), backend KHÔNG nhận được thông báo, machine vẫn hiển thị ONLINE trên admin dashboard dù TCP socket đã đóng. Đây là silent bug có thể dẫn đến player bị charged nhưng máy không hoạt động.
Tasks
Note: Cả 2 overload đều chạy trên connection-reading thread. Không cần synchronization riêng vì
machineSocketClose()là outbound HTTP/JMS call và không mutate shared state trongConnectedClient. Tuy nhiên, cần đảm bảo không gọi 2 lần nếu cả 2 code paths đều active — dùngvolatile boolean closeCalledguard nếu cần.
- [ ] Chạy
git blame ConnectedClient.javađể xác định commit đã comment out dòng đó và đọc commit message - [ ] Nếu comment là unintentional (không có lý do rõ ràng trong commit): uncomment
adminRMCCraneMachineClient.machineSocketClose(macIp)và thêm log - [ ] Nếu comment là intentional (ví dụ: throttle, duplicate call): thêm comment giải thích rõ lý do và xem xét dùng guard flag để tránh double-call với overload kia
- [ ] Verify rằng
macIpđã được set trước khihandleSocketClose(SocketClose)được gọi (tức là handshake đã hoàn thành)
// BEFORE — bị comment out trong handleSocketClose(SocketClose socketClose):
private void handleSocketClose(SocketClose socketClose) {
log.info("Socket close with reason: {}", socketClose.getReason());
// adminRMCCraneMachineClient.machineSocketClose(macIp); ← BUG: không gọi backend
}
// AFTER — cả 2 overload đều notify backend:
private void handleSocketClose(SocketClose socketClose) {
log.info("[{}] Socket close with reason: {}", macIp, socketClose.getReason());
adminRMCCraneMachineClient.machineSocketClose(macIp); // ← uncomment
}
private void handleSocketClose() {
log.info("[{}] Socket close (no reason)", macIp);
adminRMCCraneMachineClient.machineSocketClose(macIp);
}
- [ ] Nếu lo ngại double-call: thêm
AtomicBoolean closeNotifiedguard:
private final AtomicBoolean closeNotified = new AtomicBoolean(false);
private void notifyMachineOffline() {
if (closeNotified.compareAndSet(false, true)) {
log.info("[{}] Notifying backend: machine offline", macIp);
adminRMCCraneMachineClient.machineSocketClose(macIp);
}
}
Verification / Acceptance Criteria
- [ ] Khi TCP socket đóng với reason (overload
handleSocketClose(SocketClose)được trigger) → backend nhậnmachineSocketCloseevent - [ ] Machine status chuyển từ ONLINE sang OFFLINE trên admin dashboard sau socket close với reason
- [ ] Log line xác nhận
machineSocketCloseđược gọi, bao gồmmacIpvà reason - [ ] Không có double-call (2 lần gọi
machineSocketClose) nếu cả 2 overloads đều được trigger cho cùng 1 disconnect event - [ ] Unit test: mock
adminRMCCraneMachineClient, gọihandleSocketClose(new SocketClose(...))→ verifymachineSocketClose(macIp)được gọi đúng 1 lần
Files to Modify
src/main/java/.../ConnectedClient.java