All files / server/mocks/arduino-mock arduino-stdin.ts

100% Statements 1/1
100% Branches 0/0
100% Functions 0/0
100% Lines 1/1

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115                                16x                                                                                                                                                                                                    
/**
 * Arduino Stdin Command Handler — extracted from arduino-mock.ts
 *
 * Exports the C++ code for reading and dispatching stdin protocol commands:
 *  - [[SET_PIN:X:Y]] — sets a pin value externally
 *  - [[PAUSE_TIME]] / [[RESUME_TIME:N]] — freeze/unfreeze the time counter
 *  - Normal serial input is forwarded to Serial.mockInput()
 *
 * Dependencies (must appear earlier in the assembled C++ source):
 *  - `cerrMutex`, `processIsPaused`, `pausedTimeMs`, `totalPausedTimeMs` (ARDUINO_GLOBALS)
 *  - `pinValues[]` (ARDUINO_PIN_STATE_INIT)
 *  - `millis()` (ARDUINO_TIMING_AND_RANDOM)
 *  - `Serial` global instance (ARDUINO_SERIAL_CLASS)
 *  - `keepReading` (ARDUINO_GLOBALS)
 */
 
export const ARDUINO_STDIN_HANDLER = String.raw`
// Global buffer for stdin reading (used by checkStdinForPinCommands)
static char stdinBuffer[256];
static size_t stdinBufPos = 0;
 
// Helper function to set pin value from external input
void setExternalPinValue(int pin, int value) {
    if (pin >= 0 && pin < 20) {
        pinValues[pin].store(value, std::memory_order_seq_cst);
        // Send pin state update so UI reflects the change
        { std::lock_guard<std::mutex> lock(cerrMutex);
          std::cerr << "[[PIN_VALUE:" << pin << ":" << value << "]]" << std::endl; }
    }
}
 
// Helper functions for pause/resume timing
void handlePauseTimeCommand() {
    // compute current time while still running; only then flip pause flag
    unsigned long currentMs = millis();  // Get current time before freezing
    pausedTimeMs.store(currentMs);
    processIsPaused.store(true);
    { std::lock_guard<std::mutex> lock(cerrMutex);
      std::cerr << "[[TIME_FROZEN:" << currentMs << "]]" << std::endl; }
}
 
void handleResumeTimeCommand(unsigned long pauseDurationMs) {
    processIsPaused.store(false);
    // Accumulate total paused time so micros()/millis() can subtract it
    totalPausedTimeMs += pauseDurationMs;
    { std::lock_guard<std::mutex> lock(cerrMutex);
      std::cerr << "[[TIME_RESUMED:" << totalPausedTimeMs << "]]" << std::endl; }
}
 
// Non-blocking check for stdin pin commands - called from delay() and txDelay()
void checkStdinForPinCommands() {
    fd_set readfds;
    struct timeval tv;
    
    while (true) {
        FD_ZERO(&readfds);
        FD_SET(STDIN_FILENO, &readfds);
        tv.tv_sec = 0;
        tv.tv_usec = 0; // Zero timeout = immediate return
        
        int selectResult = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv);
        
        if (selectResult <= 0 || !FD_ISSET(STDIN_FILENO, &readfds)) {
            break;
        }
        
        // Read one byte
        char c;
        ssize_t n = read(STDIN_FILENO, &c, 1);
        if (n <= 0) break;
        
        if (c == '\n' || c == '\r') {
            // End of line - process buffer
            if (stdinBufPos > 0) {
                stdinBuffer[stdinBufPos] = '\0';
                
                // Check for pause/resume commands
                if (sscanf(stdinBuffer, "[[PAUSE_TIME]]") == 0 && 
                    strncmp(stdinBuffer, "[[PAUSE_TIME]]", 14) == 0) {
                    handlePauseTimeCommand();
                } else {
                    // Check for resume time with duration parameter
                    unsigned long pauseDurationMs;
                    if (sscanf(stdinBuffer, "[[RESUME_TIME:%lu]]", &pauseDurationMs) == 1) {
                        handleResumeTimeCommand(pauseDurationMs);
                    } else {
                        // Check for special pin value command: [[SET_PIN:X:Y]]
                        int pin, value;
                        if (sscanf(stdinBuffer, "[[SET_PIN:%d:%d]]", &pin, &value) == 2) {
                            setExternalPinValue(pin, value);
                        } else {
                            // Normal serial input (add newline back for serial input)
                            Serial.mockInput(stdinBuffer, stdinBufPos);
                            char newline = 10;
                            Serial.mockInput(&newline, 1);
                        }
                    }
                }
                stdinBufPos = 0;
            }
        } else if (stdinBufPos < sizeof(stdinBuffer) - 1) {
            stdinBuffer[stdinBufPos++] = c;
        }
    }
}
 
// Thread-based reader for when main thread is not in delay/serial (legacy, still useful)
void serialInputReader() {
    while (keepReading.load()) {
        checkStdinForPinCommands();
        std::this_thread::sleep_for(std::chrono::milliseconds(5));
    }
}
`;