<commit_before>/******************************************************************************
 * Fake Frog                                                                  *
 * An Arduino-based project to build a frog-shaped temperature logger.        *
 * Author: David Lougheed. Copyright 2017.                                    *
 ******************************************************************************/

#define VERSION "0.1.0"

// Includes

#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal.h>

#include <SD.h>
#include <RTClib.h>

// Compile-Time Settings

#define SERIAL_LOGGING          true    // Log to the serial display for debug.
#define FILE_LOGGING            true    // Log to file on SD card. (recommended)
#define DISPLAY_ENABLED         true    // Show menus and information on an LCD.
#define NUM_SAMPLES             10      // Samples get averaged to reduce noise.
#define SAMPLE_DELAY            10      // Milliseconds between samples.
#define READING_INTERVAL        60      // Seconds between readings.

// Hardware Settings
// - The data logging shield uses A4, A5, and digital pins 10, 11, 12, and 13.

#define NUM_THERMISTORS         4

#define THERMISTOR_1_PIN        0       // Analog pin
#define THERMISTOR_2_PIN        1       // Analog pin
#define THERMISTOR_3_PIN        2       // Analog pin
#define THERMISTOR_4_PIN        3       // Analog pin

const uint8_t thermistor_pins[NUM_THERMISTORS] = {
    THERMISTOR_1_PIN,
    THERMISTOR_2_PIN,
    THERMISTOR_3_PIN,
    THERMISTOR_4_PIN
};

#define THERMISTOR_SERIES_RES   10000
#define THERMISTOR_RES_NOM      10000   // Nominal resistance, R0.
#define THERMISTOR_B_COEFF      3950    // Beta coefficient of the thermistor.
#define THERMISTOR_TEMP_NOM     25      // Nominal temperature of R0.

#define BUTTON_1_PIN            2
#define BUTTON_2_PIN            3
#define SD_CARD_PIN             10
#define RTC_PIN_1               A4      // Analog pin
#define RTC_PIN_2               A5      // Analog pin
#define LCD_PIN_RS              4
#define LCD_PIN_EN              5
#define LCD_PIN_DB4             6
#define LCD_PIN_DB5             7
#define LCD_PIN_DB6             8
#define LCD_PIN_DB7             9

#define LCD_ROWS                2
#define LCD_COLUMNS             16

#define RTC_TYPE                RTC_PCF8523

// Other Compile-Time Constants

#define MAX_LOG_FILES           1000
#define MAX_DATA_FILES          1000

// Globals

bool serial_logging_started = false;

// - Files
File log_file;
File data_file;

// - Hardware Objects
RTC_TYPE rtc;
LiquidCrystal* lcd;

// - Data Point Variables
//   (to save memory, use global data point variables)
DateTime now;
char formatted_timestamp[] = "0000-00-00T00:00:00";
char* data_file_entry_buffer = (char*) malloc(sizeof(char) * 50);
char temperature_string[4][8];
double latest_resistance[4];
double latest_temperature[4];

/*
    DISPLAY MODES (ALL WITH LOGGING)
    0: Idle
    1: Information (RAM free)
    2: RTC Editor
*/
uint8_t display_mode = 0;

uint16_t i, z; // 16-bit iterator
uint8_t timer = 0; // Counts seconds
uint32_t milli_timer = 0; // Counts time taken to do a loop
uint32_t uptime = 0;
uint8_t cursor = 0; // Maximum: 31 (second row, last column)

bool button_1 = false;
bool button_2 = false;

// Utility Methods

