#67 2026-03-03-1222-MVCamera Issues found by Claude

Terbuka
dibuka 3 bulan lalu oleh rborisov · 0 komentar

[[MView-5GCamera-VS-Amantya-comparison-26-03-02]]

Design Issues

6. Global shared modemResponseBuffer — AtCommandInterface.cpp:8

modemResponseBuffer is a file-scope static. If two AtCommandInterface instances ever exist, their per-instance mutex would protect their own state machine but both write to the same buffer — data corruption. Even with a single instance today, it's a landmine.

7. Static state inside isModemRebootCommandCompleted() — ModemManager.cpp:375-377

Uses static local variables for its state machine. Calling resetModemInterfaceInit() resets everything else, but the static state here persists independently and cannot be reset from outside without restarting the process.

8. AT command table with hardcoded linear indices — ModemManager.cpp:37-114

nextCommandIndexIfOK / nextCommandIndexIfError are raw integer indices into the modemAtCmdForInitialization[] array. Inserting or removing a command requires manually updating every index that follows — extremely error-prone. Named enum values or a linked/table-driven approach would be safer.

9. AT response buffer is a single 2048-byte read() call — AtCommandInterface.cpp:82-103

If the modem response is fragmented across two kernel read calls (common on UART), the first partial read won't contain "OK" and the state machine immediately transitions to AT_COMMAND_RESPONSE_ERROR. AT+CGDCONT? responses with many APNs can easily be large. The buffer should be accumulated until the expected response appears or a timeout fires.

10. Sleep inside the state machine — AtCommandInterface.cpp:64

std::this_thread::sleep_for() inside the AT_COMMAND_RESPONSE_WAIT case means the calling loop burns one sleep period per state machine call. The caller's busy-poll loop then calls select again. The flow is: sleep(RESPONSE_WAIT_TIME)select(10ms). The sleep should be in the caller, not the state machine.

11. modemSerialPortScanning() fails when only the NMEA port is missing — ModemManager.cpp:542-550

Returns MODEM_PROCESS_FAILURE if the NMEA GPS serial port isn't found, even if the AT port was successfully discovered. This would prevent initialization on a device where GPS is not yet enumerated or not connected, even though the modem itself is responsive.

12. closeSerialDevicePort() logs an error on already-closed port — SerialDevicePort.cpp:131-132

The destructor calls closeSerialDevicePort(). If the port was already closed explicitly (e.g., modemInterfaceInit closes it on line 641 on every call), the destructor emits a spurious "File is already closed" error. The already-closed case is not an error; it should be a no-op.

13. GenericThreadSafeQueue::DequeueData() blocks forever — Queue.h:105-112

The condition_variable::wait has no timeout and no check against a shutdown/exit flag. Any thread blocked in DequeueData() at process exit would deadlock.


Naming and Structure

14. Header guard doesn't match filename — ModemManager.h:1

#ifndef NETWORK_QUECTEL_H_ should be #ifndef MODEM_MANAGER_H_.

15. isQuectelModemInit() misleadingly named

The function checks whether the modem-info config file exists on disk. It applies to both Telit and Quectel targets. The name implies it's Quectel-specific. Similarly QuectelModemInterfaceCleanUp() does nothing (just a DLOG) but the name implies real cleanup.

16. Two sentinel values for "not set" — ModemManager.h:54-69 vs ModemManager.h:108-109

INT_MIN = INVALID_INT (struct field defaults) and INT_MAX = "Telit signals field absent". The inline initializer at line 108-109 initializes all int fields to INT_MAX. The reset() method at line 81-84 resets to INT_MIN. So a freshly constructed object uses INT_MAX but a reset object uses INT_MIN. Code that checks both (current == INVALID_INT || current == INT_MAX) must keep track of this distinction everywhere.

17. NetworkThreadFun returns void* — thread.h:14, thread.cpp:33

