Skip to content

Thiết kế: IoT Receiver Service Feature

1. Tổng quan kiến trúc

flowchart TB
    subgraph IoT["Physical Layer"]
        CM["Crane Machine (IoT)"]
    end

    subgraph CS["BountyHunter-ControlServer"]
        CSApp["Control Server App\n(WebSocket bridge)"]
    end

    subgraph MQ["ActiveMQ Artemis"]
        Q1["queue-control-server-gameplay\nconcurrency: 1-50"]
        Q2["queue-control-server-health-check\nconcurrency: 1-50"]
        Q3["queue-machine_history_log\nconcurrency: 1-1"]
    end

    subgraph RS["receiver-service (port 8088)"]
        GL["JMSReceiverGameplayMachineListener"]
        HL["JMSReceiverHealthCheckMachineListener"]
        LL["JMSReceiverLogMachineListener"]
        BATCH["CraneMachineCheckStatusBatchConfig"]
    end

    subgraph Core["application-core"]
        GS["IGameService\n(startGame, endGame, countDown)"]
        CMS["CraneMachineService\n(status, connect, disconnect)"]
        SLACK["SlackNotificationService"]
        MHLR["MachineHistoryLogRepository"]
        MRRLR["MachineReceivedResultLogRepository"]
    end

    CM <-->|WebSocket| CSApp
    CSApp -->|publish| Q1 & Q2 & Q3

    Q1 --> GL
    Q2 --> HL
    Q3 --> LL

    GL --> GS
    HL --> CMS & SLACK
    LL --> CMS & MHLR & MRRLR & SLACK
    BATCH --> CMS

    GS -->|"WS broadcast"| Player["Players (WebSocket)"]

2. Gameplay event flow

sequenceDiagram
    participant CM as Crane Machine
    participant CS as ControlServer
    participant MQ as ActiveMQ
    participant GL as JMSReceiverGameplayListener
    participant GS as IGameService
    participant Redis as Redis (Room State)
    participant WS as WebSocket (Players)

    CM-->>CS: Game ended (win)
    CS->>MQ: publish {mac_ip, message_type: END_GAME, data: {game_res: 1}}
    MQ->>GL: receiveMessage(json)
    GL->>GL: deserialize → macIp, MessageType.END_GAME
    GL->>GS: endGameSuccess(macIp, gameResult=1, false)
    GS->>Redis: get room by macIp
    GS->>Redis: update room state = FINISHED
    GS->>WS: broadcast GAME_END to players
    GS->>MQ: publish prize allocation message

3. Health check & Slack alert flow

sequenceDiagram
    participant CM as Crane Machine
    participant CS as ControlServer
    participant MQ as ActiveMQ
    participant HL as JMSReceiverHealthCheckListener
    participant CMS as CraneMachineService
    participant DB as MySQL
    participant Slack as Slack

    CM-->>CS: Camera error
    CS->>MQ: {mac_ip, message_type: CAM_STATUS_REPORT, data: {cam_position: "TOP", cam_status: "ERROR"}}
    MQ->>HL: receiveMessage(json)
    HL->>CMS: camStatusReport(macIp, reportData)
    CMS->>DB: update camera status
    alt cam_status != OK
        HL->>Slack: sendCamErrorNotification(macIp, camPosition, camStatus)
    end

    Note over CM,Slack: Error code 81 - auto end game
    CM-->>CS: ERROR_REPORT {error_code: 81}
    CS->>MQ: publish ERROR_REPORT
    MQ->>HL: receiveMessage
    HL->>CMS: machineErrorReport(reportData, macIp)
    CMS->>DB: log error
    HL->>GS: endGameSuccess(macIp, 1, true)  # force end with error flag
    HL->>Slack: sendMachineErrorNotification(macIp, errorText, errorCode)

4. Log persistence flow

sequenceDiagram
    participant CS as ControlServer
    participant MQ as ActiveMQ
    participant LL as JMSReceiverLogMachineListener
    participant CMS as CraneMachineService
    participant DB as MySQL
    participant Slack as Slack

    CS->>MQ: {type: SAVE_MACHINE_HISTORY_LOG, mac: "AA:BB:...", log_type: DISCONNECTED}
    MQ->>LL: receiveMessage(json)
    LL->>LL: resolve boothId from machineMac\n(craneMachineService.findByMac → groupLabel-groupNumber)
    LL->>LL: set envType, boothId, noteMap
    LL->>DB: machineHistoryLogRepository.save(model)
    alt log_type == DISCONNECTED
        LL->>Slack: notifySlackMachineDisconnect(machineMac)
    end

5. concurrency design

Queue Concurrency Lý do
queue-control-server-gameplay 1-50 High throughput, parallel machines, IGameService thread-safe
queue-control-server-health-check 1-50 Parallel machines, health events independent
queue-machine_history_log 1-1 Sequential logging per machine, avoid DB write conflicts

6. Error handling

Tình huống Xử lý
JSON parse error BadRequestException thrown → message goes to DLQ
macIp không tìm thấy trong DB boothId = "UNKNOWN", vẫn persist log
IGameService throws ControlServerMessageFail Log error, không re-throw (message ack'd)
endGameSuccess throws exception (error code 81 path) catch Exception → log error, không re-throw
Slack notification fails Log error, không affect main flow