// Determine amount of free RAM.
// - Retrieved 2017-05-19 (https://playground.arduino.cc/Code/AvailableMemory)
int freeRAM() {
    extern int __heap_start, *__brkval;
    int v;
    return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

// Log a generic message.
void log(const char* msg, bool with_newline = true) {
    if (SERIAL_LOGGING) {
        if(!serial_logging_started) {
            Serial.begin(9600);
            Serial.println();
            serial_logging_started = true;
        }

        if (with_newline) {
            Serial.println(msg);
        } else {
            Serial.print(msg);
        }
    }

    if (FILE_LOGGING) {
        if (log_file) {
            if (with_newline) {
                log_file.println(msg);
            } else {
                log_file.print(msg);
            }
        }
    }
}

// Flush various logging buffers.
void log_flush() {
    if (SERIAL_LOGGING) {
        Serial.flush();
    }
    if (FILE_LOGGING) {
        log_file.flush();
    }
}

// Log an error message. Uses standard log method, then hangs forever.
void log_error(const char* msg, bool with_newline = true) {
    log(msg, with_newline);
    log_flush();
    while (true); // Loop forever
}

// Update the LCD to display latest values for the set display mode.
void update_display() {
    if (DISPLAY_ENABLED && lcd) {
        lcd->clear();
        cursor = 0;

        switch (display_mode) {
            case 1:     // Information
                lcd->print("Free RAM: ");
                lcd->print(freeRAM(), 10);
                lcd->noBlink();
                break;
            case 2:     // RTC Editor
                lcd->print("TBD");
                lcd->setCursor(0, 0);
                lcd->blink();
                break;
            case 0:     // Idle
            default:
                lcd->noBlink();
                break;
        }
    }
}

// Switch the display mode, triggering a display update.
void switch_display_mode(uint8_t m) {
    display_mode = m % 3;
    update_display();
}

// Data Methods

// Update the global formatted timestamp string with the contents of 'now'.
void update_formatted_timestamp() {
    sprintf(formatted_timestamp, "%04u-%02u-%02uT%02u:%02u:%02u", now.year(),
        now.month(), now.day(), now.hour(), now.minute(), now.second());
}

double resistance_to_temperature(double resistance) {
    // Formula: T = 1/(1/B * ln(R/R_0) + (1/T0)) - 273.15 (celcius)
    return 1 / ((log(resistance / THERMISTOR_RES_NOM) / THERMISTOR_B_COEFF) + 1
        / (THERMISTOR_TEMP_NOM + 273.15)) - 273.15;
}

void take_reading(uint8_t t) {
    now = rtc.now();

    latest_resistance[t] = 0;

    for (i = 0; i < NUM_SAMPLES; i++) {
        latest_resistance[t] += (double) analogRead(thermistor_pins[t]);
        delay(SAMPLE_DELAY);
    }

    // Formulas: R = sr / (1023 / mean_of_samples - 1)
    //           sr = thermistor series resistance

    latest_resistance[t] = THERMISTOR_SERIES_RES
        / (1023 / (latest_resistance[t] / NUM_SAMPLES) - 1); // Resistance
    latest_temperature[t] = resistance_to_temperature(latest_resistance[t]);

    // TODO: Error calculations
}

void save_reading_to_card() {
    if (data_file) {
        update_formatted_timestamp();
        for (i = 0; i < NUM_THERMISTORS; i++) {
            dtostrf(latest_temperature[i], 5, 2, temperature_string[i]);
        }

        log("Took reading: ", false);
        log(formatted_timestamp, false); log(",", false);
        log(temperature_string[0], false); log(",", false);
        log(temperature_string[1], false); log(",", false);
        log(temperature_string[2], false); log(",", false);
        log(temperature_string[3]);
        log_flush();

        data_file.print(formatted_timestamp); data_file.print(",");
        data_file.print(temperature_string[0]); data_file.print(",");
        data_file.print(temperature_string[1]); data_file.print(",");
        data_file.print(temperature_string[2]); data_file.print(",");
        data_file.println(temperature_string[3]);
        // data_file.println(data_file_entry_buffer);
        data_file.flush();
    }
}

// Main Methods

void setup() {
    // SET UP EXTERNAL ANALOG VOLTAGE REFERENCE
    // Typically from 3.3V Arduino supply. This reduces the voltage noise seen
    // from reading analog values.
    analogReference(EXTERNAL);

    // INITIALIZE SD CARD
    log("Initializing SD card... ", false);
    pinMode(SD_CARD_PIN, OUTPUT);
    if (!SD.begin()) {
        log_error("Failed.");
    }
    log("Done.");

    // SET UP LOG FILE
    if (FILE_LOGGING) {
        log("Creating log file... ", false);
        char log_file_name[] = "log_000.txt";
        for (i = 0; i < MAX_LOG_FILES; i++) {
            // Increment until we can find a log file slot.

            // Need to add 48 to get ASCII number characters.
            log_file_name[4] = i / 100 + 48;
            log_file_name[5] = i / 10 % 10 + 48;
            log_file_name[6] = i % 10 + 48;

            if (!SD.exists(log_file_name)) {
                log_file = SD.open(log_file_name, FILE_WRITE);
                break;
            }
        }
        if (log_file) {
            log("Done.");
        } else {
            log_error("Failed.");
        }
    }

    // SET UP RTC
    log("Initializing RTC... ", false);
    Wire.begin();
    if (!rtc.begin()) {
        log_error("Failed.");
    }
    log("Done.");

    // INPUT RTC TIME
    if (SERIAL_LOGGING) {
        uint16_t year;
        uint8_t month, day, hour, minute, second;

        Serial.print("Change clock? (y/n) ");
        while (Serial.available() < 1);
        Serial.println();
        if (Serial.read() == 'y') {
            Serial.println();
            Serial.print("Enter Year: ");
            while (Serial.available() < 4);
            year += (Serial.read() - 48) * 1000;
            year += (Serial.read() - 48) * 100;
            year += (Serial.read() - 48) * 10;
            year += (Serial.read() - 48);
            Serial.println(year);

            Serial.print("Enter Month: ");
            while (Serial.available() < 2);
            month += (Serial.read() - 48) * 10;
            month += (Serial.read() - 48);
            Serial.println(month);

            Serial.print("Enter Day: ");
            while (Serial.available() < 2);
            day += (Serial.read() - 48) * 10;
            day += (Serial.read() - 48);
            Serial.println(day);

            Serial.print("Enter Hour: ");
            while (Serial.available() < 2);
            hour += (Serial.read() - 48) * 10;
            hour += (Serial.read() - 48);
            Serial.println(hour);

            Serial.print("Enter Minute: ");
            while (Serial.available() < 2);
            minute += (Serial.read() - 48) * 10;
            minute += (Serial.read() - 48);
            Serial.println(minute);

            Serial.print("Enter Second: ");
            while (Serial.available() < 2);
            second += (Serial.read() - 48) * 10;
            second += (Serial.read() - 48);
            Serial.println(second);

            rtc.adjust(DateTime(year, month, day, hour, minute, second));
        }
    }

    // SET UP DATA FILE
    log("Creating data file... ", false);
    char data_file_name[] = "dat_000.csv";
    for (i = 0; i < MAX_DATA_FILES; i++) {
        // Increment until we can find a data file slot.

        // Need to add 48 to get ASCII digit characters.
        data_file_name[4] = i / 100 + 48;
        data_file_name[5] = i / 10 % 10 + 48;
        data_file_name[6] = i % 10 + 48;

        if (!SD.exists(data_file_name)) {
            data_file = SD.open(data_file_name, FILE_WRITE);
            break;
        }
    }
    if (data_file) {
        log("Done.");
    } else {
        log_error("Failed.");
    }

    // PRINT DATA FILE CSV HEADERS
    data_file.println("Timestamp,Temp1,Temp2,Temp3,Temp4");
    data_file.flush();

    // SET UP LCD
    if (DISPLAY_ENABLED) {
        lcd = new LiquidCrystal(LCD_PIN_RS, LCD_PIN_EN, LCD_PIN_DB4,
            LCD_PIN_DB5, LCD_PIN_DB6, LCD_PIN_DB7);
        lcd->begin(LCD_COLUMNS, LCD_ROWS);

        update_display();
    }

    // SET UP BUTTONS
    pinMode(BUTTON_1_PIN, INPUT);
    pinMode(BUTTON_2_PIN, INPUT);

    // Finished everything!
    now = rtc.now();
    update_formatted_timestamp();
    log("Data logger started at ", false);
    log(formatted_timestamp, false);
    log(". Software version: ", false);
    log(VERSION);
    log_flush();
}

void loop() {
    // Time the loop to make sure it runs rounded to the nearest second.
    milli_timer = millis();
    if (timer >= READING_INTERVAL) {
        timer = 0;
        for (z = 0; z < NUM_THERMISTORS; z++) { // Loop through all thermistors
            take_reading(z);
        }
        save_reading_to_card();
    }

    button_1 = digitalRead(BUTTON_1_PIN);
    button_2 = digitalRead(BUTTON_2_PIN);

    if (button_1 && button_2) {
        switch_display_mode(++display_mode);
    } else if (button_1) {

    } else if (button_2) {
        cursor = (cursor + 1) % 32;
        lcd->setCursor(cursor % 16, cursor > 15 ? 1 : 0);
    }

    milli_timer = millis() - milli_timer;
    while (milli_timer >= 1000) {
        // Prevent an integer overflow error by making sure milli_timer < 1000
        timer++; // An extra second has occurred - don't let it slip away!
        milli_timer -= 1000;
    }
    timer++;
    uptime++;
    delay(1000 - milli_timer); // (Ideally) 1 second between loops
}
<commit_msg>Remove vestiges of sprintf method of data file output.<commit_after>/******************************************************************************
 * Fake Frog                                                                  *
 * An Arduino-based project to build a frog-shaped temperature logger.        *
 * Author: David Lougheed. Copyright 2017.                                    *
 ******************************************************************************/

#define VERSION "0.1.0"

// Includes

#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal.h>

#include <SD.h>
#include <RTClib.h>

// Compile-Time Settings

#define SERIAL_LOGGING          true    // Log to the serial display for debug.
#define FILE_LOGGING            true    // Log to file on SD card. (recommended)
#define DISPLAY_ENABLED         true    // Show menus and information on an LCD.
#define NUM_SAMPLES             10      // Samples get averaged to reduce noise.
#define SAMPLE_DELAY            10      // Milliseconds between samples.
#define READING_INTERVAL        60      // Seconds between readings.

// Hardware Settings
// - The data logging shield uses A4, A5, and digital pins 10, 11, 12, and 13.

#define NUM_THERMISTORS         4

#define THERMISTOR_1_PIN        0       // Analog pin
#define THERMISTOR_2_PIN        1       // Analog pin
#define THERMISTOR_3_PIN        2       // Analog pin
#define THERMISTOR_4_PIN        3       // Analog pin

const uint8_t thermistor_pins[NUM_THERMISTORS] = {
    THERMISTOR_1_PIN,
    THERMISTOR_2_PIN,
    THERMISTOR_3_PIN,
    THERMISTOR_4_PIN
};

#define THERMISTOR_SERIES_RES   10000
#define THERMISTOR_RES_NOM      10000   // Nominal resistance, R0.
#define THERMISTOR_B_COEFF      3950    // Beta coefficient of the thermistor.
#define THERMISTOR_TEMP_NOM     25      // Nominal temperature of R0.

#define BUTTON_1_PIN            2
#define BUTTON_2_PIN            3
#define SD_CARD_PIN             10
#define RTC_PIN_1               A4      // Analog pin
#define RTC_PIN_2               A5      // Analog pin
#define LCD_PIN_RS              4
#define LCD_PIN_EN              5
#define LCD_PIN_DB4             6
#define LCD_PIN_DB5             7
#define LCD_PIN_DB6             8
#define LCD_PIN_DB7             9

#define LCD_ROWS                2
#define LCD_COLUMNS             16

#define RTC_TYPE                RTC_PCF8523

// Other Compile-Time Constants

#define MAX_LOG_FILES           1000
#define MAX_DATA_FILES          1000

// Globals

bool serial_logging_started = false;

// - Files
File log_file;
File data_file;

// - Hardware Objects
RTC_TYPE rtc;
LiquidCrystal* lcd;

// - Data Point Variables
//   (to save memory, use global data point variables)
DateTime now;
char formatted_timestamp[] = "0000-00-00T00:00:00";
char temperature_string[4][8];
double latest_resistance[4];
double latest_temperature[4];

/*
    DISPLAY MODES (ALL WITH LOGGING)
    0: Idle
    1: Information (RAM free)
    2: RTC Editor
*/
uint8_t display_mode = 0;

uint16_t i, z; // 16-bit iterator
uint8_t timer = 0; // Counts seconds
uint32_t milli_timer = 0; // Counts time taken to do a loop
uint32_t uptime = 0;
uint8_t cursor = 0; // Maximum: 31 (second row, last column)

bool button_1 = false;
bool button_2 = false;

// Utility Methods

// Determine amount of free RAM.
// - Retrieved 2017-05-19 (https://playground.arduino.cc/Code/AvailableMemory)
int freeRAM() {
    extern int __heap_start, *__brkval;
    int v;
    return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

// Log a generic message.
void log(const char* msg, bool with_newline = true) {
    if (SERIAL_LOGGING) {
        if(!serial_logging_started) {
            Serial.begin(9600);
            Serial.println();
            serial_logging_started = true;
        }

        if (with_newline) {
            Serial.println(msg);
        } else {
            Serial.print(msg);
        }
    }

    if (FILE_LOGGING) {
        if (log_file) {
            if (with_newline) {
                log_file.println(msg);
            } else {
                log_file.print(msg);
            }
        }
    }
}

// Flush various logging buffers.
void log_flush() {
    if (SERIAL_LOGGING) {
        Serial.flush();
    }
    if (FILE_LOGGING) {
        log_file.flush();
    }
}

// Log an error message. Uses standard log method, then hangs forever.
void log_error(const char* msg, bool with_newline = true) {
    log(msg, with_newline);
    log_flush();
    while (true); // Loop forever
}

// Update the LCD to display latest values for the set display mode.
void update_display() {
    if (DISPLAY_ENABLED && lcd) {
        lcd->clear();
        cursor = 0;

        switch (display_mode) {
            case 1:     // Information
                lcd->print("Free RAM: ");
                lcd->print(freeRAM(), 10);
                lcd->noBlink();
                break;
            case 2:     // RTC Editor
                lcd->print("TBD");
                lcd->setCursor(0, 0);
                lcd->blink();
                break;
            case 0:     // Idle
            default:
                lcd->noBlink();
                break;
        }
    }
}

// Switch the display mode, triggering a display update.
void switch_display_mode(uint8_t m) {
    display_mode = m % 3;
    update_display();
}

// Data Methods

// Update the global formatted timestamp string with the contents of 'now'.
void update_formatted_timestamp() {
    sprintf(formatted_timestamp, "%04u-%02u-%02uT%02u:%02u:%02u", now.year(),
        now.month(), now.day(), now.hour(), now.minute(), now.second());
}

double resistance_to_temperature(double resistance) {
    // Formula: T = 1/(1/B * ln(R/R_0) + (1/T0)) - 273.15 (celcius)
    return 1 / ((log(resistance / THERMISTOR_RES_NOM) / THERMISTOR_B_COEFF) + 1
        / (THERMISTOR_TEMP_NOM + 273.15)) - 273.15;
}

void take_reading(uint8_t t) {
    now = rtc.now();

    latest_resistance[t] = 0;

    for (i = 0; i < NUM_SAMPLES; i++) {
        latest_resistance[t] += (double) analogRead(thermistor_pins[t]);
        delay(SAMPLE_DELAY);
    }

    // Formulas: R = sr / (1023 / mean_of_samples - 1)
    //           sr = thermistor series resistance

    latest_resistance[t] = THERMISTOR_SERIES_RES
        / (1023 / (latest_resistance[t] / NUM_SAMPLES) - 1); // Resistance
    latest_temperature[t] = resistance_to_temperature(latest_resistance[t]);

    // TODO: Error calculations
}

void save_reading_to_card() {
    if (data_file) {
        update_formatted_timestamp();
        for (i = 0; i < NUM_THERMISTORS; i++) {
            dtostrf(latest_temperature[i], 5, 2, temperature_string[i]);
        }

        log("Took reading: ", false);
        log(formatted_timestamp, false); log(",", false);
        log(temperature_string[0], false); log(",", false);
        log(temperature_string[1], false); log(",", false);
        log(temperature_string[2], false); log(",", false);
        log(temperature_string[3]);
        log_flush();

        data_file.print(formatted_timestamp); data_file.print(",");
        data_file.print(temperature_string[0]); data_file.print(",");
        data_file.print(temperature_string[1]); data_file.print(",");
        data_file.print(temperature_string[2]); data_file.print(",");
        data_file.println(temperature_string[3]);
        data_file.flush();
    }
}

// Main Methods

void setup() {
    // SET UP EXTERNAL ANALOG VOLTAGE REFERENCE
    // Typically from 3.3V Arduino supply. This reduces the voltage noise seen
    // from reading analog values.
    analogReference(EXTERNAL);

    // INITIALIZE SD CARD
    log("Initializing SD card... ", false);
    pinMode(SD_CARD_PIN, OUTPUT);
    if (!SD.begin()) {
        log_error("Failed.");
    }
    log("Done.");

    // SET UP LOG FILE
    if (FILE_LOGGING) {
        log("Creating log file... ", false);
        char log_file_name[] = "log_000.txt";
        for (i = 0; i < MAX_LOG_FILES; i++) {
            // Increment until we can find a log file slot.

            // Need to add 48 to get ASCII number characters.
            log_file_name[4] = i / 100 + 48;
            log_file_name[5] = i / 10 % 10 + 48;
            log_file_name[6] = i % 10 + 48;

            if (!SD.exists(log_file_name)) {
                log_file = SD.open(log_file_name, FILE_WRITE);
                break;
            }
        }
        if (log_file) {
            log("Done.");
        } else {
            log_error("Failed.");
        }
    }

    // SET UP RTC
    log("Initializing RTC... ", false);
    Wire.begin();
    if (!rtc.begin()) {
        log_error("Failed.");
    }
    log("Done.");

    // INPUT RTC TIME
    if (SERIAL_LOGGING) {
        uint16_t year;
        uint8_t month, day, hour, minute, second;

        Serial.print("Change clock? (y/n) ");
        while (Serial.available() < 1);
        Serial.println();
        if (Serial.read() == 'y') {
            Serial.println();
            Serial.print("Enter Year: ");
            while (Serial.available() < 4);
            year += (Serial.read() - 48) * 1000;
            year += (Serial.read() - 48) * 100;
            year += (Serial.read() - 48) * 10;
            year += (Serial.read() - 48);
            Serial.println(year);

            Serial.print("Enter Month: ");
            while (Serial.available() < 2);
            month += (Serial.read() - 48) * 10;
            month += (Serial.read() - 48);
            Serial.println(month);

            Serial.print("Enter Day: ");
            while (Serial.available() < 2);
            day += (Serial.read() - 48) * 10;
            day += (Serial.read() - 48);
            Serial.println(day);

            Serial.print("Enter Hour: ");
            while (Serial.available() < 2);
            hour += (Serial.read() - 48) * 10;
            hour += (Serial.read() - 48);
            Serial.println(hour);

            Serial.print("Enter Minute: ");
            while (Serial.available() < 2);
            minute += (Serial.read() - 48) * 10;
            minute += (Serial.read() - 48);
            Serial.println(minute);

            Serial.print("Enter Second: ");
            while (Serial.available() < 2);
            second += (Serial.read() - 48) * 10;
            second += (Serial.read() - 48);
            Serial.println(second);

            rtc.adjust(DateTime(year, month, day, hour, minute, second));
        }
    }

    // SET UP DATA FILE
    log("Creating data file... ", false);
    char data_file_name[] = "dat_000.csv";
    for (i = 0; i < MAX_DATA_FILES; i++) {
        // Increment until we can find a data file slot.

        // Need to add 48 to get ASCII digit characters.
        data_file_name[4] = i / 100 + 48;
        data_file_name[5] = i / 10 % 10 + 48;
        data_file_name[6] = i % 10 + 48;

        if (!SD.exists(data_file_name)) {
            data_file = SD.open(data_file_name, FILE_WRITE);
            break;
        }
    }
    if (data_file) {
        log("Done.");
    } else {
        log_error("Failed.");
    }

    // PRINT DATA FILE CSV HEADERS
    data_file.println("Timestamp,Temp1,Temp2,Temp3,Temp4");
    data_file.flush();

    // SET UP LCD
    if (DISPLAY_ENABLED) {
        lcd = new LiquidCrystal(LCD_PIN_RS, LCD_PIN_EN, LCD_PIN_DB4,
            LCD_PIN_DB5, LCD_PIN_DB6, LCD_PIN_DB7);
        lcd->begin(LCD_COLUMNS, LCD_ROWS);

        update_display();
    }

    // SET UP BUTTONS
    pinMode(BUTTON_1_PIN, INPUT);
    pinMode(BUTTON_2_PIN, INPUT);

    // Finished everything!
    now = rtc.now();
    update_formatted_timestamp();
    log("Data logger started at ", false);
    log(formatted_timestamp, false);
    log(". Software version: ", false);
    log(VERSION);
    log_flush();
}

void loop() {
    // Time the loop to make sure it runs rounded to the nearest second.
    milli_timer = millis();
    if (timer >= READING_INTERVAL) {
        timer = 0;
        for (z = 0; z < NUM_THERMISTORS; z++) { // Loop through all thermistors
            take_reading(z);
        }
        save_reading_to_card();
    }

    button_1 = digitalRead(BUTTON_1_PIN);
    button_2 = digitalRead(BUTTON_2_PIN);

    if (button_1 && button_2) {
        switch_display_mode(++display_mode);
    } else if (button_1) {

    } else if (button_2) {
        cursor = (cursor + 1) % 32;
        lcd->setCursor(cursor % 16, cursor > 15 ? 1 : 0);
    }

    milli_timer = millis() - milli_timer;
    while (milli_timer >= 1000) {
        // Prevent an integer overflow error by making sure milli_timer < 1000
        timer++; // An extra second has occurred - don't let it slip away!
        milli_timer -= 1000;
    }
    timer++;
    uptime++;
    delay(1000 - milli_timer); // (Ideally) 1 second between loops
}
<|endoftext|>


<commit_before>// Copyright 2016 Ilya Albrekht
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <time.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <errno.h>

#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <memory>

#include "cal.h"
#include "net.h"
#include "channel.h"
#include "gpio.h"

#include "log.h"

#include <signal.h>

#include "ArduinoJson/ArduinoJson.hpp"

/*
#include <libical/ical.h>

#include "libical/icalproperty_cxx.h"
#include "libical/vcomponent_cxx.h"
#include "libical/icalrecur.h" */

// Time after which to check if any of the valves are active (sec)
#define CHECK_TIME 10
// Time to update calendar info from the internet (sec)
#define UPDATE_TIME (60*60)

//// TODO List
// * Extract cal for a week

using namespace std;
using namespace LibICal;

// Global variables
int g_DebugLevel=6;
std::vector<shared_ptr<channel_t>> valves;

// Signal handlers
void sighandler_stop(int id)
{
    for(auto &v: valves)
        v.reset();
    exit(0);
}

#define PIN_LED_ACTIVE 4

void ParseOptions(int argc, char* argv[])
{
    int i=1;
    for(; i<argc; i++)
    {
        if(strcmp("--debug", argv[i]) == 0)
        {
            g_DebugLevel = 7;
            DEBUG_PRINT(LOG_INFO, "Debug print enabled");
        }

        // Run channel <CH> for <N> minutes
        // must be last one in the list
        if(strcmp("--run", argv[i]) == 0)
        {
            if( argc < i+2 )
            {
                cerr << "Invalid number of arguments\n";
                exit(-1);
            }

            int ch = atoi( argv[i+1] );
            int t  = atoi( argv[i+2] );

            DEBUG_PRINT(LOG_INFO, "Start channel " << ch << " for " << t << " minutes" );

            GpioRelay *R = new GpioRelay(ch);

            R->Start();
            sleep(t*60);
            R->Stop();

            delete R;

            exit(0);
        }
    }
}

int main(int argc, char* argv[])
{

    ParseOptions(argc, argv);

    // Register signals
    signal(SIGINT,  sighandler_stop);
    signal(SIGKILL, sighandler_stop);
    signal(SIGTERM, sighandler_stop);

    // Load configuration and create
    {
        ifstream cf("config.json");
        string config_str((std::istreambuf_iterator<char>(cf)),
                         std::istreambuf_iterator<char>());
        cf.close();

        StaticJsonBuffer<512> jbuf;
        JsonObject& root = jbuf.parseObject(config_str);

        if(!root.success())
        {
            DEBUG_PRINT(LOG_WARNING, "Error parsing config.json");
            exit(-1);
        }

        int n_s = root["schedule"].size();
        DEBUG_PRINT(LOG_INFO, "Loading schedule configuration (ch# " << n_s << ")" );
        for(int i=0; i<n_s; ++i)
        {
            if(root["schedule"][i]["type"] == string("ical"))
            {
                DEBUG_PRINT(LOG_INFO, "  - ical, gpio " << root["schedule"][i]["igpo"][0]);
                // Adding ical type schedule
                //cout << root["schedule"][i]["url"] << endl;
                string gpio_id = root["schedule"][i]["igpo"][0];
                shared_ptr<channel_t> vt(new channel_t(root["schedule"][i]["url"],std::stoi(gpio_id)) );

                //cout << "$ filename " << vt->get_cache_filename() << endl;
                if(!vt->load_cached(UPDATE_TIME)) {
                    //cout << "Load from web\n";
                    vt->load_from_url();
                }
                valves.push_back(vt);
            }
        }
    }

    time_t last_reload = time(0); //-2*UPDATE_TIME; // Hack to force the reload on the first iteration

    // Enter into work loop
    while(true)
    {
        bool to_reload;
        if( to_reload = (time(0) - last_reload) > UPDATE_TIME )
            last_reload = time(0);
        for( auto &vt : valves)
        {
            if(to_reload)
            {
                int err;
                if(err=vt->load_from_url() !=NET_SUCCESS) {
                    DEBUG_PRINT(LOG_INFO, "ERROR: Error loading ical" );
                }
            }

            DEBUG_PRINT(LOG_INFO, "INFO: update status");
            if(vt->vc.IsActive() != vt->gpio.GetStatus())
                vt->gpio.SetStatus(vt->vc.IsActive());
        }

        sleep(CHECK_TIME);
    }

    return 0;
}

<commit_msg>Help message added<commit_after>// Copyright 2016 Ilya Albrekht
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <time.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <errno.h>

#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <memory>

#include "cal.h"
#include "net.h"
#include "channel.h"
#include "gpio.h"

#include "log.h"

#include <signal.h>

#include "ArduinoJson/ArduinoJson.hpp"

/*
#include <libical/ical.h>

#include "libical/icalproperty_cxx.h"
#include "libical/vcomponent_cxx.h"
#include "libical/icalrecur.h" */

// Time after which to check if any of the valves are active (sec)
#define CHECK_TIME 10
// Time to update calendar info from the internet (sec)
#define UPDATE_TIME (60*60)

//// TODO List
// * Extract cal for a week

using namespace std;
using namespace LibICal;

// Global variables
int g_DebugLevel=6;
std::vector<shared_ptr<channel_t>> valves;

// Signal handlers
void sighandler_stop(int id)
{
    for(auto &v: valves)
        v.reset();
    exit(0);
}

#define PIN_LED_ACTIVE 4

void ParseOptions(int argc, char* argv[])
{
    int i=1;

    for(; i<argc; i++)
    {
        if( strcmp("--help", argv[i]) == 0 ) {
		printf("IoT scheduler by Ilya Albrekht\n");
		printf("  --debug           Enable debug level output\n");
		printf("  --run <C> <N>     Turn on channel C for N minutes\n");
		printf("  --daemon -D       Run as daemon\n");
		exit(0);
	}
    }

    for(i=1; i<argc; i++)
    {
        if(strcmp("--debug", argv[i]) == 0)
        {
            g_DebugLevel = 7;
            DEBUG_PRINT(LOG_INFO, "Debug print enabled");
        }

        // Run channel <CH> for <N> minutes
        // must be last one in the list
        if(strcmp("--run", argv[i]) == 0)
        {
            if( argc < i+2 )
            {
                cerr << "Invalid number of arguments\n";
                exit(-1);
            }

            int ch = atoi( argv[i+1] );
            int t  = atoi( argv[i+2] );

            DEBUG_PRINT(LOG_INFO, "Start channel " << ch << " for " << t << " minutes" );

            GpioRelay *R = new GpioRelay(ch);

            R->Start();
            sleep(t*60);
            R->Stop();

            delete R;

            exit(0);
        }
    }
}

int main(int argc, char* argv[])
{

    ParseOptions(argc, argv);

    // Register signals
    signal(SIGINT,  sighandler_stop);
    signal(SIGKILL, sighandler_stop);
    signal(SIGTERM, sighandler_stop);

    // Load configuration and create
    {
        ifstream cf("config.json");
        string config_str((std::istreambuf_iterator<char>(cf)),
                         std::istreambuf_iterator<char>());
        cf.close();

        StaticJsonBuffer<512> jbuf;
        JsonObject& root = jbuf.parseObject(config_str);

        if(!root.success())
        {
            DEBUG_PRINT(LOG_WARNING, "Error parsing config.json");
            exit(-1);
        }

        int n_s = root["schedule"].size();
        DEBUG_PRINT(LOG_INFO, "Loading schedule configuration (ch# " << n_s << ")" );
        for(int i=0; i<n_s; ++i)
        {
            if(root["schedule"][i]["type"] == string("ical"))
            {
                DEBUG_PRINT(LOG_INFO, "  - ical, gpio " << root["schedule"][i]["igpo"][0]);
                // Adding ical type schedule
                //cout << root["schedule"][i]["url"] << endl;
                string gpio_id = root["schedule"][i]["igpo"][0];
                shared_ptr<channel_t> vt(new channel_t(root["schedule"][i]["url"],std::stoi(gpio_id)) );

                //cout << "$ filename " << vt->get_cache_filename() << endl;
                if(!vt->load_cached(UPDATE_TIME)) {
                    //cout << "Load from web\n";
                    vt->load_from_url();
                }
                valves.push_back(vt);
            }
        }
    }

    time_t last_reload = time(0); //-2*UPDATE_TIME; // Hack to force the reload on the first iteration

    // Enter into work loop
    while(true)
    {
        bool to_reload;
        if( to_reload = (time(0) - last_reload) > UPDATE_TIME )
            last_reload = time(0);
        for( auto &vt : valves)
        {
            if(to_reload)
            {
                int err;
                if(err=vt->load_from_url() !=NET_SUCCESS) {
                    DEBUG_PRINT(LOG_INFO, "ERROR: Error loading ical" );
                }
            }

            DEBUG_PRINT(LOG_INFO, "INFO: update status");
            if(vt->vc.IsActive() != vt->gpio.GetStatus())
                vt->gpio.SetStatus(vt->vc.IsActive());
        }

        sleep(CHECK_TIME);
    }

    return 0;
}

<|endoftext|>


<commit_before>
#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "Root.h"

#include <exception>
#include <csignal>
#include <stdlib.h>

#ifdef _MSC_VER
	#include <dbghelp.h>
#endif  // _MSC_VER
#include "OSSupport/StackTrace.h"

bool cRoot::m_TerminateEventRaised = false;  // If something has told the server to stop; checked periodically in cRoot
static bool g_ServerTerminated = false;  // Set to true when the server terminates, so our CTRL handler can then tell the OS to close the console

/** If set to true, the protocols will log each player's incoming (C->S) communication to a per-connection logfile */
bool g_ShouldLogCommIn;

/** If set to true, the protocols will log each player's outgoing (S->C) communication to a per-connection logfile */
bool g_ShouldLogCommOut;

/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window
// _X 2014_02_20: Disabled for canon repo, it makes the debug version too slow in MSVC2013
// and we haven't had a memory leak for over a year anyway.
// #define ENABLE_LEAK_FINDER

#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
	#pragma warning(push)
	#pragma warning(disable:4100)
	#include "LeakFinder.h"
	#pragma warning(pop)
#endif

void NonCtrlHandler(int a_Signal)
{
	LOGD("Terminate event raised from std::signal");
	cRoot::m_TerminateEventRaised = true;

	switch (a_Signal)
	{
		case SIGSEGV:
		{
			std::signal(SIGSEGV, SIG_DFL);
			LOGERROR("  D:    | MCServer has encountered an error and needs to close");
			LOGERROR("Details | SIGSEGV: Segmentation fault");
			PrintStackTrace();
			abort();
		}
		case SIGABRT:
		#ifdef SIGABRT_COMPAT
		case SIGABRT_COMPAT:
		#endif
		{
			std::signal(a_Signal, SIG_DFL);
			LOGERROR("  D:    | MCServer has encountered an error and needs to close");
			LOGERROR("Details | SIGABRT: Server self-terminated due to an internal fault");
			PrintStackTrace();
			abort();
		}
		case SIGINT:
		case SIGTERM:
		{
			std::signal(a_Signal, SIG_IGN);  // Server is shutting down, wait for it...
			break;
		}
		default: break;
	}
}

#if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER)
////////////////////////////////////////////////////////////////////////////////
// Windows 32-bit stuff: when the server crashes, create a "dump file" containing the callstack of each thread and some variables; let the user send us that crash file for analysis

typedef BOOL  (WINAPI *pMiniDumpWriteDump)(
	HANDLE hProcess,
	DWORD ProcessId,
	HANDLE hFile,
	MINIDUMP_TYPE DumpType,
	PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
	PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
	PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);

pMiniDumpWriteDump g_WriteMiniDump;  // The function in dbghlp DLL that creates dump files

char g_DumpFileName[MAX_PATH];  // Filename of the dump file; hes to be created before the dump handler kicks in
char g_ExceptionStack[128 * 1024];  // Substitute stack, just in case the handler kicks in because of "insufficient stack space"
MINIDUMP_TYPE g_DumpFlags = MiniDumpNormal;  // By default dump only the stack and some helpers

/** This function gets called just before the "program executed an illegal instruction and will be terminated" or similar.
Its purpose is to create the crashdump using the dbghlp DLLs
*/
LONG WINAPI LastChanceExceptionFilter(__in struct _EXCEPTION_POINTERS * a_ExceptionInfo)
{
	char * newStack = &g_ExceptionStack[sizeof(g_ExceptionStack)];
	char * oldStack;

	// Use the substitute stack:
	// This code is the reason why we don't support 64-bit (yet)
	_asm
	{
		mov oldStack, esp
		mov esp, newStack
	}

	MINIDUMP_EXCEPTION_INFORMATION  ExcInformation;
	ExcInformation.ThreadId = GetCurrentThreadId();
	ExcInformation.ExceptionPointers = a_ExceptionInfo;
	ExcInformation.ClientPointers = 0;

	// Write the dump file:
	HANDLE dumpFile = CreateFile(g_DumpFileName, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
	g_WriteMiniDump(GetCurrentProcess(), GetCurrentProcessId(), dumpFile, g_DumpFlags, (a_ExceptionInfo) ? &ExcInformation : nullptr, nullptr, nullptr);
	CloseHandle(dumpFile);

	// Print the stack trace for the basic debugging:
	PrintStackTrace();

	// Revert to old stack:
	_asm
	{
		mov esp, oldStack
	}

	return 0;
}

#endif  // _WIN32 && !_WIN64

#ifdef _WIN32
// Handle CTRL events in windows, including console window close
BOOL CtrlHandler(DWORD fdwCtrlType)
{
	cRoot::m_TerminateEventRaised = true;
	LOGD("Terminate event raised from the Windows CtrlHandler");

	if (fdwCtrlType == CTRL_CLOSE_EVENT)  // Console window closed via 'x' button, Windows will try to close immediately, therefore...
	{
		while (!g_ServerTerminated) { cSleep::MilliSleep(100); }  // Delay as much as possible to try to get the server to shut down cleanly
	}

	return TRUE;
}
#endif

////////////////////////////////////////////////////////////////////////////////
// main:

int main( int argc, char **argv)
{
	UNUSED(argc);
	UNUSED(argv);

	#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
	InitLeakFinder();
	#endif

	// Magic code to produce dump-files on Windows if the server crashes:
	#if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER)
	HINSTANCE hDbgHelp = LoadLibrary("DBGHELP.DLL");
	g_WriteMiniDump = (pMiniDumpWriteDump)GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
	if (g_WriteMiniDump != nullptr)
	{
		_snprintf_s(g_DumpFileName, ARRAYCOUNT(g_DumpFileName), _TRUNCATE, "crash_mcs_%x.dmp", GetCurrentProcessId());
		SetUnhandledExceptionFilter(LastChanceExceptionFilter);

		// Parse arguments for minidump flags:
		for (int i = 0; i < argc; i++)
		{
			if (_stricmp(argv[i], "/cdg") == 0)
			{
				// Add globals to the dump
				g_DumpFlags = (MINIDUMP_TYPE)(g_DumpFlags | MiniDumpWithDataSegs);
			}
			else if (_stricmp(argv[i], "/cdf") == 0)
			{
				// Add full memory to the dump (HUUUGE file)
				g_DumpFlags = (MINIDUMP_TYPE)(g_DumpFlags | MiniDumpWithFullMemory);
			}
		}  // for i - argv[]
	}
	#endif  // _WIN32 && !_WIN64
	// End of dump-file magic

	#ifdef _WIN32
	if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE))
	{
		LOGERROR("Could not install the Windows CTRL handler!");
	}
	#endif

	#if defined(_DEBUG) && defined(_MSC_VER)
	_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

	// _X: The simple built-in CRT leak finder - simply break when allocating the Nth block ({N} is listed in the leak output)
	// Only useful when the leak is in the same sequence all the time
	// _CrtSetBreakAlloc(85950);

	#endif  // _DEBUG && _MSC_VER

	#ifndef _DEBUG
	std::signal(SIGSEGV, NonCtrlHandler);
	std::signal(SIGTERM, NonCtrlHandler);
	std::signal(SIGINT,  NonCtrlHandler);
	std::signal(SIGABRT, NonCtrlHandler);
	#ifdef SIGABRT_COMPAT
	std::signal(SIGABRT_COMPAT, NonCtrlHandler);
	#endif  // SIGABRT_COMPAT
	#endif

	// DEBUG: test the dumpfile creation:
	// *((int *)0) = 0;

	// Check if comm logging is to be enabled:
	for (int i = 0; i < argc; i++)
	{
		AString Arg(argv[i]);
		if (
			(NoCaseCompare(Arg, "/commlog") == 0) ||
			(NoCaseCompare(Arg, "/logcomm") == 0)
		)
		{
			g_ShouldLogCommIn = true;
			g_ShouldLogCommOut = true;
		}
		else if (
			(NoCaseCompare(Arg, "/commlogin") == 0) ||
			(NoCaseCompare(Arg, "/comminlog") == 0) ||
			(NoCaseCompare(Arg, "/logcommin") == 0)
		)
		{
			g_ShouldLogCommIn = true;
		}
		else if (
			(NoCaseCompare(Arg, "/commlogout") == 0) ||
			(NoCaseCompare(Arg, "/commoutlog") == 0) ||
			(NoCaseCompare(Arg, "/logcommout") == 0)
		)
		{
			g_ShouldLogCommOut = true;
		}
		else if (NoCaseCompare(Arg, "nooutbuf") == 0)
		{
			setvbuf(stdout, nullptr, _IONBF, 0);
		}
	}  // for i - argv[]

	cLogger::InitiateMultithreading();

	#if !defined(ANDROID_NDK)
	try
	#endif
	{
		cRoot Root;
		Root.Start();
	}
	#if !defined(ANDROID_NDK)
	catch (std::exception & e)
	{
		LOGERROR("Standard exception: %s", e.what());
	}
	catch (...)
	{
		LOGERROR("Unknown exception!");
	}
	#endif

	#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
	DeinitLeakFinder();
	#endif

	g_ServerTerminated = true;

	return EXIT_SUCCESS;
}

<commit_msg>Removed unneeded include.<commit_after>
#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "Root.h"

#include <exception>
#include <csignal>
#include <stdlib.h>

#ifdef _MSC_VER
	#include <dbghelp.h>
#endif  // _MSC_VER

bool cRoot::m_TerminateEventRaised = false;  // If something has told the server to stop; checked periodically in cRoot
static bool g_ServerTerminated = false;  // Set to true when the server terminates, so our CTRL handler can then tell the OS to close the console

/** If set to true, the protocols will log each player's incoming (C->S) communication to a per-connection logfile */
bool g_ShouldLogCommIn;

/** If set to true, the protocols will log each player's outgoing (S->C) communication to a per-connection logfile */
bool g_ShouldLogCommOut;

/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window
// _X 2014_02_20: Disabled for canon repo, it makes the debug version too slow in MSVC2013
// and we haven't had a memory leak for over a year anyway.
// #define ENABLE_LEAK_FINDER

#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
	#pragma warning(push)
	#pragma warning(disable:4100)
	#include "LeakFinder.h"
	#pragma warning(pop)
#endif

void NonCtrlHandler(int a_Signal)
{
	LOGD("Terminate event raised from std::signal");
	cRoot::m_TerminateEventRaised = true;

	switch (a_Signal)
	{
		case SIGSEGV:
		{
			std::signal(SIGSEGV, SIG_DFL);
			LOGERROR("  D:    | MCServer has encountered an error and needs to close");
			LOGERROR("Details | SIGSEGV: Segmentation fault");
			PrintStackTrace();
			abort();
		}
		case SIGABRT:
		#ifdef SIGABRT_COMPAT
		case SIGABRT_COMPAT:
		#endif
		{
			std::signal(a_Signal, SIG_DFL);
			LOGERROR("  D:    | MCServer has encountered an error and needs to close");
			LOGERROR("Details | SIGABRT: Server self-terminated due to an internal fault");
			PrintStackTrace();
			abort();
		}
		case SIGINT:
		case SIGTERM:
		{
			std::signal(a_Signal, SIG_IGN);  // Server is shutting down, wait for it...
			break;
		}
		default: break;
	}
}

#if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER)
////////////////////////////////////////////////////////////////////////////////
// Windows 32-bit stuff: when the server crashes, create a "dump file" containing the callstack of each thread and some variables; let the user send us that crash file for analysis

typedef BOOL  (WINAPI *pMiniDumpWriteDump)(
	HANDLE hProcess,
	DWORD ProcessId,
	HANDLE hFile,
	MINIDUMP_TYPE DumpType,
	PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
	PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
	PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);

pMiniDumpWriteDump g_WriteMiniDump;  // The function in dbghlp DLL that creates dump files

char g_DumpFileName[MAX_PATH];  // Filename of the dump file; hes to be created before the dump handler kicks in
char g_ExceptionStack[128 * 1024];  // Substitute stack, just in case the handler kicks in because of "insufficient stack space"
MINIDUMP_TYPE g_DumpFlags = MiniDumpNormal;  // By default dump only the stack and some helpers

/** This function gets called just before the "program executed an illegal instruction and will be terminated" or similar.
Its purpose is to create the crashdump using the dbghlp DLLs
*/
LONG WINAPI LastChanceExceptionFilter(__in struct _EXCEPTION_POINTERS * a_ExceptionInfo)
{
	char * newStack = &g_ExceptionStack[sizeof(g_ExceptionStack)];
	char * oldStack;

	// Use the substitute stack:
	// This code is the reason why we don't support 64-bit (yet)
	_asm
	{
		mov oldStack, esp
		mov esp, newStack
	}

	MINIDUMP_EXCEPTION_INFORMATION  ExcInformation;
	ExcInformation.ThreadId = GetCurrentThreadId();
	ExcInformation.ExceptionPointers = a_ExceptionInfo;
	ExcInformation.ClientPointers = 0;

	// Write the dump file:
	HANDLE dumpFile = CreateFile(g_DumpFileName, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
	g_WriteMiniDump(GetCurrentProcess(), GetCurrentProcessId(), dumpFile, g_DumpFlags, (a_ExceptionInfo) ? &ExcInformation : nullptr, nullptr, nullptr);
	CloseHandle(dumpFile);

	// Print the stack trace for the basic debugging:
	PrintStackTrace();

	// Revert to old stack:
	_asm
	{
		mov esp, oldStack
	}

	return 0;
}

#endif  // _WIN32 && !_WIN64

#ifdef _WIN32
// Handle CTRL events in windows, including console window close
BOOL CtrlHandler(DWORD fdwCtrlType)
{
	cRoot::m_TerminateEventRaised = true;
	LOGD("Terminate event raised from the Windows CtrlHandler");

	if (fdwCtrlType == CTRL_CLOSE_EVENT)  // Console window closed via 'x' button, Windows will try to close immediately, therefore...
	{
		while (!g_ServerTerminated) { cSleep::MilliSleep(100); }  // Delay as much as possible to try to get the server to shut down cleanly
	}

	return TRUE;
}
#endif

////////////////////////////////////////////////////////////////////////////////
// main:

int main( int argc, char **argv)
{
	UNUSED(argc);
	UNUSED(argv);

	#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
	InitLeakFinder();
	#endif

	// Magic code to produce dump-files on Windows if the server crashes:
	#if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER)
	HINSTANCE hDbgHelp = LoadLibrary("DBGHELP.DLL");
	g_WriteMiniDump = (pMiniDumpWriteDump)GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
	if (g_WriteMiniDump != nullptr)
	{
		_snprintf_s(g_DumpFileName, ARRAYCOUNT(g_DumpFileName), _TRUNCATE, "crash_mcs_%x.dmp", GetCurrentProcessId());
		SetUnhandledExceptionFilter(LastChanceExceptionFilter);

		// Parse arguments for minidump flags:
		for (int i = 0; i < argc; i++)
		{
			if (_stricmp(argv[i], "/cdg") == 0)
			{
				// Add globals to the dump
				g_DumpFlags = (MINIDUMP_TYPE)(g_DumpFlags | MiniDumpWithDataSegs);
			}
			else if (_stricmp(argv[i], "/cdf") == 0)
			{
				// Add full memory to the dump (HUUUGE file)
				g_DumpFlags = (MINIDUMP_TYPE)(g_DumpFlags | MiniDumpWithFullMemory);
			}
		}  // for i - argv[]
	}
	#endif  // _WIN32 && !_WIN64
	// End of dump-file magic

	#ifdef _WIN32
	if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE))
	{
		LOGERROR("Could not install the Windows CTRL handler!");
	}
	#endif

	#if defined(_DEBUG) && defined(_MSC_VER)
	_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

	// _X: The simple built-in CRT leak finder - simply break when allocating the Nth block ({N} is listed in the leak output)
	// Only useful when the leak is in the same sequence all the time
	// _CrtSetBreakAlloc(85950);

	#endif  // _DEBUG && _MSC_VER

	#ifndef _DEBUG
	std::signal(SIGSEGV, NonCtrlHandler);
	std::signal(SIGTERM, NonCtrlHandler);
	std::signal(SIGINT,  NonCtrlHandler);
	std::signal(SIGABRT, NonCtrlHandler);
	#ifdef SIGABRT_COMPAT
	std::signal(SIGABRT_COMPAT, NonCtrlHandler);
	#endif  // SIGABRT_COMPAT
	#endif

	// DEBUG: test the dumpfile creation:
	// *((int *)0) = 0;

	// Check if comm logging is to be enabled:
	for (int i = 0; i < argc; i++)
	{
		AString Arg(argv[i]);
		if (
			(NoCaseCompare(Arg, "/commlog") == 0) ||
			(NoCaseCompare(Arg, "/logcomm") == 0)
		)
		{
			g_ShouldLogCommIn = true;
			g_ShouldLogCommOut = true;
		}
		else if (
			(NoCaseCompare(Arg, "/commlogin") == 0) ||
			(NoCaseCompare(Arg, "/comminlog") == 0) ||
			(NoCaseCompare(Arg, "/logcommin") == 0)
		)
		{
			g_ShouldLogCommIn = true;
		}
		else if (
			(NoCaseCompare(Arg, "/commlogout") == 0) ||
			(NoCaseCompare(Arg, "/commoutlog") == 0) ||
			(NoCaseCompare(Arg, "/logcommout") == 0)
		)
		{
			g_ShouldLogCommOut = true;
		}
		else if (NoCaseCompare(Arg, "nooutbuf") == 0)
		{
			setvbuf(stdout, nullptr, _IONBF, 0);
		}
	}  // for i - argv[]

	cLogger::InitiateMultithreading();

	#if !defined(ANDROID_NDK)
	try
	#endif
	{
		cRoot Root;
		Root.Start();
	}
	#if !defined(ANDROID_NDK)
	catch (std::exception & e)
	{
		LOGERROR("Standard exception: %s", e.what());
	}
	catch (...)
	{
		LOGERROR("Unknown exception!");
	}
	#endif

	#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
	DeinitLeakFinder();
	#endif

	g_ServerTerminated = true;

	return EXIT_SUCCESS;
}