This is a C pthreads signature. std::thread expects a void return (or any return — it's discarded). Returning void* is harmless but misleading and suggests pthreads was the original launcher.


Minor

18. main() always returns EXIT_FAILURE — main.cpp:143

Normal SIGTERM/SIGINT shutdown goes through goto main_end which exits with EXIT_FAILURE. Clean shutdown should return EXIT_SUCCESS.

19. LOG_FIRST_N(INFO, 25) silently stops logging — AtCommandInterface.cpp:26

After 25 AT command writes, the log goes permanently silent — you'll never see which command was written. This makes production debugging very hard for a long-running process.

20. GPS location never published — thread.cpp:135-167

The location field is declared in the KPI publisher schema, but ModemNetworkKpi::latitude and longitude are never populated from the NMEA GPS serial device (nmeaGPSSerialDevice). The GPS init commands run (AT$GPSP=1, AT$GPSNMUN), but there's no NMEA read loop anywhere in the KPI publishing path.

21. scanSerialDevicePort() enumerates all char devices — SerialDevicePort.cpp:9-35

Returns every character device in /dev/ (including /dev/null, /dev/random, etc.), not just serial ports. This function is unused in the current flow (superseded by enumerateTtyUSBPorts()), but it's misleading and should either be scoped to TTY devices or removed.

22. Commented-out saveJsonKpiInfoToFile() block — thread.cpp:188-192

The conditional call via checkKpiConfigFileExist() is commented out, leaving the unconditional call active. The dead commented block should be removed.

[!error] Gogs Issue Creation Failed net::ERR_FAILED

[!error] Gogs Issue Creation Failed net::ERR_FAILED


[[MView-5GCamera-VS-Amantya-comparison-26-03-02]] ### Design Issues **6. Global shared `modemResponseBuffer`** — [AtCommandInterface.cpp:8](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/AtCommandInterface.cpp#L8) `modemResponseBuffer` is a file-scope static. If two `AtCommandInterface` instances ever exist, their per-instance mutex would protect their own state machine but both write to the same buffer — data corruption. Even with a single instance today, it's a landmine. **7. Static state inside `isModemRebootCommandCompleted()`** — [ModemManager.cpp:375-377](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/ModemManager.cpp#L375-L377) Uses `static` local variables for its state machine. Calling `resetModemInterfaceInit()` resets everything else, but the static state here persists independently and cannot be reset from outside without restarting the process. **8. AT command table with hardcoded linear indices** — [ModemManager.cpp:37-114](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/ModemManager.cpp#L37-L114) `nextCommandIndexIfOK` / `nextCommandIndexIfError` are raw integer indices into the `modemAtCmdForInitialization[]` array. Inserting or removing a command requires manually updating every index that follows — extremely error-prone. Named enum values or a linked/table-driven approach would be safer. **9. AT response buffer is a single 2048-byte `read()` call** — [AtCommandInterface.cpp:82-103](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/AtCommandInterface.cpp#L82-L103) If the modem response is fragmented across two kernel read calls (common on UART), the first partial read won't contain `"OK"` and the state machine immediately transitions to `AT_COMMAND_RESPONSE_ERROR`. `AT+CGDCONT?` responses with many APNs can easily be large. The buffer should be accumulated until the expected response appears or a timeout fires. **10. Sleep inside the state machine** — [AtCommandInterface.cpp:64](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/AtCommandInterface.cpp#L64) `std::this_thread::sleep_for()` inside the `AT_COMMAND_RESPONSE_WAIT` case means the calling loop burns one sleep period per state machine call. The caller's busy-poll loop then calls `select` again. The flow is: `sleep(RESPONSE_WAIT_TIME)` → `select(10ms)`. The sleep should be in the caller, not the state machine. **11. `modemSerialPortScanning()` fails when only the NMEA port is missing** — [ModemManager.cpp:542-550](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/ModemManager.cpp#L542-L550) Returns `MODEM_PROCESS_FAILURE` if the NMEA GPS serial port isn't found, even if the AT port was successfully discovered. This would prevent initialization on a device where GPS is not yet enumerated or not connected, even though the modem itself is responsive. **12. `closeSerialDevicePort()` logs an error on already-closed port** — [SerialDevicePort.cpp:131-132](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/SerialDevicePort.cpp#L131-L132) The destructor calls `closeSerialDevicePort()`. If the port was already closed explicitly (e.g., `modemInterfaceInit` closes it on line 641 on every call), the destructor emits a spurious `"File is already closed"` error. The already-closed case is not an error; it should be a no-op. **13. `GenericThreadSafeQueue::DequeueData()` blocks forever** — [Queue.h:105-112](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/Queue.h#L105-L112) The `condition_variable::wait` has no timeout and no check against a shutdown/exit flag. Any thread blocked in `DequeueData()` at process exit would deadlock. --- ### Naming and Structure **14. Header guard doesn't match filename** — [ModemManager.h:1](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/ModemManager.h#L1) `#ifndef NETWORK_QUECTEL_H_` should be `#ifndef MODEM_MANAGER_H_`. **15. `isQuectelModemInit()` misleadingly named** The function checks whether the modem-info config file exists on disk. It applies to both Telit and Quectel targets. The name implies it's Quectel-specific. Similarly `QuectelModemInterfaceCleanUp()` does nothing (just a `DLOG`) but the name implies real cleanup. **16. Two sentinel values for "not set"** — [ModemManager.h:54-69](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/ModemManager.h#L54-L69) vs [ModemManager.h:108-109](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/ModemManager.h#L108-L109) `INT_MIN` = `INVALID_INT` (struct field defaults) and `INT_MAX` = "Telit signals field absent". The inline initializer at line 108-109 initializes all int fields to `INT_MAX`. The `reset()` method at line 81-84 resets to `INT_MIN`. So a freshly constructed object uses `INT_MAX` but a reset object uses `INT_MIN`. Code that checks both (`current == INVALID_INT || current == INT_MAX`) must keep track of this distinction everywhere. **17. `NetworkThreadFun` returns `void*`** — [thread.h:14](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/thread.h#L14), [thread.cpp:33](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/thread.cpp#L33) This is a C pthreads signature. `std::thread` expects a `void` return (or any return — it's discarded). Returning `void*` is harmless but misleading and suggests pthreads was the original launcher. --- ### Minor **18. `main()` always returns `EXIT_FAILURE`** — [main.cpp:143](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/main.cpp#L143) Normal SIGTERM/SIGINT shutdown goes through `goto main_end` which exits with `EXIT_FAILURE`. Clean shutdown should return `EXIT_SUCCESS`. **19. `LOG_FIRST_N(INFO, 25)` silently stops logging** — [AtCommandInterface.cpp:26](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/AtCommandInterface.cpp#L26) After 25 AT command writes, the log goes permanently silent — you'll never see which command was written. This makes production debugging very hard for a long-running process. **20. GPS location never published** — [thread.cpp:135-167](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/thread.cpp#L135-L167) The `location` field is declared in the KPI publisher schema, but `ModemNetworkKpi::latitude` and `longitude` are never populated from the NMEA GPS serial device (`nmeaGPSSerialDevice`). The GPS init commands run (`AT$GPSP=1`, `AT$GPSNMUN`), but there's no NMEA read loop anywhere in the KPI publishing path. **21. `scanSerialDevicePort()` enumerates all char devices** — [SerialDevicePort.cpp:9-35](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/SerialDevicePort.cpp#L9-L35) Returns every character device in `/dev/` (including `/dev/null`, `/dev/random`, etc.), not just serial ports. This function is unused in the current flow (superseded by `enumerateTtyUSBPorts()`), but it's misleading and should either be scoped to TTY devices or removed. **22. Commented-out `saveJsonKpiInfoToFile()` block** — [thread.cpp:188-192](vscode-webview://1sdr1468tc25qngit5km7c2ots54ruqv1lk6mdd21thdl4ptn96c/initmodem/src/thread.cpp#L188-L192) The conditional call via `checkKpiConfigFileExist()` is commented out, leaving the unconditional call active. The dead commented block should be removed. > [!error] Gogs Issue Creation Failed > net::ERR_FAILED > [!error] Gogs Issue Creation Failed > net::ERR_FAILED ---
Masuk untuk bergabung dalam percakapan ini.
Tidak ada tonggak
Tidak ada penerima
1 Peserta
Memuat...
Batal
Simpan
Belum ada konten.