<|endoftext|>


<commit_before>#pragma once

#include "../abstract_kernel.hpp"
#include "parameter_result_cache.hpp"

#include <chrono>

namespace autotune {

template <typename R, typename... Args> class with_tests {
private:
  std::function<bool(R)> t;

public:
  void setup_test(std::function<bool(R)> t_) { t = t_; };

  bool has_test() { return t ? true : false; }

  bool test(R r) { return t(r); };
};

template <typename R, typename... Args> class without_tests {};

template <typename parameter_interface, typename R, typename... Args>
class abstract_tuner
    : public std::conditional<!std::is_same<R, void>::value,
                              with_tests<R, Args...>,
                              without_tests<R, Args...>>::type {
protected:
  autotune::abstract_kernel<R, cppjit::detail::pack<Args...>> &f;
  parameter_interface parameters;
  parameter_value_set optimal_parameter_values;
  double optimal_duration;
  bool verbose;
  bool do_measurement;
  bool do_write_header;
  std::ofstream scenario_kernel_duration_file;
  std::ofstream scenario_compile_duration_file;

  parameter_result_cache<parameter_interface> result_cache;

  std::function<void(parameter_interface &)> parameter_adjustment_functor;
  std::function<void(parameter_interface &, const parameter_value_set &)>
      extended_parameter_adjustment_functor;

  size_t repetitions = 1;

public:
  abstract_tuner(autotune::abstract_kernel<R, cppjit::detail::pack<Args...>> &f,
                 parameter_interface &parameters)
      : f(f), parameters(parameters), optimal_duration(-1.0), verbose(false),
         do_measurement(false), do_write_header(true) {}

  double evaluate(bool &did_eval, Args &... args) {

    if (!result_cache.contains(parameters)) {
      result_cache.insert(parameters);
    } else {
      did_eval = false;

      if (verbose) {
        std::cout << "------ skipped eval ------" << std::endl;
        parameters.print_values();
        std::cout << "--------------------------" << std::endl;
      }
      return std::numeric_limits<double>::max();
    }

    parameter_interface original_parameters = parameters;
    if (parameter_adjustment_functor || extended_parameter_adjustment_functor) {
      if (verbose) {
        std::cout << "------ parameters pre-adjustment ------" << std::endl;
        parameters.print_values();
        std::cout << "--------------------------" << std::endl;
      }
      if (parameter_adjustment_functor)
        parameter_adjustment_functor(parameters);
      else if (extended_parameter_adjustment_functor)
        extended_parameter_adjustment_functor(parameters,
                                              f.get_parameter_values());
    }

    parameter_value_set parameter_values = f.get_parameter_values();
    for (size_t parameter_index = 0; parameter_index < parameters.size();
             parameter_index++) {
      auto &p = parameters[parameter_index];
      parameter_values[p->get_name()] = p->get_value();
    }
    if (!f.precompile_validate_parameters(parameter_values)) {
      if (verbose) {
        std::cout << "------ invalidated eval (precompile) ------" << std::endl;
        parameters.print_values();
        std::cout << "--------------------------" << std::endl;
      }
      did_eval = false;
      return std::numeric_limits<double>::max();
    } else {
      if (verbose) {
        std::cout << "parameter combination passed precompile check"
                  << std::endl;
      }
    }

    f.set_parameter_values(parameter_values);

    if (do_measurement && do_write_header) {
      this->write_header();
      do_write_header = false;
    }

    did_eval = true;

    if (verbose) {
      std::cout << "------ begin eval ------" << std::endl;
      //parameters.print_values();
      print_parameter_values(parameter_values);
    }

    f.create_parameter_file();

    auto start_compile = std::chrono::high_resolution_clock::now();
    f.compile();
    auto end_compile = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration_compile =
        end_compile - start_compile;

    if (!f.is_valid_parameter_combination()) {
      if (verbose) {
        std::cout << "invalid parameter combination encountered" << std::endl;
      }
      did_eval = false;
      return std::numeric_limits<double>::max();
    } else {
      if (verbose) {
        std::cout << "parameter combination is valid" << std::endl;
      }
    }

    auto start = std::chrono::high_resolution_clock::now();

    // call kernel, discard possibly returned values
    if
      constexpr(!std::is_same<R, void>::value) {
        if (this->has_test()) {
          for (size_t i = 0; i < repetitions; i++) {
            bool test_ok = this->test(f(args...));
            if (!test_ok) {
              if (verbose) {
                std::cout << "warning: test for combination failed!"
                          << std::endl;
              }
              return std::numeric_limits<double>::max();
            } else {
              if (verbose) {
                std::cout << "test for combination passed" << std::endl;
              }
            }
          }
        } else {
          for (size_t i = 0; i < repetitions; i++) {
            f(args...);
          }
        }
      }
    else {
      for (size_t i = 0; i < repetitions; i++) {
        f(args...);
      }
    }

    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration = end - start;

    if (verbose) {
      if (f.has_kernel_duration_functor()) {
        std::cout << "internal duration: " << f.get_internal_kernel_duration()
                  << std::endl;
        if (repetitions > 1) {
          std::cout << "internal duration per repetition: "
                    << (f.get_internal_kernel_duration() /
                        static_cast<double>(repetitions))
                    << std::endl;
        }
        std::cout << "(duration tuner: " << duration.count() << "s)"
                  << std::endl;
        if (repetitions > 1) {
          std::cout << "(duration tuner per repetition: "
                    << (duration.count() / static_cast<double>(repetitions))
                    << "s)" << std::endl;
        }
      } else {
        std::cout << "duration: " << duration.count() << "s" << std::endl;
        if (repetitions > 1) {
          std::cout << "duration tuner per reptition: "
                    << (duration.count() / static_cast<double>(repetitions))
                    << "s" << std::endl;
        }
        std::cout << "------- end eval -------" << std::endl;
      }
    }

    if (parameter_adjustment_functor || extended_parameter_adjustment_functor) {
      parameters = original_parameters;
    }

    double final_duration;
    if (f.has_kernel_duration_functor()) {
      if (do_measurement) {
        this->write_measurement(f.get_internal_kernel_duration(),
                                duration_compile.count());
      }
      final_duration = f.get_internal_kernel_duration();
    } else {
      if (do_measurement) {
        this->write_measurement(duration.count(), duration_compile.count());
      }
      final_duration = duration.count();
    }
    if (optimal_duration < 0.0 || final_duration < optimal_duration) {
      optimal_duration = final_duration;
      optimal_parameter_values = parameter_values;
    }
    return final_duration;
  }

  const parameter_value_set& get_optimal_parameter_values() const {
    if (optimal_duration < 0.0)
      return f.get_parameter_values();
    else
      return optimal_parameter_values;
  }

  void report(const std::string &message, double duration,
              parameter_interface &parameters) {
    std::cout << message << "; duration: " << duration << std::endl;
    parameters.print_values();
  }

  void set_verbose(bool verbose) { this->verbose = verbose; }

  void report_verbose(const std::string &message, double duration,
                      parameter_interface &parameters) {
    if (verbose) {
      report(message, duration, parameters);
    }
  }

  void write_header() {
    const parameter_value_set &parameter_values = f.get_parameter_values();
    bool first = true;
    for (auto &p : parameter_values) {
      if (!first) {
        scenario_kernel_duration_file << ", ";
        scenario_compile_duration_file << ", ";
      } else {
        first = false;
      }
      scenario_kernel_duration_file << p.first;
      scenario_compile_duration_file << p.first;
    }
    scenario_kernel_duration_file << ", "
                                  << "duration" << std::endl;
    scenario_compile_duration_file << ", "
                                   << "duration" << std::endl;
  }

  void write_measurement(double duration_kernel_s, double duration_compile_s) {
    const parameter_value_set &parameter_values = f.get_parameter_values();
    bool first = true;
    for (auto &p : parameter_values) {
      if (!first) {
        scenario_kernel_duration_file << ", ";
        scenario_compile_duration_file << ", ";
      } else {
        first = false;
      }
      scenario_kernel_duration_file << p.second;
      scenario_compile_duration_file << p.second;
    }
    scenario_kernel_duration_file << ", " << duration_kernel_s << std::endl;
    scenario_compile_duration_file << ", " << duration_compile_s << std::endl;
  }

  void set_write_measurement(const std::string &scenario_name) {
    if (do_measurement) {
      if (scenario_kernel_duration_file.is_open()) {
        scenario_kernel_duration_file.close();
      }
      if (scenario_compile_duration_file.is_open()) {
        scenario_compile_duration_file.close();
      }
    }
    do_measurement = true;
    do_write_header = true;
    scenario_kernel_duration_file.open(scenario_name + "_kernel_duration.csv");
    scenario_compile_duration_file.open(scenario_name +
                                        "_compile_duration.csv");
  }

  void set_parameter_adjustment_functor(
      std::function<void(parameter_interface &)> parameter_adjustment_functor) {
    this->parameter_adjustment_functor = parameter_adjustment_functor;
    this->extended_parameter_adjustment_functor = nullptr;
  }

  void set_parameter_adjustment_functor(
      std::function<void(parameter_interface &, const parameter_value_set &)>
          parameter_adjustment_functor) {
    this->extended_parameter_adjustment_functor = parameter_adjustment_functor;
    this->parameter_adjustment_functor = [this](parameter_interface &parameters) -> void {
      this->extended_parameter_adjustment_functor(parameters, this->f.get_parameter_values());
    };
  }

  // execute kernel multiple times to average across the result
  void set_repetitions(size_t repetitions) { this->repetitions = repetitions; }
};
} // namespace autotune
<commit_msg>added additional verbose info to abstract_tuner eval<commit_after>#pragma once

#include "../abstract_kernel.hpp"
#include "parameter_result_cache.hpp"

#include <chrono>

namespace autotune {

template <typename R, typename... Args> class with_tests {
private:
  std::function<bool(R)> t;

public:
  void setup_test(std::function<bool(R)> t_) { t = t_; };

  bool has_test() { return t ? true : false; }

  bool test(R r) { return t(r); };
};

template <typename R, typename... Args> class without_tests {};

template <typename parameter_interface, typename R, typename... Args>
class abstract_tuner
    : public std::conditional<!std::is_same<R, void>::value,
                              with_tests<R, Args...>,
                              without_tests<R, Args...>>::type {
protected:
  autotune::abstract_kernel<R, cppjit::detail::pack<Args...>> &f;
  parameter_interface parameters;
  parameter_value_set optimal_parameter_values;
  double optimal_duration;
  bool verbose;
  bool do_measurement;
  bool do_write_header;
  std::ofstream scenario_kernel_duration_file;
  std::ofstream scenario_compile_duration_file;

  parameter_result_cache<parameter_interface> result_cache;

  std::function<void(parameter_interface &)> parameter_adjustment_functor;
  std::function<void(parameter_interface &, const parameter_value_set &)>
      extended_parameter_adjustment_functor;

  size_t repetitions = 1;

public:
  abstract_tuner(autotune::abstract_kernel<R, cppjit::detail::pack<Args...>> &f,
                 parameter_interface &parameters)
      : f(f), parameters(parameters), optimal_duration(-1.0), verbose(false),
         do_measurement(false), do_write_header(true) {}

  double evaluate(bool &did_eval, Args &... args) {
    if (verbose) {
      parameter_value_set parameter_values = f.get_parameter_values();
      for (size_t parameter_index = 0; parameter_index < parameters.size();
               parameter_index++) {
        auto &p = parameters[parameter_index];
        parameter_values[p->get_name()] = p->get_value();
      }
      std::cout << "------ try eval ------" << std::endl;
      //parameters.print_values();
      print_parameter_values(parameter_values);
    }
    if (!result_cache.contains(parameters)) {
      result_cache.insert(parameters);
    } else {
      did_eval = false;

      if (verbose) {
        std::cout << "------ skipped eval ------" << std::endl;
        parameters.print_values();
        std::cout << "--------------------------" << std::endl;
      }
      return std::numeric_limits<double>::max();
    }

    parameter_interface original_parameters = parameters;
    if (parameter_adjustment_functor || extended_parameter_adjustment_functor) {
      if (verbose) {
        std::cout << "------ parameters pre-adjustment ------" << std::endl;
        parameters.print_values();
        std::cout << "--------------------------" << std::endl;
      }
      if (parameter_adjustment_functor)
        parameter_adjustment_functor(parameters);
      else if (extended_parameter_adjustment_functor)
        extended_parameter_adjustment_functor(parameters,
                                              f.get_parameter_values());
    }

    parameter_value_set parameter_values = f.get_parameter_values();
    for (size_t parameter_index = 0; parameter_index < parameters.size();
             parameter_index++) {
      auto &p = parameters[parameter_index];
      parameter_values[p->get_name()] = p->get_value();
    }
    if (!f.precompile_validate_parameters(parameter_values)) {
      if (verbose) {
        std::cout << "------ invalidated eval (precompile) ------" << std::endl;
        parameters.print_values();
        std::cout << "--------------------------" << std::endl;
      }
      did_eval = false;
      return std::numeric_limits<double>::max();
    } else {
      if (verbose) {
        std::cout << "parameter combination passed precompile check"
                  << std::endl;
      }
    }

    f.set_parameter_values(parameter_values);

    if (do_measurement && do_write_header) {
      this->write_header();
      do_write_header = false;
    }

    did_eval = true;

    if (verbose) {
      std::cout << "------ begin eval ------" << std::endl;
      //parameters.print_values();
      print_parameter_values(parameter_values);
    }

    f.create_parameter_file();

    auto start_compile = std::chrono::high_resolution_clock::now();
    f.compile();
    auto end_compile = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration_compile =
        end_compile - start_compile;

    if (!f.is_valid_parameter_combination()) {
      if (verbose) {
        std::cout << "invalid parameter combination encountered" << std::endl;
      }
      did_eval = false;
      return std::numeric_limits<double>::max();
    } else {
      if (verbose) {
        std::cout << "parameter combination is valid" << std::endl;
      }
    }

    auto start = std::chrono::high_resolution_clock::now();

    // call kernel, discard possibly returned values
    if
      constexpr(!std::is_same<R, void>::value) {
        if (this->has_test()) {
          for (size_t i = 0; i < repetitions; i++) {
            bool test_ok = this->test(f(args...));
            if (!test_ok) {
              if (verbose) {
                std::cout << "warning: test for combination failed!"
                          << std::endl;
              }
              return std::numeric_limits<double>::max();
            } else {
              if (verbose) {
                std::cout << "test for combination passed" << std::endl;
              }
            }
          }
        } else {
          for (size_t i = 0; i < repetitions; i++) {
            f(args...);
          }
        }
      }
    else {
      for (size_t i = 0; i < repetitions; i++) {
        f(args...);
      }
    }

    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration = end - start;

    if (verbose) {
      if (f.has_kernel_duration_functor()) {
        std::cout << "internal duration: " << f.get_internal_kernel_duration()
                  << std::endl;
        if (repetitions > 1) {
          std::cout << "internal duration per repetition: "
                    << (f.get_internal_kernel_duration() /
                        static_cast<double>(repetitions))
                    << std::endl;
        }
        std::cout << "(duration tuner: " << duration.count() << "s)"
                  << std::endl;
        if (repetitions > 1) {
          std::cout << "(duration tuner per repetition: "
                    << (duration.count() / static_cast<double>(repetitions))
                    << "s)" << std::endl;
        }
      } else {
        std::cout << "duration: " << duration.count() << "s" << std::endl;
        if (repetitions > 1) {
          std::cout << "duration tuner per reptition: "
                    << (duration.count() / static_cast<double>(repetitions))
                    << "s" << std::endl;
        }
        std::cout << "------- end eval -------" << std::endl;
      }
    }

    if (parameter_adjustment_functor || extended_parameter_adjustment_functor) {
      parameters = original_parameters;
    }

    double final_duration;
    if (f.has_kernel_duration_functor()) {
      if (do_measurement) {
        this->write_measurement(f.get_internal_kernel_duration(),
                                duration_compile.count());
      }
      final_duration = f.get_internal_kernel_duration();
    } else {
      if (do_measurement) {
        this->write_measurement(duration.count(), duration_compile.count());
      }
      final_duration = duration.count();
    }
    if (optimal_duration < 0.0 || final_duration < optimal_duration) {
      optimal_duration = final_duration;
      optimal_parameter_values = parameter_values;
    }
    return final_duration;
  }

  const parameter_value_set& get_optimal_parameter_values() const {
    if (optimal_duration < 0.0)
      return f.get_parameter_values();
    else
      return optimal_parameter_values;
  }

  void report(const std::string &message, double duration,
              parameter_interface &parameters) {
    std::cout << message << "; duration: " << duration << std::endl;
    parameters.print_values();
  }

  void set_verbose(bool verbose) { this->verbose = verbose; }

  void report_verbose(const std::string &message, double duration,
                      parameter_interface &parameters) {
    if (verbose) {
      report(message, duration, parameters);
    }
  }

  void write_header() {
    const parameter_value_set &parameter_values = f.get_parameter_values();
    bool first = true;
    for (auto &p : parameter_values) {
      if (!first) {
        scenario_kernel_duration_file << ", ";
        scenario_compile_duration_file << ", ";
      } else {
        first = false;
      }
      scenario_kernel_duration_file << p.first;
      scenario_compile_duration_file << p.first;
    }
    scenario_kernel_duration_file << ", "
                                  << "duration" << std::endl;
    scenario_compile_duration_file << ", "
                                   << "duration" << std::endl;
  }

  void write_measurement(double duration_kernel_s, double duration_compile_s) {
    const parameter_value_set &parameter_values = f.get_parameter_values();
    bool first = true;
    for (auto &p : parameter_values) {
      if (!first) {
        scenario_kernel_duration_file << ", ";
        scenario_compile_duration_file << ", ";
      } else {
        first = false;
      }
      scenario_kernel_duration_file << p.second;
      scenario_compile_duration_file << p.second;
    }
    scenario_kernel_duration_file << ", " << duration_kernel_s << std::endl;
    scenario_compile_duration_file << ", " << duration_compile_s << std::endl;
  }

  void set_write_measurement(const std::string &scenario_name) {
    if (do_measurement) {
      if (scenario_kernel_duration_file.is_open()) {
        scenario_kernel_duration_file.close();
      }
      if (scenario_compile_duration_file.is_open()) {
        scenario_compile_duration_file.close();
      }
    }
    do_measurement = true;
    do_write_header = true;
    scenario_kernel_duration_file.open(scenario_name + "_kernel_duration.csv");
    scenario_compile_duration_file.open(scenario_name +
                                        "_compile_duration.csv");
  }

  void set_parameter_adjustment_functor(
      std::function<void(parameter_interface &)> parameter_adjustment_functor) {
    this->parameter_adjustment_functor = parameter_adjustment_functor;
    this->extended_parameter_adjustment_functor = nullptr;
  }

  void set_parameter_adjustment_functor(
      std::function<void(parameter_interface &, const parameter_value_set &)>
          parameter_adjustment_functor) {
    this->extended_parameter_adjustment_functor = parameter_adjustment_functor;
    this->parameter_adjustment_functor = [this](parameter_interface &parameters) -> void {
      this->extended_parameter_adjustment_functor(parameters, this->f.get_parameter_values());
    };
  }

  // execute kernel multiple times to average across the result
  void set_repetitions(size_t repetitions) { this->repetitions = repetitions; }
};
} // namespace autotune
<|endoftext|>


<commit_before>#pragma once

#include "depthai-shared/utility/Serialization.hpp"
#include "tl/optional.hpp"

// tl::optional serialization for nlohmann json
// partial specialization (full specialization works too)
namespace nlohmann {
template <typename T>
struct adl_serializer<tl::optional<T>> {
    static void to_json(json& j, const tl::optional<T>& opt) {  // NOLINT this is a specialization, naming conventions don't apply
        if(opt == tl::nullopt) {
            j = nullptr;
        } else {
            j = *opt;  // this will call adl_serializer<T>::to_json which will
                       // find the free function to_json in T's namespace!
        }
    }

    static void from_json(const json& j, tl::optional<T>& opt) {  // NOLINT this is a specialization, naming conventions don't apply
        if(j.is_null()) {
            opt = tl::nullopt;
        } else {
            opt = j.get<T>();  // same as above, but with
                               // adl_serializer<T>::from_json
        }
    }
};
}  // namespace nlohmann

// tl::optional serialization for libnop
namespace nop {

//
// Optional<T> encoding formats:
//
// Empty Optional<T>:
//
// +-----+
// | NIL |
// +-----+
//
// Non-empty Optional<T>
//
// +---//----+
// | ELEMENT |
// +---//----+
//
// Element must be a valid encoding of type T.
//

template <typename T>
struct Encoding<tl::optional<T>> : EncodingIO<tl::optional<T>> {
    using Type = tl::optional<T>;

    static constexpr EncodingByte Prefix(const Type& value) {
        return value ? Encoding<T>::Prefix(*value) : EncodingByte::Nil;
    }

    static constexpr std::size_t Size(const Type& value) {
        return value ? Encoding<T>::Size(*value) : BaseEncodingSize(EncodingByte::Nil);
    }

    static constexpr bool Match(EncodingByte prefix) {
        return prefix == EncodingByte::Nil || Encoding<T>::Match(prefix);
    }

    template <typename Writer>
    static constexpr Status<void> WritePayload(EncodingByte prefix, const Type& value, Writer* writer) {
        if(value) {
            return Encoding<T>::WritePayload(prefix, *value, writer);
        } else {
            return {};
        }
    }

    template <typename Reader>
    static constexpr Status<void> ReadPayload(EncodingByte prefix, Type* value, Reader* reader) {
        if(prefix == EncodingByte::Nil) {
            value->reset();
        } else {
            T temp;
            auto status = Encoding<T>::ReadPayload(prefix, &temp, reader);
            if(!status) return status;

            *value = std::move(temp);
        }

        return {};
    }
};

}  // namespace nop<commit_msg>Updated libnop optional<commit_after>#pragma once

#include "depthai-shared/utility/Serialization.hpp"
#include "tl/optional.hpp"

// tl::optional serialization for nlohmann json
// partial specialization (full specialization works too)
namespace nlohmann {
template <typename T>
struct adl_serializer<tl::optional<T>> {
    static void to_json(json& j, const tl::optional<T>& opt) {  // NOLINT this is a specialization, naming conventions don't apply
        if(opt == tl::nullopt) {
            j = nullptr;
        } else {
            j = *opt;  // this will call adl_serializer<T>::to_json which will
                       // find the free function to_json in T's namespace!
        }
    }

    static void from_json(const json& j, tl::optional<T>& opt) {  // NOLINT this is a specialization, naming conventions don't apply
        if(j.is_null()) {
            opt = tl::nullopt;
        } else {
            opt = j.get<T>();  // same as above, but with
                               // adl_serializer<T>::from_json
        }
    }
};
}  // namespace nlohmann

// tl::optional serialization for libnop
namespace nop {

//
// Optional<T> encoding formats:
//
// Empty Optional<T>:
//
// +-----+
// | NIL |
// +-----+
//
// Non-empty Optional<T>
//
// +---//----+
// | ELEMENT |
// +---//----+
//
// Element must be a valid encoding of type T.
//

template <typename T>
struct Encoding<tl::optional<T>> : EncodingIO<tl::optional<T>> {
    using Type = tl::optional<T>;

    static constexpr EncodingByte Prefix(const Type& value) {
        return value ? Encoding<T>::Prefix(*value) : EncodingByte::Empty;
    }

    static constexpr std::size_t Size(const Type& value) {
        return value ? Encoding<T>::Size(*value) : BaseEncodingSize(EncodingByte::Empty);
    }

    static constexpr bool Match(EncodingByte prefix) {
        return prefix == EncodingByte::Empty || Encoding<T>::Match(prefix);
    }

    template <typename Writer>
    static constexpr Status<void> WritePayload(EncodingByte prefix, const Type& value, Writer* writer) {
        if(value) {
            return Encoding<T>::WritePayload(prefix, *value, writer);
        } else {
            return {};
        }
    }

    template <typename Reader>
    static constexpr Status<void> ReadPayload(EncodingByte prefix, Type* value, Reader* reader) {
        if(prefix == EncodingByte::Empty) {
            value->reset();
        } else {
            T temp;
            auto status = Encoding<T>::ReadPayload(prefix, &temp, reader);
            if(!status) return status;

            *value = std::move(temp);
        }

        return {};
    }
};

}  // namespace nop<|endoftext|>


<commit_before>//
//  DihedralIsometry.cpp
//  GroupTheory
//
//  Created by Donald Pinckney on 11/16/15.
//  Copyright © 2015 Donald Pinckney. All rights reserved.
//

#include "DihedralIsometry.h"

int rotationMod(int rotationIndex, int mod) {
    int mult = abs(rotationIndex) / mod + 1;
    return (rotationIndex + mult*mod) % mod;
}

// NOTE: THIS ALLOCATES MEMORY! YOU MUST CALL DELETE WHEN DONE.
DihedralIsometry *DihedralIsometry::compose(const DihedralIsometry &x, const DihedralIsometry &y) {
    bool reflects = x.reflects != y.reflects;

    int rotationIndex;
    if(x.reflects) {
        rotationIndex = x.rotationIndex - y.rotationIndex;
    } else {
        rotationIndex = x.rotationIndex + y.rotationIndex;
    }
    rotationIndex = rotationMod(rotationIndex, x.n);

    DihedralIsometry *iso = new DihedralIsometry(x.n, reflects, rotationIndex);

    return iso;
}

// NOTE: THIS ALLOCATES MEMORY! YOU MUST CALL DELETE WHEN DONE.
DihedralIsometry *DihedralIsometry::generateIsometries(unsigned int n, unsigned int *count) {
    *count = 2*n;

    DihedralIsometry *isos = new DihedralIsometry[*count];

    for (unsigned int i = 0; i < n; i++) {
        isos[i].n = n;
        isos[i].reflects = false;
        isos[i].rotationIndex = i;

        isos[i + n].n = n;
        isos[i + n].reflects = true;
        isos[i + n].rotationIndex = i;
    }

    return isos;
}

bool operator==(const DihedralIsometry &lhs, const DihedralIsometry &rhs) {
    return lhs.n == rhs.n && lhs.reflects == rhs.reflects && lhs.rotationIndex == rhs.rotationIndex;
}
bool operator!=(const DihedralIsometry &lhs, const DihedralIsometry &rhs) {
    return !(lhs == rhs);
}
std::ostream & operator<<(std::ostream &lhs, const DihedralIsometry &rhs) {
    if(rhs.rotationIndex == 0 && rhs.reflects == false) {
        lhs << "1";
    }
    else if(rhs.rotationIndex != 0) {
        lhs << "x^" << rhs.rotationIndex;
    }
    lhs << (rhs.reflects ? "y" : "");

    return lhs;
}
<commit_msg>Added proper include<commit_after>//
//  DihedralIsometry.cpp
//  GroupTheory
//
//  Created by Donald Pinckney on 11/16/15.
//  Copyright © 2015 Donald Pinckney. All rights reserved.
//

#include "DihedralIsometry.h"
#include <stdlib.h>

int rotationMod(int rotationIndex, int mod) {
    int mult = abs(rotationIndex) / mod + 1;
    return (rotationIndex + mult*mod) % mod;
}

// NOTE: THIS ALLOCATES MEMORY! YOU MUST CALL DELETE WHEN DONE.
DihedralIsometry *DihedralIsometry::compose(const DihedralIsometry &x, const DihedralIsometry &y) {
    bool reflects = x.reflects != y.reflects;

    int rotationIndex;
    if(x.reflects) {
        rotationIndex = x.rotationIndex - y.rotationIndex;
    } else {
        rotationIndex = x.rotationIndex + y.rotationIndex;
    }
    rotationIndex = rotationMod(rotationIndex, x.n);

    DihedralIsometry *iso = new DihedralIsometry(x.n, reflects, rotationIndex);

    return iso;
}

// NOTE: THIS ALLOCATES MEMORY! YOU MUST CALL DELETE WHEN DONE.
DihedralIsometry *DihedralIsometry::generateIsometries(unsigned int n, unsigned int *count) {
    *count = 2*n;

    DihedralIsometry *isos = new DihedralIsometry[*count];

    for (unsigned int i = 0; i < n; i++) {
        isos[i].n = n;
        isos[i].reflects = false;
        isos[i].rotationIndex = i;

        isos[i + n].n = n;
        isos[i + n].reflects = true;
        isos[i + n].rotationIndex = i;
    }

    return isos;
}

bool operator==(const DihedralIsometry &lhs, const DihedralIsometry &rhs) {
    return lhs.n == rhs.n && lhs.reflects == rhs.reflects && lhs.rotationIndex == rhs.rotationIndex;
}
bool operator!=(const DihedralIsometry &lhs, const DihedralIsometry &rhs) {
    return !(lhs == rhs);
}
std::ostream & operator<<(std::ostream &lhs, const DihedralIsometry &rhs) {
    if(rhs.rotationIndex == 0 && rhs.reflects == false) {
        lhs << "1";
    }
    else if(rhs.rotationIndex != 0) {
        lhs << "x^" << rhs.rotationIndex;
    }
    lhs << (rhs.reflects ? "y" : "");

    return lhs;
}
<|endoftext|>


<commit_before>// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/content_handler/application_controller_impl.h"

#include <utility>

#include "apps/modular/lib/app/connect.h"
#include "flutter/content_handler/app.h"
#include "flutter/content_handler/runtime_holder.h"
#include "lib/ftl/logging.h"
#include "lib/mtl/vmo/vector.h"

namespace flutter_runner {

ApplicationControllerImpl::ApplicationControllerImpl(
    App* app,
    modular::ApplicationPackagePtr application,
    modular::ApplicationStartupInfoPtr startup_info,
    fidl::InterfaceRequest<modular::ApplicationController> controller)
    : app_(app), binding_(this) {
  if (controller.is_pending()) {
    binding_.Bind(std::move(controller));
    binding_.set_connection_error_handler([this] {
      app_->Destroy(this);
      // |this| has been deleted at this point.
    });
  }

  std::vector<char> bundle;
  if (!mtl::VectorFromVmo(std::move(application->data), &bundle)) {
    FTL_LOG(ERROR) << "Failed to receive bundle.";
    return;
  }

  // TODO(abarth): The Dart code should end up with outgoing_services.
  if (startup_info->outgoing_services.is_pending()) {
    service_provider_bindings_.AddBinding(
        this, std::move(startup_info->outgoing_services));
  }

  url_ = startup_info->url;
  runtime_holder_.reset(new RuntimeHolder());

  // TODO(abarth): The Dart code should end up with environment_services.
  runtime_holder_->Init(modular::ServiceProviderPtr::Create(
                            std::move(startup_info->environment_services)),
                        std::move(bundle));
}

ApplicationControllerImpl::~ApplicationControllerImpl() = default;

void ApplicationControllerImpl::Kill(const KillCallback& callback) {
  runtime_holder_.reset();
  app_->Destroy(this);
  // |this| has been deleted at this point.
}

void ApplicationControllerImpl::Detach() {
  binding_.set_connection_error_handler(ftl::Closure());
}

void ApplicationControllerImpl::ConnectToService(
    const fidl::String& service_name,
    mx::channel client_handle) {
  if (service_name == mozart::ViewProvider::Name_) {
    view_provider_bindings_.AddBinding(
        this,
        fidl::InterfaceRequest<mozart::ViewProvider>(std::move(client_handle)));
  }
}

void ApplicationControllerImpl::CreateView(
    fidl::InterfaceRequest<mozart::ViewOwner> view_owner_request,
    fidl::InterfaceRequest<modular::ServiceProvider> services) {
  runtime_holder_->CreateView(url_, std::move(view_owner_request),
                              std::move(services));
}

}  // namespace flutter_runner
<commit_msg>Update to new way of passing application environment. (#3209)<commit_after>// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/content_handler/application_controller_impl.h"

#include <utility>

#include "apps/modular/lib/app/connect.h"
#include "flutter/content_handler/app.h"
#include "flutter/content_handler/runtime_holder.h"
#include "lib/ftl/logging.h"
#include "lib/mtl/vmo/vector.h"

namespace flutter_runner {

ApplicationControllerImpl::ApplicationControllerImpl(
    App* app,
    modular::ApplicationPackagePtr application,
    modular::ApplicationStartupInfoPtr startup_info,
    fidl::InterfaceRequest<modular::ApplicationController> controller)
    : app_(app), binding_(this) {
  if (controller.is_pending()) {
    binding_.Bind(std::move(controller));
    binding_.set_connection_error_handler([this] {
      app_->Destroy(this);
      // |this| has been deleted at this point.
    });
  }

  std::vector<char> bundle;
  if (!mtl::VectorFromVmo(std::move(application->data), &bundle)) {
    FTL_LOG(ERROR) << "Failed to receive bundle.";
    return;
  }

  // TODO(abarth): The Dart code should end up with outgoing_services.
  if (startup_info->outgoing_services) {
    service_provider_bindings_.AddBinding(
        this, std::move(startup_info->outgoing_services));
  }

  url_ = startup_info->url;
  runtime_holder_.reset(new RuntimeHolder());

  // TODO(abarth): The Dart code should end up with environment.
  modular::ServiceProviderPtr environment_services;
  modular::ApplicationEnvironmentPtr::Create(
      std::move(startup_info->environment))
      ->GetServices(GetProxy(&environment_services));
  runtime_holder_->Init(std::move(environment_services), std::move(bundle));
}

ApplicationControllerImpl::~ApplicationControllerImpl() = default;

void ApplicationControllerImpl::Kill(const KillCallback& callback) {
  runtime_holder_.reset();
  app_->Destroy(this);
  // |this| has been deleted at this point.
}

void ApplicationControllerImpl::Detach() {
  binding_.set_connection_error_handler(ftl::Closure());
}

void ApplicationControllerImpl::ConnectToService(
    const fidl::String& service_name,
    mx::channel client_handle) {
  if (service_name == mozart::ViewProvider::Name_) {
    view_provider_bindings_.AddBinding(
        this,
        fidl::InterfaceRequest<mozart::ViewProvider>(std::move(client_handle)));
  }
}

void ApplicationControllerImpl::CreateView(
    fidl::InterfaceRequest<mozart::ViewOwner> view_owner_request,
    fidl::InterfaceRequest<modular::ServiceProvider> services) {
  runtime_holder_->CreateView(url_, std::move(view_owner_request),
                              std::move(services));
}

}  // namespace flutter_runner
<|endoftext|>


<commit_before>
#include <cstdio>

#include "main.h"

class A {
public:
	int a() {
	}
};
<commit_msg>remove duplicated include directives.<commit_after>
#include "main.h"

class A {
public:
	int a() {
	}
};
<|endoftext|>


<commit_before>// Copyright (c) 2017 Pierre Moreau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <cstring>
#include <iostream>
#include <vector>

#include "source/spirv_target_env.h"
#include "spirv-tools/libspirv.hpp"
#include "spirv-tools/linker.hpp"
#include "tools/io.h"

void print_usage(char* argv0) {
  printf(
      R"(%s - Link SPIR-V binary files together.

USAGE: %s [options] <filename> [<filename> ...]

The SPIR-V binaries are read from the different <filename>.

NOTE: The linker is a work in progress.

Options:
  -h, --help       Print this help.
  -o               Name of the resulting linked SPIR-V binary.
  --create-library Link the binaries into a library, keeping all exported symbols.
  --version        Display linker version information
  --target-env     {vulkan1.0|spv1.0|spv1.1|spv1.2|opencl2.1|opencl2.2}
                   Use Vulkan1.0/SPIR-V1.0/SPIR-V1.1/SPIR-V1.2/OpenCL-2.1/OpenCL2.2 validation rules.
)",
      argv0, argv0);
}

int main(int argc, char** argv) {
  std::vector<const char*> inFiles;
  const char* outFile = nullptr;
  spv_target_env target_env = SPV_ENV_UNIVERSAL_1_0;
  spvtools::LinkerOptions options;
  bool continue_processing = true;
  int return_code = 0;

  for (int argi = 1; continue_processing && argi < argc; ++argi) {
    const char* cur_arg = argv[argi];
    if ('-' == cur_arg[0]) {
      if (0 == strcmp(cur_arg, "-o")) {
        if (argi + 1 < argc) {
          if (!outFile) {
            outFile = argv[++argi];
          } else {
            fprintf(stderr, "error: More than one output file specified\n");
            continue_processing = false;
            return_code = 1;
          }
        } else {
          fprintf(stderr, "error: Missing argument to %s\n", cur_arg);
          continue_processing = false;
          return_code = 1;
        }
      } else if (0 == strcmp(cur_arg, "--create-library")) {
        options.SetCreateLibrary(true);
      } else if (0 == strcmp(cur_arg, "--version")) {
        printf("%s\n", spvSoftwareVersionDetailsString());
        // TODO(dneto): Add OpenCL 2.2 at least.
        printf("Targets:\n  %s\n  %s\n  %s\n",
               spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1),
               spvTargetEnvDescription(SPV_ENV_VULKAN_1_0),
               spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2));
        continue_processing = false;
        return_code = 0;
      } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
        print_usage(argv[0]);
        continue_processing = false;
        return_code = 0;
      } else if (0 == strcmp(cur_arg, "--target-env")) {
        if (argi + 1 < argc) {
          const auto env_str = argv[++argi];
          if (!spvParseTargetEnv(env_str, &target_env)) {
            fprintf(stderr, "error: Unrecognized target env: %s\n", env_str);
            continue_processing = false;
            return_code = 1;
          }
        } else {
          fprintf(stderr, "error: Missing argument to --target-env\n");
          continue_processing = false;
          return_code = 1;
        }
      }
    } else {
      inFiles.push_back(cur_arg);
    }
  }

  // Exit if command line parsing was not successful.
  if (!continue_processing) {
    return return_code;
  }

  if (inFiles.empty()) {
    fprintf(stderr, "error: No input file specified\n");
    return 1;
  }

  std::vector<std::vector<uint32_t>> contents(inFiles.size());
  for (size_t i = 0u; i < inFiles.size(); ++i) {
    if (!ReadFile<uint32_t>(inFiles[i], "rb", &contents[i])) return 1;
  }

  spvtools::Linker linker(target_env);
  linker.SetMessageConsumer([](spv_message_level_t level, const char*,
                               const spv_position_t& position,
                               const char* message) {
    switch (level) {
      case SPV_MSG_FATAL:
      case SPV_MSG_INTERNAL_ERROR:
      case SPV_MSG_ERROR:
        std::cerr << "error: " << position.index << ": " << message
                  << std::endl;
        break;
      case SPV_MSG_WARNING:
        std::cout << "warning: " << position.index << ": " << message
                  << std::endl;
        break;
      case SPV_MSG_INFO:
        std::cout << "info: " << position.index << ": " << message << std::endl;
        break;
      default:
        break;
    }
  });

  std::vector<uint32_t> linkingResult;
  bool succeed = linker.Link(contents, linkingResult, options);

  if (!WriteFile<uint32_t>(outFile, "wb", linkingResult.data(),
                           linkingResult.size()))
    return 1;

  return !succeed;
}
<commit_msg>Linker: Fix incorrect exit status.<commit_after>// Copyright (c) 2017 Pierre Moreau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <cstring>
#include <iostream>
#include <vector>

#include "source/spirv_target_env.h"
#include "spirv-tools/libspirv.hpp"
#include "spirv-tools/linker.hpp"
#include "tools/io.h"

void print_usage(char* argv0) {
  printf(
      R"(%s - Link SPIR-V binary files together.

USAGE: %s [options] <filename> [<filename> ...]

The SPIR-V binaries are read from the different <filename>.

NOTE: The linker is a work in progress.

Options:
  -h, --help       Print this help.
  -o               Name of the resulting linked SPIR-V binary.
  --create-library Link the binaries into a library, keeping all exported symbols.
  --version        Display linker version information
  --target-env     {vulkan1.0|spv1.0|spv1.1|spv1.2|opencl2.1|opencl2.2}
                   Use Vulkan1.0/SPIR-V1.0/SPIR-V1.1/SPIR-V1.2/OpenCL-2.1/OpenCL2.2 validation rules.
)",
      argv0, argv0);
}

int main(int argc, char** argv) {
  std::vector<const char*> inFiles;
  const char* outFile = nullptr;
  spv_target_env target_env = SPV_ENV_UNIVERSAL_1_0;
  spvtools::LinkerOptions options;
  bool continue_processing = true;
  int return_code = 0;

  for (int argi = 1; continue_processing && argi < argc; ++argi) {
    const char* cur_arg = argv[argi];
    if ('-' == cur_arg[0]) {
      if (0 == strcmp(cur_arg, "-o")) {
        if (argi + 1 < argc) {
          if (!outFile) {
            outFile = argv[++argi];
          } else {
            fprintf(stderr, "error: More than one output file specified\n");
            continue_processing = false;
            return_code = 1;
          }
        } else {
          fprintf(stderr, "error: Missing argument to %s\n", cur_arg);
          continue_processing = false;
          return_code = 1;
        }
      } else if (0 == strcmp(cur_arg, "--create-library")) {
        options.SetCreateLibrary(true);
      } else if (0 == strcmp(cur_arg, "--version")) {
        printf("%s\n", spvSoftwareVersionDetailsString());
        // TODO(dneto): Add OpenCL 2.2 at least.
        printf("Targets:\n  %s\n  %s\n  %s\n",
               spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1),
               spvTargetEnvDescription(SPV_ENV_VULKAN_1_0),
               spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2));
        continue_processing = false;
        return_code = 0;
      } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
        print_usage(argv[0]);
        continue_processing = false;
        return_code = 0;
      } else if (0 == strcmp(cur_arg, "--target-env")) {
        if (argi + 1 < argc) {
          const auto env_str = argv[++argi];
          if (!spvParseTargetEnv(env_str, &target_env)) {
            fprintf(stderr, "error: Unrecognized target env: %s\n", env_str);
            continue_processing = false;
            return_code = 1;
          }
        } else {
          fprintf(stderr, "error: Missing argument to --target-env\n");
          continue_processing = false;
          return_code = 1;
        }
      }
    } else {
      inFiles.push_back(cur_arg);
    }
  }

  // Exit if command line parsing was not successful.
  if (!continue_processing) {
    return return_code;
  }

  if (inFiles.empty()) {
    fprintf(stderr, "error: No input file specified\n");
    return 1;
  }

  std::vector<std::vector<uint32_t>> contents(inFiles.size());
  for (size_t i = 0u; i < inFiles.size(); ++i) {
    if (!ReadFile<uint32_t>(inFiles[i], "rb", &contents[i])) return 1;
  }

  spvtools::Linker linker(target_env);
  linker.SetMessageConsumer([](spv_message_level_t level, const char*,
                               const spv_position_t& position,
                               const char* message) {
    switch (level) {
      case SPV_MSG_FATAL:
      case SPV_MSG_INTERNAL_ERROR:
      case SPV_MSG_ERROR:
        std::cerr << "error: " << position.index << ": " << message
                  << std::endl;
        break;
      case SPV_MSG_WARNING:
        std::cout << "warning: " << position.index << ": " << message
                  << std::endl;
        break;
      case SPV_MSG_INFO:
        std::cout << "info: " << position.index << ": " << message << std::endl;
        break;
      default:
        break;
    }
  });

  std::vector<uint32_t> linkingResult;
  spv_result_t status = linker.Link(contents, linkingResult, options);

  if (!WriteFile<uint32_t>(outFile, "wb", linkingResult.data(),
                           linkingResult.size()))
    return 1;

  return status == SPV_SUCCESS ? 0 : 1;
}
<|endoftext|>