<commit_before>#include "3RVX.h"
#include "Controllers\Volume\CoreAudio.h"
#include "VolumeOSD.h"
#include <Wtsapi32.h>
#include "Logger.h"
#include "Settings.h"
#include <iostream>

int APIENTRY
wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow) {
    hInst = hInstance;

#ifdef _DEBUG
    Logger::OpenConsole();
#endif

    QCLOG(L"  _____ ______     ____  _______ ");
    QCLOG(L" |___ /|  _ \\ \\   / /\\ \\/ /___ / ");
    QCLOG(L"   |_ \\| |_) \\ \\ / /  \\  /  |_ \\ ");
    QCLOG(L"  ___) |  _ < \\ V /   /  \\ ___) |");
    QCLOG(L" |____/|_| \\_\\ \\_/   /_/\\_\\____/ ");
    QCLOG(L"");

    QCLOG(L"Starting up...");

    mutex = CreateMutex(NULL, FALSE, L"Local\\3RVX");
    if (GetLastError() == ERROR_ALREADY_EXISTS) {
        if (mutex) {
            ReleaseMutex(mutex);
        }

        HWND masterWnd = FindWindow(CLASS_3RVX, CLASS_3RVX);
        CLOG(L"A previous instance of the program is already running.\n"
            L"Requesting Settings dialog from window: %d", masterWnd);
        SendMessage(masterWnd, WM_3RVX_CONTROL, MSG_SETTINGS, NULL);

#ifdef _DEBUG
        CLOG(L"Press [enter] to terminate");
        std::cin.get();
#endif

        return EXIT_SUCCESS;
    }

    using namespace Gdiplus;
    GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    mainWnd = CreateMainWnd(hInstance);
    if (mainWnd == NULL) {
        CLOG(L"Could not create main window");
        return EXIT_FAILURE;
    }

    HRESULT hr = CoInitializeEx(NULL,
        COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (hr != S_OK) {
        CLOG(L"Failed to initialize the COM library.");
        return EXIT_FAILURE;
    }

    /* Tell the program to initialize */
    PostMessage(mainWnd, WM_3RVX_CONTROL, MSG_LOAD, NULL);

    /* Start the event loop */
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    GdiplusShutdown(gdiplusToken);
    return (int)msg.wParam;
}

void Init() {
    CLOG(L"Initializing...");

    volCtrl = new CoreAudio(mainWnd);
    volCtrl->Init();
    float currentVolume = volCtrl->Volume();

    Settings settings(L"Settings.xml");

    Skin skin(settings.SkinName());

    vOsd = new VolumeOSD(hInst);
    vOsd->LoadSkin(&skin);
    vOsd->MeterLevels(currentVolume);

    hotkeys = settings.Hotkeys();
    HotkeyManager *hkm = HotkeyManager::Instance(mainWnd);
    for (auto it = hotkeys.begin(); it != hotkeys.end(); ++it) {
        int combination = it->first;
        hkm->Register(combination);
    }

    WTSRegisterSessionNotification(mainWnd, NOTIFY_FOR_THIS_SESSION);
}

HWND CreateMainWnd(HINSTANCE hInstance) {
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = NULL;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = NULL;
    wcex.cbWndExtra = NULL;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = CLASS_3RVX;
    wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if (!RegisterClassEx(&wcex)) {
        return NULL;
    }

    HWND hWnd = CreateWindowEx(
        NULL,
        CLASS_3RVX, CLASS_3RVX,
        NULL, NULL, NULL, //your boat, gently down the stream
        NULL, NULL, HWND_MESSAGE, NULL, hInstance, NULL);

    return hWnd;
}

LRESULT CALLBACK WndProc(
    HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {

    switch (message) {
    case MSG_VOLCHNG: {
        float v = volCtrl->Volume();
        QCLOG(L"Volume level: %.0f", v * 100.0f);
        vOsd->MeterLevels(v);
        break;
    }

    case MSG_DEVCHNG:
        CLOG(L"Device change detected.");
        volCtrl->ReattachDefaultDevice();
        break;

    case WM_HOTKEY: {
        CLOG(L"Hotkey: %d", (int) wParam);
        int action = hotkeys[(int) wParam];
        if (action == 100) {
            float current = volCtrl->Volume();
            volCtrl->Volume(current + .1f);
        }
        break;
    }

    case WM_WTSSESSION_CHANGE:
        CLOG(L"Detected session change");
        break;
    }

    if (message == WM_3RVX_CONTROL) {
        switch (wParam) {
        case MSG_LOAD:
            Init();
            break;

        case MSG_SETTINGS:
            CLOG(L"Launching settings editor");
            /* TODO: launch! */
            break;
        }
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}<commit_msg>Update for Skin constructor change<commit_after>#include "3RVX.h"
#include "Controllers\Volume\CoreAudio.h"
#include "VolumeOSD.h"
#include <Wtsapi32.h>
#include "Logger.h"
#include "Settings.h"
#include <iostream>
#include "Skin.h"

int APIENTRY
wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow) {
    hInst = hInstance;

#ifdef _DEBUG
    Logger::OpenConsole();
#endif

    QCLOG(L"  _____ ______     ____  _______ ");
    QCLOG(L" |___ /|  _ \\ \\   / /\\ \\/ /___ / ");
    QCLOG(L"   |_ \\| |_) \\ \\ / /  \\  /  |_ \\ ");
    QCLOG(L"  ___) |  _ < \\ V /   /  \\ ___) |");
    QCLOG(L" |____/|_| \\_\\ \\_/   /_/\\_\\____/ ");
    QCLOG(L"");

    QCLOG(L"Starting up...");

    mutex = CreateMutex(NULL, FALSE, L"Local\\3RVX");
    if (GetLastError() == ERROR_ALREADY_EXISTS) {
        if (mutex) {
            ReleaseMutex(mutex);
        }

        HWND masterWnd = FindWindow(CLASS_3RVX, CLASS_3RVX);
        CLOG(L"A previous instance of the program is already running.\n"
            L"Requesting Settings dialog from window: %d", masterWnd);
        SendMessage(masterWnd, WM_3RVX_CONTROL, MSG_SETTINGS, NULL);

#ifdef _DEBUG
        CLOG(L"Press [enter] to terminate");
        std::cin.get();
#endif

        return EXIT_SUCCESS;
    }

    using namespace Gdiplus;
    GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    mainWnd = CreateMainWnd(hInstance);
    if (mainWnd == NULL) {
        CLOG(L"Could not create main window");
        return EXIT_FAILURE;
    }

    HRESULT hr = CoInitializeEx(NULL,
        COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (hr != S_OK) {
        CLOG(L"Failed to initialize the COM library.");
        return EXIT_FAILURE;
    }

    /* Tell the program to initialize */
    PostMessage(mainWnd, WM_3RVX_CONTROL, MSG_LOAD, NULL);

    /* Start the event loop */
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    GdiplusShutdown(gdiplusToken);
    return (int)msg.wParam;
}

void Init() {
    CLOG(L"Initializing...");

    volCtrl = new CoreAudio(mainWnd);
    volCtrl->Init();
    float currentVolume = volCtrl->Volume();

    Settings settings(L"Settings.xml");

    std::wstring skinName = settings.SkinName();
    std::wstring skinXML = SKINS_DIR L"\\" + skinName + L"\\" SKIN_XML;
    Skin skin(skinXML);

    vOsd = new VolumeOSD(hInst);
    vOsd->LoadSkin(&skin);
    vOsd->MeterLevels(currentVolume);

    hotkeys = settings.Hotkeys();
    HotkeyManager *hkm = HotkeyManager::Instance(mainWnd);
    for (auto it = hotkeys.begin(); it != hotkeys.end(); ++it) {
        int combination = it->first;
        hkm->Register(combination);
    }

    WTSRegisterSessionNotification(mainWnd, NOTIFY_FOR_THIS_SESSION);
}

HWND CreateMainWnd(HINSTANCE hInstance) {
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = NULL;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = NULL;
    wcex.cbWndExtra = NULL;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = CLASS_3RVX;
    wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if (!RegisterClassEx(&wcex)) {
        return NULL;
    }

    HWND hWnd = CreateWindowEx(
        NULL,
        CLASS_3RVX, CLASS_3RVX,
        NULL, NULL, NULL, //your boat, gently down the stream
        NULL, NULL, HWND_MESSAGE, NULL, hInstance, NULL);

    return hWnd;
}

LRESULT CALLBACK WndProc(
    HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {

    switch (message) {
    case MSG_VOLCHNG: {
        float v = volCtrl->Volume();
        QCLOG(L"Volume level: %.0f", v * 100.0f);
        vOsd->MeterLevels(v);
        break;
    }

    case MSG_DEVCHNG:
        CLOG(L"Device change detected.");
        volCtrl->ReattachDefaultDevice();
        break;

    case WM_HOTKEY: {
        CLOG(L"Hotkey: %d", (int) wParam);
        int action = hotkeys[(int) wParam];
        if (action == 100) {
            float current = volCtrl->Volume();
            volCtrl->Volume(current + .1f);
        }
        break;
    }

    case WM_WTSSESSION_CHANGE:
        CLOG(L"Detected session change");
        break;
    }

    if (message == WM_3RVX_CONTROL) {
        switch (wParam) {
        case MSG_LOAD:
            Init();
            break;

        case MSG_SETTINGS:
            CLOG(L"Launching settings editor");
            /* TODO: launch! */
            break;
        }
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}<|endoftext|>


<commit_before>#include <avr/io.h>
#include <util/delay.h>
#include <avr/sleep.h>
#include <stdio.h>

#include "testHooks.h"
#include "TestStreams.h"

#include "comms/NMEA.h"
//TESTING "comms/NMEA.cpp"
//TESTING "math/GreatCircle.cpp"
//TESTING "math/Waypoint.cpp"
//TESTING "math/SpatialMath.cpp"

typedef struct GPRMC{
    const char * desc;
    const char * str;
    float time;
    bool  active;
    float latitude;
    float longitude;
    float speed;
    float trackAngle;
    long  date;
    float magVar;
} GPRMC;

/*
lat/lon is in degrees+decimal minutes, code spec is decimal degrees
function decimalDegrees(degreeDecimalMinutes)
    minutes = degreeDecimalMinutes % 100.0
    degrees = (degreeDecimalMinutes - minutes) / 100.0
    decimalDegrees = degrees + minutes/60.0
    return decimalDegrees
end

speed in GPRMC is in knots, code spec returns mph
mph(knots) = knots * 1.15077945
*/

//examples taken from http://www.gpsinformation.org/dale/nmea.htm
const GPRMC goodData[] {
    { "NE GPRMC",
      "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A",
      123519, true, 48.11729, 11.51666, 25.77746, 84.4, 230394, 3.1
    },
    { "NW GPRMC",
      "$GPRMC,183729,A,3907.356,N,12102.482,W,000.0,360.0,080301,015.5,E*6F",
      183729, true, 39.12260, -121.04136, 0.0, 360.0, 80301, 15.5
    },
    { "Bad Status",
      "$GPRMC,152926,V,6027.8259,N,02225.6713,E,10.8,0.0,190803,5.9,E,S*22",
      152926, false, 60.46376, 22.42786, 12.428418, 0.0, 190803, 5.9
    },
    { "Empty Fields",
      "$GPRMC,162254.00,A,3723.02837,N,12159.39853,W,0.820,188.36,110706,,,A*74",
      162254, true, 37.38380, -121.98998, 0.94364, 188.36, 110706, 0.0
    },
    { "DecimalTime",
      "$GPRMC,230611.016,V,3907.3813,N,12102.4635,W,0.14,136.40,041002,,*04",
      230611.016, false, 39.12302, -121.04106, 0.161109, 136.40, 41002, 0.0
    },
    { "Surrounded GPRMC",
      "$GPGSV,3,3,09,24,12,282,00*4D"
      "$GPGLL,3553.5295,N,13938.6570,E,002454,A,A*4F"
      "$GPBOD,,T,,M,,*47"
      "$PGRME,8.6,M,9.6,M,12.9,M*15"
      "$PGRMZ,51,f*30"
      "$HCHDG,101.1,,,7.1,W*3C"
      "$GPRTE,1,1,c,*37"
      "$GPRMC,002456,A,3553.5295,N,13938.6570,E,0.0,43.1,180700,7.1,W,A*3D"
      "$GPGGA,002454,3553.5295,N,13938.6570,E,1,05,2.2,18.3,M,39.0,M,,*7F"
      "$GPGSA,A,3,01,04,07,16,20,,,,,,,,3.6,2.2,2.7*35"
      "$GPGSV,3,1,09,01,38,103,37,02,23,215,00,04,38,297,37,05,00,328,00*70"
      "$GPGSV,3,2,09,07,77,299,47,11,07,087,00,16,74,041,47,20,38,044,43*73",
      2456, true, 35.89215, 139.64428, 0.0, 43.1, 180700,7.1
    },
};
const int numGoodData = sizeof(goodData)/sizeof(goodData[0]);

const char * badStrings[] {

};

#define nmeaOfString(s) \
    StringStream ss(s); \
    NMEA nmea(ss); \
    nmea.update(); \
    ss.reset("$"); \
    nmea.update();
    //the extra dollar sign is an artifact of the current NMEA implementation

bool parseTime(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        FPASSERT(nmea.getTimeOfFix(), goodData[i].time);
    }
    return true;
}
bool parseActive(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        CASSERT(nmea.getWarning(), !goodData[i].active);
    }
    return true;
}
bool parseLatitude(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        FPASSERT(nmea.getLatitude(), goodData[i].latitude);
    }
    return true;
}
bool parseLongitude(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        FPASSERT(nmea.getLongitude(), goodData[i].longitude);
    }
    return true;
}
bool parseSpeed(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        FPASSERT(nmea.getGroundSpeed(), goodData[i].speed);
    }
    return true;
}
bool parseTrackAngle(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        FPASSERT(nmea.getCourse(), goodData[i].trackAngle);
    }
    return true;
}
bool parseDate(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        CASSERT(nmea.getDateOfFix(), goodData[i].date);
    }
    return true;
}
bool parseMagVar(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        FPASSERT(nmea.getMagVar(), goodData[i].magVar);
    }
    return true;
}

int main(void){
    TEST(parseTime       );
    TEST(parseActive     );
    TEST(parseLatitude   );
    TEST(parseLongitude  );
    TEST(parseSpeed      );
    TEST(parseTrackAngle );
    TEST(parseDate       );
    TEST(parseMagVar     );
    return 0;
}
<commit_msg>Add some evil strings to test with<commit_after>#include <avr/io.h>
#include <util/delay.h>
#include <avr/sleep.h>
#include <stdio.h>

#include "testHooks.h"
#include "TestStreams.h"

#include "comms/NMEA.h"
//TESTING "comms/NMEA.cpp"
//TESTING "math/GreatCircle.cpp"
//TESTING "math/Waypoint.cpp"
//TESTING "math/SpatialMath.cpp"

typedef struct GPRMC{
    const char * desc;
    const char * str;
    float time;
    bool  active;
    float latitude;
    float longitude;
    float speed;
    float trackAngle;
    long  date;
    float magVar;
} GPRMC;

/*
lat/lon is in degrees+decimal minutes, code spec is decimal degrees
function decimalDegrees(degreeDecimalMinutes)
    minutes = degreeDecimalMinutes % 100.0
    degrees = (degreeDecimalMinutes - minutes) / 100.0
    decimalDegrees = degrees + minutes/60.0
    return decimalDegrees
end

speed in GPRMC is in knots, code spec returns mph
mph(knots) = knots * 1.15077945
*/

//examples taken from http://www.gpsinformation.org/dale/nmea.htm
const GPRMC goodData[] {
    { "NE GPRMC",
      "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A",
      123519, true, 48.11729, 11.51666, 25.77746, 84.4, 230394, 3.1
    },
    { "NW GPRMC",
      "$GPRMC,183729,A,3907.356,N,12102.482,W,000.0,360.0,080301,015.5,E*6F",
      183729, true, 39.12260, -121.04136, 0.0, 360.0, 80301, 15.5
    },
    { "Bad Status",
      "$GPRMC,152926,V,6027.8259,N,02225.6713,E,10.8,0.0,190803,5.9,E,S*22",
      152926, false, 60.46376, 22.42786, 12.428418, 0.0, 190803, 5.9
    },
    { "Empty Fields",
      "$GPRMC,162254.00,A,3723.02837,N,12159.39853,W,0.820,188.36,110706,,,A*74",
      162254, true, 37.38380, -121.98998, 0.94364, 188.36, 110706, 0.0
    },
    { "DecimalTime",
      "$GPRMC,230611.016,V,3907.3813,N,12102.4635,W,0.14,136.40,041002,,*04",
      230611.016, false, 39.12302, -121.04106, 0.161109, 136.40, 41002, 0.0
    },
    { "Surrounded GPRMC",
      "$GPGSV,3,3,09,24,12,282,00*4D"
      "$GPGLL,3553.5295,N,13938.6570,E,002454,A,A*4F"
      "$GPBOD,,T,,M,,*47"
      "$PGRME,8.6,M,9.6,M,12.9,M*15"
      "$PGRMZ,51,f*30"
      "$HCHDG,101.1,,,7.1,W*3C"
      "$GPRTE,1,1,c,*37"
      "$GPRMC,002456,A,3553.5295,N,13938.6570,E,0.0,43.1,180700,7.1,W,A*3D"
      "$GPGGA,002454,3553.5295,N,13938.6570,E,1,05,2.2,18.3,M,39.0,M,,*7F"
      "$GPGSA,A,3,01,04,07,16,20,,,,,,,,3.6,2.2,2.7*35"
      "$GPGSV,3,1,09,01,38,103,37,02,23,215,00,04,38,297,37,05,00,328,00*70"
      "$GPGSV,3,2,09,07,77,299,47,11,07,087,00,16,74,041,47,20,38,044,43*73",
      2456, true, 35.89215, 139.64428, 0.0, 43.1, 180700,7.1
    },
};
const int numGoodData = sizeof(goodData)/sizeof(goodData[0]);

const char * badStrings[] {
    "$$$$$$$$$$$",
    "$,,,,,,,,,,,,,,,,,,,",
    "$GPRMC,,,,,,,,,,,,,,,,,,",
    "$GPRMC$",
    "$GPRMC,123519,X,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A",
    "$GPRMC,X,X,X,X,X,X,X,X,X,X,XXXX",
    "$GPRMC,123519549631488436214567/89321456,A,",
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    "4807.038,N,01131.00000-100.4,E,022.4,084.4,2303946544563217896,003.1,W*6A",
};
const int numBadStrings = sizeof(badStrings)/sizeof(badStrings[0]);

#define nmeaOfString(s) \
    StringStream ss(s); \
    NMEA nmea(ss); \
    nmea.update(); \
    ss.reset("$"); \
    nmea.update();
    //the extra dollar sign is an artifact of the current NMEA implementation

bool parseTime(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        FPASSERT(nmea.getTimeOfFix(), goodData[i].time);
    }
    return true;
}
bool parseActive(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        CASSERT(nmea.getWarning(), !goodData[i].active);
    }
    return true;
}
bool parseLatitude(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        FPASSERT(nmea.getLatitude(), goodData[i].latitude);
    }
    return true;
}
bool parseLongitude(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        FPASSERT(nmea.getLongitude(), goodData[i].longitude);
    }
    return true;
}
bool parseSpeed(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        FPASSERT(nmea.getGroundSpeed(), goodData[i].speed);
    }
    return true;
}
bool parseTrackAngle(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        FPASSERT(nmea.getCourse(), goodData[i].trackAngle);
    }
    return true;
}
bool parseDate(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        CASSERT(nmea.getDateOfFix(), goodData[i].date);
    }
    return true;
}
bool parseMagVar(){
    for(int i=0; i<numGoodData; i++){
        nmeaOfString(goodData[i].str);
        FPASSERT(nmea.getMagVar(), goodData[i].magVar);
    }
    return true;
}
bool testBadStringFirst(){
    // test that the parser can recover from getting a bunch of bad data first
    for(int i=0; i<numBadStrings; i++){
        StringStream ss(badStrings[i]);
        NMEA nmea(ss);
        nmea.update();
        ss.reset(goodData[0].str);
        nmea.update();
        ss.reset("$");
        nmea.update();

        FPASSERT(goodData[0].time, nmea.getTimeOfFix());
        CASSERT( goodData[0].active, !nmea.getWarning());
        FPASSERT(goodData[0].latitude, nmea.getLatitude());
        FPASSERT(goodData[0].longitude, nmea.getLongitude());
        FPASSERT(goodData[0].speed, nmea.getGroundSpeed());
        FPASSERT(goodData[0].trackAngle, nmea.getCourse());
        CASSERT( goodData[0].date, nmea.getDateOfFix());
        FPASSERT(goodData[0].magVar, nmea.getMagVar());
    }
    return true;
}

int main(void){
    TEST(parseTime         );
    TEST(parseActive       );
    TEST(parseLatitude     );
    TEST(parseLongitude    );
    TEST(parseSpeed        );
    TEST(parseTrackAngle   );
    TEST(parseDate         );
    TEST(parseMagVar       );
    TEST(testBadStringFirst);
    return 0;
}
<|endoftext|>


<commit_before>/*
This file is part of the ORK library.
Full copyright and license terms can be found in the LICENSE.txt file.
*/
#pragma once

#include"ork/orientation.hpp"
#if ORK_USE_GLM
#include"glm/vec3.hpp"
#include"glm/geometric.hpp"

namespace ork {
namespace GLM {//Kind of deceiving, but whatever

struct dunit3 {
private:
	glm::dvec3 _vec;
public:
	ORK_INLINE explicit dunit3(const glm::dvec3&vector) :_vec(glm::normalize(vector)) {}
	ORK_INLINE dunit3(const double x, const double y, const double z) :_vec(glm::normalize(glm::dvec3(x, y, z))) {}
public:
	ORK_INLINE const glm::dvec3&get()const {
		return _vec;
	}
	ORK_INLINE double&operator[](const size_t index) {
		return _vec[static_cast<unsigned>(index)];
	}
	ORK_INLINE const double&operator[](const size_t index)const {
		return _vec[static_cast<unsigned>(index)];
	}
	ORK_INLINE double x()const { return _vec.x; }
	ORK_INLINE double y()const { return _vec.y; }
	ORK_INLINE double z()const { return _vec.z; }
	ORK_INLINE const double&operator[](const unsigned index) const {//unsigned because glm uses int indices
		return _vec[index];
	}
	ORK_INLINE dunit3 operator-()const {
		return dunit3(-_vec);
	}
	ORK_INLINE unsigned size()const {//unsigned for consistency with glm
		return 3;
	}
	ORK_INLINE unsigned length()const {//For consistency with glm
		return 3;
	}
};

ORK_INLINE glm::dvec3 operator*(const double lhs, const dunit3&rhs) {
	return lhs*rhs.get();
}
ORK_ORK_EXT(o_stream&) operator<<(o_stream&stream, const dunit3&vec);

//Simply tired of defining these
ORK_EXTERN const dunit3 pos_x;
ORK_EXTERN const dunit3 neg_x;
ORK_EXTERN const dunit3 pos_y;
ORK_EXTERN const dunit3 neg_y;
ORK_EXTERN const dunit3 pos_z;
ORK_EXTERN const dunit3 neg_z;

//GLM version of loops, needed because GLM uses length() and unsigned as the type
#define LOOPVG(SERIES,INDEX)for(unsigned INDEX=0, limit##INDEX=SERIES.length(); INDEX!=limit##INDEX; ++INDEX)
#define LOOPVIG(SERIES)LOOPVG(SERIES,i)
#define LOOPVJG(SERIES)LOOPVG(SERIES,j)
#define LOOPVKG(SERIES)LOOPVG(SERIES,k)
#define LOOPVLG(SERIES)LOOPVG(SERIES,l)

/*
This function basically tests that two floats could have resulted from a single, mathematically equivalent operation
float f1=0.2;//Cannot be exactly represented! (One bit either way)
float f2=0.1;//ditto
Float f2+=0.1;//Maybe 2 bits different now(both bits)
assert(f1==f2);//have fun crashing
assert(might_be_equal(f1,f2));//We are safe, assuming IEEE 754 (Ok,I am not authoritatively positive)
There are false positives with small numbers!
*/
namespace detail {

template<typename T, unsigned eps_factor = 16, unsigned rel_factor = 1>
ORK_INLINE bool equal_simple(const T&lhs, const T&rhs) {
	static const T abs_eps = eps_factor * std::numeric_limits<T>::epsilon();//We need an absolute epsilon
	static const T rel_eps = rel_factor * abs_eps;//Factor of 2 to allow for the case of second LSB bump
	return std::abs(lhs - rhs) <= std::max(abs_eps, rel_eps*std::max(lhs, rhs));
}

template<class T>
ORK_INLINE bool equal_vector(const T&lhs, const T&rhs) {
	LOOPVIG(lhs) {
		if(!equal_simple(lhs[i], rhs[i]))return false;
	}
	return true;
}

template<class T>
ORK_INLINE bool equal_matrix(const T&lhs, const T&rhs) {
	LOOPVIG(lhs) {
		if(!equal_vector(lhs[i], rhs[i]))return false;
	}
	return true;
}

template<typename T>
ORK_INLINE bool less_simple(const T&lhs, const T&rhs) {
	return lhs < rhs && !equal_simple<T>(lhs, rhs);
}

template<typename T>
ORK_INLINE bool greater_simple(const T&lhs, const T&rhs) {
	return lhs > rhs && !equal_simple<T>(lhs, rhs);
}

template<typename T>
ORK_INLINE bool less_equal_simple(const T&lhs, const T&rhs) {
	return lhs < rhs || equal_simple<T>(lhs, rhs);
}

template<typename T>
ORK_INLINE bool greater_equal_simple(const T&lhs, const T&rhs) {
	return lhs > rhs || equal_simple<T>(lhs, rhs);
}

}//namespace detail

//Use matrix as the default because there are more of them
template<typename T>
ORK_INLINE bool equal(const T&lhs, const T&rhs) {
	return detail::equal_matrix<T>(lhs, rhs);
}

//And we have not bothered to add other overloads
template<typename V, glm::precision P>
ORK_INLINE bool equal(const glm::tvec3<V, P>&lhs, const glm::tvec3<V, P>&rhs) {
	return detail::equal_vector<glm::tvec3<V, P>>(lhs, rhs);
}
template<typename V, glm::precision P>
ORK_INLINE bool equal(const glm::tvec4<V, P>&lhs, const glm::tvec4<V, P>&rhs) {
	return detail::equal_vector<glm::tvec4<V, P>>(lhs, rhs);
}
ORK_INLINE bool equal(const GLM::dunit3&lhs, const GLM::dunit3&rhs) {
	return detail::equal_vector<GLM::dunit3>(lhs, rhs);
}

template<>
ORK_INLINE bool equal<float>(const float&lhs, const float&rhs) {
	return detail::equal_simple<float>(lhs, rhs);
}
template<>
ORK_INLINE bool equal<double>(const double&lhs, const double&rhs) {
	return detail::equal_simple<double>(lhs, rhs);
}

template<class V>
ORK_INLINE bool parallel(const V&v1, const V&v2) {
	const double norms = glm::length(v1) * glm::length(v2);
	const double dot = glm::dot(v1, v2);
	return equal(norms, dot);
}
template<>
ORK_INLINE bool parallel<dunit3>(const dunit3&v1, const dunit3&v2) {
	const double norms = glm::length(v1.get()) * glm::length(v2.get());
	const double dot = glm::dot(v1.get(), v2.get());
	return equal(norms, dot);
}

template<class V>
ORK_INLINE bool antiparallel(const V&v1, const V&v2) {
	return parallel<V>(v1, -v2);
}

template<class V>
ORK_INLINE bool axial(const V&v1, const V&v2) {
	return parallel<V>(v1, v2) || parallel<V>(v1, -v2);
}

template<class V>
ORK_INLINE bool orthogonal(const V&v1, const V&v2) {
	const double norms = glm::length(v1) * glm::length(v2);
	const double dot = glm::dot(v1, v2);
	const bool test = equal(norms, norms + dot);
	return test;
}
template<>
ORK_INLINE bool orthogonal<dunit3>(const dunit3&v1, const dunit3&v2) {
	return orthogonal(v1.get(), v2.get());
}

/*
Some vector functions
*/

template<class V>
ORK_INLINE bool less(const V&v1, const V&v2) {
	LOOPVIG(v1) {
		if(equal(v1[i], v2[i]) || v1[i] > v2[i])return false;
	}
	return true;
}
template<>
ORK_INLINE bool less<float>(const float&lhs, const float&rhs) {
	return detail::less_simple<float>(lhs, rhs);
}
template<>
ORK_INLINE bool less<double>(const double&lhs, const double&rhs) {
	return detail::less_simple<double>(lhs, rhs);
}

template<class V>
ORK_INLINE bool greater(const V&v1, const V&v2) {
	LOOPVIG(v1) {
		if(equal(v1[i], v2[i]) || v1[i] < v2[i])return false;
	}
	return true;
}
template<>
ORK_INLINE bool greater<float>(const float&lhs, const float&rhs) {
	return detail::greater_simple<float>(lhs, rhs);
}
template<>
ORK_INLINE bool greater<double>(const double&lhs, const double&rhs) {
	return detail::greater_simple<double>(lhs, rhs);
}

template<class V>
ORK_INLINE bool less_equal(const V&v1, const V&v2) {
	LOOPVIG(v1) {
		if(greater(v1[i], v2[i]))return false;
	}
	return true;
}
template<>
ORK_INLINE bool less_equal<float>(const float&lhs, const float&rhs) {
	return detail::less_equal_simple<float>(lhs, rhs);
}
template<>
ORK_INLINE bool less_equal<double>(const double&lhs, const double&rhs) {
	return detail::less_equal_simple<double>(lhs, rhs);
}

template<class V>
ORK_INLINE bool greater_equal(const V&v1, const V&v2) {
	LOOPVIG(v1) {
		if(less(v1[i], v2[i]))return false;
	}
	return true;
}
template<>
ORK_INLINE bool greater_equal<float>(const float&lhs, const float&rhs) {
	return detail::greater_equal_simple<float>(lhs, rhs);
}
template<>
ORK_INLINE bool greater_equal<double>(const double&lhs, const double&rhs) {
	return detail::greater_equal_simple<double>(lhs, rhs);
}

//Rotates a vector about the origin, such that normal would become new_normal if subject to the same rotation
ORK_ORK_EXT(glm::dvec3) rotate(const glm::dvec3&vec, const dunit3&normal, const dunit3&new_normal);

ORK_ORK_EXT(glm::dvec3) proj_on_plane(const glm::dvec3&vec, const GLM::dunit3&normal);

/*
Orientation stuff
*/
ORK_ORK_EXT(const dunit3&) orientation2direction(orientation axis);

}//namespace GLM

ORK_ORK_EXT(string) to_string(const glm::dvec3&vec);

}//namespace ork

namespace glm {
ORK_ORK_EXT(ork::o_stream&) operator<<(ork::o_stream&stream, const glm::dvec2&vec);
ORK_ORK_EXT(ork::o_stream&) operator<<(ork::o_stream&stream, const glm::dvec3&vec);
}

#endif//ORK_USE_GLM<commit_msg>Exposed tolerance value to clients<commit_after>/*
This file is part of the ORK library.
Full copyright and license terms can be found in the LICENSE.txt file.
*/
#pragma once

#include"ork/orientation.hpp"
#if ORK_USE_GLM
#include"glm/vec3.hpp"
#include"glm/geometric.hpp"

namespace ork {
namespace GLM {//Kind of deceiving, but whatever

struct dunit3 {
private:
	glm::dvec3 _vec;
public:
	ORK_INLINE explicit dunit3(const glm::dvec3&vector) :_vec(glm::normalize(vector)) {}
	ORK_INLINE dunit3(const double x, const double y, const double z) :_vec(glm::normalize(glm::dvec3(x, y, z))) {}
public:
	ORK_INLINE const glm::dvec3&get()const {
		return _vec;
	}
	ORK_INLINE double&operator[](const size_t index) {
		return _vec[static_cast<unsigned>(index)];
	}
	ORK_INLINE const double&operator[](const size_t index)const {
		return _vec[static_cast<unsigned>(index)];
	}
	ORK_INLINE double x()const { return _vec.x; }
	ORK_INLINE double y()const { return _vec.y; }
	ORK_INLINE double z()const { return _vec.z; }
	ORK_INLINE const double&operator[](const unsigned index) const {//unsigned because glm uses int indices
		return _vec[index];
	}
	ORK_INLINE dunit3 operator-()const {
		return dunit3(-_vec);
	}
	ORK_INLINE unsigned size()const {//unsigned for consistency with glm
		return 3;
	}
	ORK_INLINE unsigned length()const {//For consistency with glm
		return 3;
	}
};

ORK_INLINE glm::dvec3 operator*(const double lhs, const dunit3&rhs) {
	return lhs*rhs.get();
}
ORK_ORK_EXT(o_stream&) operator<<(o_stream&stream, const dunit3&vec);

//Simply tired of defining these
ORK_EXTERN const dunit3 pos_x;
ORK_EXTERN const dunit3 neg_x;
ORK_EXTERN const dunit3 pos_y;
ORK_EXTERN const dunit3 neg_y;
ORK_EXTERN const dunit3 pos_z;
ORK_EXTERN const dunit3 neg_z;

//GLM version of loops, needed because GLM uses length() and unsigned as the type
#define LOOPVG(SERIES,INDEX)for(unsigned INDEX=0, limit##INDEX=SERIES.length(); INDEX!=limit##INDEX; ++INDEX)
#define LOOPVIG(SERIES)LOOPVG(SERIES,i)
#define LOOPVJG(SERIES)LOOPVG(SERIES,j)
#define LOOPVKG(SERIES)LOOPVG(SERIES,k)
#define LOOPVLG(SERIES)LOOPVG(SERIES,l)

/*
This function basically tests that two floats could have resulted from a single, mathematically equivalent operation
float f1=0.2;//Cannot be exactly represented! (One bit either way)
float f2=0.1;//ditto
Float f2+=0.1;//Maybe 2 bits different now(both bits)
assert(f1==f2);//have fun crashing
assert(might_be_equal(f1,f2));//We are safe, assuming IEEE 754 (Ok,I am not authoritatively positive)
There are false positives with small numbers!
*/
template<typename T>struct default_epsilon_factor {
	static const unsigned value = 16;//This is only verified over time as the minimum upper bound across ACIS and OCC when T is double
};
template<typename T, unsigned eps_factor = default_epsilon_factor<T>::value>
ORK_INLINE ORK_CONSTEXPR T tolerance() {
	return eps_factor * std::numeric_limits<T>::epsilon();
}

namespace detail {

template<typename T, unsigned eps_factor = default_epsilon_factor<T>::value, unsigned rel_factor = 1>
ORK_INLINE bool equal_simple(const T&lhs, const T&rhs) {
	static const T abs_eps = tolerance<T, eps_factor>();//We need an absolute epsilon
	static const T rel_eps = rel_factor * abs_eps;//Factor of 2 to allow for the case of second LSB bump
	return std::abs(lhs - rhs) <= std::max(abs_eps, rel_eps*std::max(lhs, rhs));
}

template<class T>
ORK_INLINE bool equal_vector(const T&lhs, const T&rhs) {
	LOOPVIG(lhs) {
		if(!equal_simple(lhs[i], rhs[i]))return false;
	}
	return true;
}

template<class T>
ORK_INLINE bool equal_matrix(const T&lhs, const T&rhs) {
	LOOPVIG(lhs) {
		if(!equal_vector(lhs[i], rhs[i]))return false;
	}
	return true;
}

template<typename T>
ORK_INLINE bool less_simple(const T&lhs, const T&rhs) {
	return lhs < rhs && !equal_simple<T>(lhs, rhs);
}

template<typename T>
ORK_INLINE bool greater_simple(const T&lhs, const T&rhs) {
	return lhs > rhs && !equal_simple<T>(lhs, rhs);
}

template<typename T>
ORK_INLINE bool less_equal_simple(const T&lhs, const T&rhs) {
	return lhs < rhs || equal_simple<T>(lhs, rhs);
}

template<typename T>
ORK_INLINE bool greater_equal_simple(const T&lhs, const T&rhs) {
	return lhs > rhs || equal_simple<T>(lhs, rhs);
}

}//namespace detail

//Use matrix as the default because there are more of them
template<typename T>
ORK_INLINE bool equal(const T&lhs, const T&rhs) {
	return detail::equal_matrix<T>(lhs, rhs);
}

//And we have not bothered to add other overloads
template<typename V, glm::precision P>
ORK_INLINE bool equal(const glm::tvec3<V, P>&lhs, const glm::tvec3<V, P>&rhs) {
	return detail::equal_vector<glm::tvec3<V, P>>(lhs, rhs);
}
template<typename V, glm::precision P>
ORK_INLINE bool equal(const glm::tvec4<V, P>&lhs, const glm::tvec4<V, P>&rhs) {
	return detail::equal_vector<glm::tvec4<V, P>>(lhs, rhs);
}
ORK_INLINE bool equal(const GLM::dunit3&lhs, const GLM::dunit3&rhs) {
	return detail::equal_vector<GLM::dunit3>(lhs, rhs);
}

template<>
ORK_INLINE bool equal<float>(const float&lhs, const float&rhs) {
	return detail::equal_simple<float>(lhs, rhs);
}
template<>
ORK_INLINE bool equal<double>(const double&lhs, const double&rhs) {
	return detail::equal_simple<double>(lhs, rhs);
}

template<class V>
ORK_INLINE bool parallel(const V&v1, const V&v2) {
	const double norms = glm::length(v1) * glm::length(v2);
	const double dot = glm::dot(v1, v2);
	return equal(norms, dot);
}
template<>
ORK_INLINE bool parallel<dunit3>(const dunit3&v1, const dunit3&v2) {
	const double norms = glm::length(v1.get()) * glm::length(v2.get());
	const double dot = glm::dot(v1.get(), v2.get());
	return equal(norms, dot);
}

template<class V>
ORK_INLINE bool antiparallel(const V&v1, const V&v2) {
	return parallel<V>(v1, -v2);
}

template<class V>
ORK_INLINE bool axial(const V&v1, const V&v2) {
	return parallel<V>(v1, v2) || parallel<V>(v1, -v2);
}

template<class V>
ORK_INLINE bool orthogonal(const V&v1, const V&v2) {
	const double norms = glm::length(v1) * glm::length(v2);
	const double dot = glm::dot(v1, v2);
	const bool test = equal(norms, norms + dot);
	return test;
}
template<>
ORK_INLINE bool orthogonal<dunit3>(const dunit3&v1, const dunit3&v2) {
	return orthogonal(v1.get(), v2.get());
}

/*
Some vector functions
*/

template<class V>
ORK_INLINE bool less(const V&v1, const V&v2) {
	LOOPVIG(v1) {
		if(equal(v1[i], v2[i]) || v1[i] > v2[i])return false;
	}
	return true;
}
template<>
ORK_INLINE bool less<float>(const float&lhs, const float&rhs) {
	return detail::less_simple<float>(lhs, rhs);
}
template<>
ORK_INLINE bool less<double>(const double&lhs, const double&rhs) {
	return detail::less_simple<double>(lhs, rhs);
}

template<class V>
ORK_INLINE bool greater(const V&v1, const V&v2) {
	LOOPVIG(v1) {
		if(equal(v1[i], v2[i]) || v1[i] < v2[i])return false;
	}
	return true;
}
template<>
ORK_INLINE bool greater<float>(const float&lhs, const float&rhs) {
	return detail::greater_simple<float>(lhs, rhs);
}
template<>
ORK_INLINE bool greater<double>(const double&lhs, const double&rhs) {
	return detail::greater_simple<double>(lhs, rhs);
}

template<class V>
ORK_INLINE bool less_equal(const V&v1, const V&v2) {
	LOOPVIG(v1) {
		if(greater(v1[i], v2[i]))return false;
	}
	return true;
}
template<>
ORK_INLINE bool less_equal<float>(const float&lhs, const float&rhs) {
	return detail::less_equal_simple<float>(lhs, rhs);
}
template<>
ORK_INLINE bool less_equal<double>(const double&lhs, const double&rhs) {
	return detail::less_equal_simple<double>(lhs, rhs);
}

template<class V>
ORK_INLINE bool greater_equal(const V&v1, const V&v2) {
	LOOPVIG(v1) {
		if(less(v1[i], v2[i]))return false;
	}
	return true;
}
template<>
ORK_INLINE bool greater_equal<float>(const float&lhs, const float&rhs) {
	return detail::greater_equal_simple<float>(lhs, rhs);
}
template<>
ORK_INLINE bool greater_equal<double>(const double&lhs, const double&rhs) {
	return detail::greater_equal_simple<double>(lhs, rhs);
}

//Rotates a vector about the origin, such that normal would become new_normal if subject to the same rotation
ORK_ORK_EXT(glm::dvec3) rotate(const glm::dvec3&vec, const dunit3&normal, const dunit3&new_normal);

ORK_ORK_EXT(glm::dvec3) proj_on_plane(const glm::dvec3&vec, const GLM::dunit3&normal);

/*
Orientation stuff
*/
ORK_ORK_EXT(const dunit3&) orientation2direction(orientation axis);

}//namespace GLM

ORK_ORK_EXT(string) to_string(const glm::dvec3&vec);

}//namespace ork

namespace glm {
ORK_ORK_EXT(ork::o_stream&) operator<<(ork::o_stream&stream, const glm::dvec2&vec);
ORK_ORK_EXT(ork::o_stream&) operator<<(ork::o_stream&stream, const glm::dvec3&vec);
}

#endif//ORK_USE_GLM<|endoftext|>


<commit_before>// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp

#include <NDK/Widgets/BoxLayout.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Core/StackVector.hpp>
#include <kiwi/kiwi.h>
#include <cassert>
#include <vector>

namespace Ndk
{
	struct BoxLayout::State
	{
		std::vector<kiwi::Variable> sizeVar;
		kiwi::Solver solver;
	};

	BoxLayout::BoxLayout(BaseWidget* parent, BoxLayoutOrientation orientation) :
	BaseWidget(parent),
	m_orientation(orientation),
	m_spacing(5.f)
	{
		m_state = std::make_unique<State>();
	}

	BoxLayout::~BoxLayout() = default;

	void BoxLayout::Layout()
	{
		BaseWidget::Layout();

		std::size_t axis;

		switch (m_orientation)
		{
			case BoxLayoutOrientation_Horizontal:
				axis = 0; //< x
				break;

			case BoxLayoutOrientation_Vertical:
				axis = 1; //< y
				break;

			default:
				assert(false);
				break;
		}

		//TODO: Keep solver state when widgets don't change
		std::size_t widgetChildCount = GetWidgetChildCount();
		if (widgetChildCount == 0)
			return;

		m_state->solver.reset();

		m_state->sizeVar.clear();
		m_state->sizeVar.reserve(widgetChildCount);

		kiwi::Expression sizeSum;

		Nz::Vector2f layoutSize = GetSize();
		float availableSpace = layoutSize[axis] - m_spacing * (widgetChildCount - 1);
		float perfectSpacePerWidget = availableSpace / widgetChildCount;

		// Handle size
		ForEachWidgetChild([&](BaseWidget* child)
		{
			if (!child->IsVisible())
				return;

			float maximumSize = child->GetMaximumSize()[axis];
			float minimumSize = child->GetMinimumSize()[axis];

			m_state->sizeVar.emplace_back();
			auto& sizeVar = m_state->sizeVar.back();

			m_state->solver.addConstraint({ sizeVar >= minimumSize | kiwi::strength::required });

			if (maximumSize < std::numeric_limits<float>::infinity())
				m_state->solver.addConstraint({ sizeVar <= maximumSize | kiwi::strength::required });

			m_state->solver.addConstraint({ sizeVar == perfectSpacePerWidget | kiwi::strength::medium });

			sizeSum = sizeSum + sizeVar;
		});

		kiwi::Variable targetSize("LayoutSize");

		m_state->solver.addConstraint(sizeSum <= targetSize | kiwi::strength::strong);

		m_state->solver.addEditVariable(targetSize, kiwi::strength::strong);
		m_state->solver.suggestValue(targetSize, availableSpace);

		m_state->solver.updateVariables();

		std::size_t varIndex = 0;

		float remainingSize = availableSpace;

		ForEachWidgetChild([&](BaseWidget* child)
		{
			if (!child->IsVisible())
				return;

			Nz::Vector2f newSize = layoutSize;
			newSize[axis] = m_state->sizeVar[varIndex].value();

			child->Resize(newSize);
			remainingSize -= newSize[axis];

			varIndex++;
		});

		float spacing = m_spacing + remainingSize / (widgetChildCount - 1);

		// Handle position
		float cursor = 0.f;
		bool first = true;
		ForEachWidgetChild([&](BaseWidget* child)
		{
			if (first)
				first = false;
			else
				cursor += spacing;

			Nz::Vector2f position = Nz::Vector2f(0.f, 0.f);
			position[axis] = cursor;

			child->SetPosition(position);

			cursor += child->GetSize()[axis];
		});
	}
}
<commit_msg>Sdk/BoxLayout: Fix widgets not taking up free space<commit_after>// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp

#include <NDK/Widgets/BoxLayout.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Core/StackVector.hpp>
#include <kiwi/kiwi.h>
#include <cassert>
#include <vector>

namespace Ndk
{
	struct BoxLayout::State
	{
		std::vector<kiwi::Variable> sizeVar;
		kiwi::Solver solver;
	};

	BoxLayout::BoxLayout(BaseWidget* parent, BoxLayoutOrientation orientation) :
	BaseWidget(parent),
	m_orientation(orientation),
	m_spacing(5.f)
	{
		m_state = std::make_unique<State>();
	}

	BoxLayout::~BoxLayout() = default;

	void BoxLayout::Layout()
	{
		BaseWidget::Layout();

		std::size_t axis;

		switch (m_orientation)
		{
			case BoxLayoutOrientation_Horizontal:
				axis = 0; //< x
				break;

			case BoxLayoutOrientation_Vertical:
				axis = 1; //< y
				break;

			default:
				assert(false);
				break;
		}

		//TODO: Keep solver state when widgets don't change
		std::size_t widgetChildCount = GetWidgetChildCount();
		if (widgetChildCount == 0)
			return;

		m_state->solver.reset();

		m_state->sizeVar.clear();
		m_state->sizeVar.reserve(widgetChildCount);

		kiwi::Expression sizeSum;

		Nz::Vector2f layoutSize = GetSize();
		float availableSpace = layoutSize[axis] - m_spacing * (widgetChildCount - 1);
		float perfectSpacePerWidget = availableSpace / widgetChildCount;

		// Handle size
		ForEachWidgetChild([&](BaseWidget* child)
		{
			if (!child->IsVisible())
				return;

			float maximumSize = child->GetMaximumSize()[axis];
			float minimumSize = child->GetMinimumSize()[axis];

			m_state->sizeVar.emplace_back();
			auto& sizeVar = m_state->sizeVar.back();

			m_state->solver.addConstraint({ sizeVar >= minimumSize | kiwi::strength::required });

			if (maximumSize < std::numeric_limits<float>::infinity())
				m_state->solver.addConstraint({ sizeVar <= maximumSize | kiwi::strength::required });

			m_state->solver.addConstraint({ sizeVar >= perfectSpacePerWidget | kiwi::strength::medium });

			sizeSum = sizeSum + sizeVar;
		});

		kiwi::Variable targetSize("LayoutSize");

		m_state->solver.addConstraint(sizeSum <= targetSize | kiwi::strength::strong);

		m_state->solver.addEditVariable(targetSize, kiwi::strength::strong);
		m_state->solver.suggestValue(targetSize, availableSpace);

		m_state->solver.updateVariables();

		std::size_t varIndex = 0;

		float remainingSize = availableSpace;

		ForEachWidgetChild([&](BaseWidget* child)
		{
			if (!child->IsVisible())
				return;

			Nz::Vector2f newSize = layoutSize;
			newSize[axis] = m_state->sizeVar[varIndex].value();

			child->Resize(newSize);
			remainingSize -= newSize[axis];

			varIndex++;
		});

		float spacing = m_spacing + remainingSize / (widgetChildCount - 1);

		// Handle position
		float cursor = 0.f;
		bool first = true;
		ForEachWidgetChild([&](BaseWidget* child)
		{
			if (first)
				first = false;
			else
				cursor += spacing;

			Nz::Vector2f position = Nz::Vector2f(0.f, 0.f);
			position[axis] = cursor;

			child->SetPosition(position);

			cursor += child->GetSize()[axis];
		});
	}
}
<|endoftext|>


<commit_before>/******************************************************************************

  This source file is part of the TEM tomography project.

  Copyright Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

  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 "CentralWidget.h"
#include "ui_CentralWidget.h"

#include <pqView.h>
#include <vtkAxis.h>
#include <vtkChartXY.h>
#include <vtkContextMouseEvent.h>
#include <vtkContextScene.h>
#include <vtkContextView.h>
#include <vtkEventQtSlotConnect.h>
#include <vtkFloatArray.h>
#include <vtkImageData.h>
#include <vtkIntArray.h>
#include <vtkMathUtilities.h>
#include <vtkObjectFactory.h>
#include <vtkPlotBar.h>
#include <vtkPointData.h>
#include <vtkSMSourceProxy.h>
#include <vtkSMViewProxy.h>
#include <vtkTable.h>
#include <vtkTransform2D.h>
#include <vtkTrivialProducer.h>
#include <vtkContext2D.h>
#include <vtkPen.h>

#include <QtDebug>
#include <QThread>

#include "ActiveObjects.h"
#include "ComputeHistogram.h"
#include "DataSource.h"
#include "ModuleContour.h"
#include "ModuleManager.h"
#include "Utilities.h"

#ifdef DAX_DEVICE_ADAPTER
# include "dax/ModuleStreamingContour.h"
#endif

namespace TEM
{

//-----------------------------------------------------------------------------
// This is just here for now - quick and dirty historgram calculations...
void PopulateHistogram(vtkImageData *input, vtkTable *output)
{
  // The output table will have the twice the number of columns, they will be
  // the x and y for input column. This is the bin centers, and the population.
  double minmax[2] = { 0.0, 0.0 };
  const int numberOfBins = 256;

  // The bin values are the centers, extending +/- half an inc either side
  switch (input->GetScalarType())
    {
    vtkTemplateMacro(
          TEM::GetScalarRange(reinterpret_cast<VTK_TT *>(input->GetPointData()->GetScalars()->GetVoidPointer(0)),
                         input->GetPointData()->GetScalars()->GetNumberOfTuples(),
                         minmax));
    default:
      break;
    }
  if (minmax[0] == minmax[1])
    {
    minmax[1] = minmax[0] + 1.0;
    }

  double inc = (minmax[1] - minmax[0]) / numberOfBins;
  double halfInc = inc / 2.0;
  vtkSmartPointer<vtkFloatArray> extents =
      vtkFloatArray::SafeDownCast(
        output->GetColumnByName(vtkStdString("image_extents").c_str()));
  if (!extents)
    {
    extents = vtkSmartPointer<vtkFloatArray>::New();
    extents->SetName(vtkStdString("image_extents").c_str());
    }
  extents->SetNumberOfTuples(numberOfBins);
  double min = minmax[0] + halfInc;
  for (int j = 0; j < numberOfBins; ++j)
    {
    extents->SetValue(j, min + j * inc);
    }
  vtkSmartPointer<vtkIntArray> populations =
      vtkIntArray::SafeDownCast(
        output->GetColumnByName(vtkStdString("image_pops").c_str()));
  if (!populations)
    {
    populations = vtkSmartPointer<vtkIntArray>::New();
    populations->SetName(vtkStdString("image_pops").c_str());
    }
  populations->SetNumberOfTuples(numberOfBins);
  int *pops = static_cast<int *>(populations->GetVoidPointer(0));
  for (int k = 0; k < numberOfBins; ++k)
    {
    pops[k] = 0;
    }

  switch (input->GetScalarType())
    {
    vtkTemplateMacro(
          TEM::CalculateHistogram(reinterpret_cast<VTK_TT *>(input->GetPointData()->GetScalars()->GetVoidPointer(0)),
                             input->GetPointData()->GetScalars()->GetNumberOfTuples(),
                             minmax[0], pops, inc, numberOfBins));
    default:
      cout << "UpdateFromFile: Unknown data type" << endl;
    }

#ifndef NDEBUG
  vtkIdType total = 0;
  for (int i = 0; i < numberOfBins; ++i)
    total += pops[i];
  assert(total == input->GetPointData()->GetScalars()->GetNumberOfTuples());
#endif

  output->AddColumn(extents.GetPointer());
  output->AddColumn(populations.GetPointer());
}

// Quick background thread for the histogram calculation.
class HistogramWorker : public QThread
{
  Q_OBJECT

  void run();

public:
  HistogramWorker(QObject *p = 0) : QThread(p) {}

  vtkSmartPointer<vtkImageData> input;
  vtkSmartPointer<vtkTable> output;
};

void HistogramWorker::run()
{
  if (input && output)
    {
    PopulateHistogram(input.Get(), output.Get());
    }
}

class CentralWidget::CWInternals
{
public:
  Ui::CentralWidget Ui;
};

class vtkHistogramMarker : public vtkPlot
{
public:
  static vtkHistogramMarker * New();
  double PositionX;

  bool Paint(vtkContext2D *painter)
  {
    vtkNew<vtkPen> pen;
    pen->SetColor(255, 0, 0, 255);
    pen->SetWidth(2.0);
    painter->ApplyPen(pen.Get());
    painter->DrawLine(PositionX, 0, PositionX, 1e9);
    return true;
  }
};
vtkStandardNewMacro(vtkHistogramMarker);

class vtkChartHistogram : public vtkChartXY
{
public:
  static vtkChartHistogram * New();

  bool MouseDoubleClickEvent(const vtkContextMouseEvent &mouse);

  vtkNew<vtkTransform2D> Transform;
  double PositionX;
  vtkNew<vtkHistogramMarker> Marker;
};

vtkStandardNewMacro(vtkChartHistogram)

bool vtkChartHistogram::MouseDoubleClickEvent(const vtkContextMouseEvent &m)
{
  // Determine the location of the click, and emit something we can listen to!
  vtkPlotBar *histo = 0;
  if (this->GetNumberOfPlots() > 0)
    {
    histo = vtkPlotBar::SafeDownCast(this->GetPlot(0));
    }
  if (!histo)
    {
    return false;
    }
  this->CalculateUnscaledPlotTransform(histo->GetXAxis(), histo->GetYAxis(),
                                       this->Transform.Get());
  vtkVector2f pos;
  this->Transform->InverseTransformPoints(m.GetScenePos().GetData(), pos.GetData(),
                                          1);
  this->PositionX = pos.GetX();
  this->Marker->PositionX = this->PositionX;
  this->Marker->Modified();
  this->Scene->SetDirty(true);
  if (this->GetNumberOfPlots() == 1)
    {
    this->AddPlot(this->Marker.Get());
    }
  this->InvokeEvent(vtkCommand::CursorChangedEvent);
  return true;
}

//-----------------------------------------------------------------------------
CentralWidget::CentralWidget(QWidget* parentObject, Qt::WindowFlags wflags)
  : Superclass(parentObject, wflags),
    Internals(new CentralWidget::CWInternals()),
    Worker(NULL)
{
  this->Internals->Ui.setupUi(this);

  QList<int> sizes;
  sizes << 200 << 200;
  this->Internals->Ui.splitter->setSizes(sizes);
  this->Internals->Ui.splitter->setStretchFactor(0, 0);
  this->Internals->Ui.splitter->setStretchFactor(1, 1);

  // Set up our little chart.
  this->Histogram
      ->SetInteractor(this->Internals->Ui.histogramWidget->GetInteractor());
  this->Internals->Ui.histogramWidget
      ->SetRenderWindow(this->Histogram->GetRenderWindow());
  vtkChartHistogram* chart = this->Chart.Get();
  this->Histogram->GetScene()->AddItem(chart);
  chart->SetBarWidthFraction(1.0);
  chart->SetRenderEmpty(true);
  chart->SetAutoAxes(false);
  chart->GetAxis(vtkAxis::LEFT)->SetTitle("");
  chart->GetAxis(vtkAxis::BOTTOM)->SetTitle("");
  chart->GetAxis(vtkAxis::LEFT)->SetBehavior(vtkAxis::FIXED);
  chart->GetAxis(vtkAxis::LEFT)->SetRange(0.0001, 10);
  chart->GetAxis(vtkAxis::LEFT)->SetMinimumLimit(1);
  chart->GetAxis(vtkAxis::LEFT)->SetLogScale(true);

  this->EventLink->Connect(chart, vtkCommand::CursorChangedEvent, this,
                           SLOT(histogramClicked(vtkObject*)));
}

//-----------------------------------------------------------------------------
CentralWidget::~CentralWidget()
{
}

//-----------------------------------------------------------------------------
void CentralWidget::setDataSource(DataSource* source)
{
  if (this->ADataSource)
    {
    this->disconnect(this->ADataSource);
    }
  this->ADataSource = source;
  if (source)
    {
    this->connect(source, SIGNAL(dataChanged()), SLOT(refreshHistogram()));
    }

  // Whenever the data source changes clear the plot, and then populate when
  // ready (or use the cached histogram values.
  this->Chart->ClearPlots();

  if (!source)
    {
    return;
    }

  // Get the actual data source, build a histogram out of it.
  vtkTrivialProducer *t = vtkTrivialProducer::SafeDownCast(
    source->producer()->GetClientSideObject());
  vtkImageData *data = vtkImageData::SafeDownCast(t->GetOutputDataObject(0));

  // Check our cache, and use that if appopriate (or update it).
  if (this->HistogramCache.contains(data))
    {
    vtkTable *cachedTable = this->HistogramCache[data];
    if (cachedTable->GetMTime() > data->GetMTime())
      {
      this->setHistogramTable(cachedTable);
      return;
      }
    else
      {
      // Should this ever happen? Do we want to support this? -- YES!!!
      //qDebug() << "Image data changed after histogram calculation.";
      //return;
      this->HistogramCache.remove(data);
      }
    }

  // Calculate a histogram.
  vtkNew<vtkTable> table;
  this->HistogramCache[data] = table.Get();

  if (!this->Worker)
    {
    this->Worker = new HistogramWorker(this);
    connect(this->Worker, SIGNAL(finished()), SLOT(histogramReady()));
    }
  else if (this->Worker->isRunning())
    {
    // FIXME: Queue, abort, something.
    qDebug() << "Worker already running, skipping this one.";
    return;
    }
  this->Worker->input = data;
  this->Worker->output = table.Get();
  this->Worker->start();
}

void CentralWidget::refreshHistogram()
{
  this->setDataSource(this->ADataSource);
}

void CentralWidget::histogramReady()
{
  if (!this->Worker || !this->Worker->input || !this->Worker->output)
    return;

  this->setHistogramTable(this->Worker->output.Get());

  this->Worker->input = NULL;
  this->Worker->output = NULL;
}

void CentralWidget::histogramClicked(vtkObject *caller)
{
  //qDebug() << "Histogram clicked at" << this->Chart->PositionX
  //         << "making this a great spot to ask for an isosurface at value"
  //         << this->Chart->PositionX;
  Q_ASSERT(this->ADataSource);

  vtkSMViewProxy* view = ActiveObjects::instance().activeView();
  if (!view)
    {
    return;
    }

  // Use active ModuleContour is possible. Otherwise, find the first existing
  // ModuleContour instance or just create a new one, if none exists.
#ifdef DAX_DEVICE_ADAPTER
  typedef ModuleStreamingContour ModuleContourType;
#else
  typedef ModuleContour ModuleContourType;
#endif

  ModuleContourType* contour = qobject_cast<ModuleContourType*>(
    ActiveObjects::instance().activeModule());
  if (!contour)
    {
    QList<ModuleContourType*> contours =
      ModuleManager::instance().findModules<ModuleContourType*>(this->ADataSource, view);
    if (contours.size() == 0)
      {
      contour = qobject_cast<ModuleContourType*>(ModuleManager::instance().createAndAddModule(
          "Contour", this->ADataSource, view));
      }
    else
      {
      contour = contours[0];
      }
    ActiveObjects::instance().setActiveModule(contour);
    }
  Q_ASSERT(contour);
  contour->setIsoValue(this->Chart->PositionX);
  TEM::convert<pqView*>(view)->render();
}

void CentralWidget::setHistogramTable(vtkTable *table)
{
  this->Chart->ClearPlots();
  vtkPlot *plot = this->Chart->AddPlot(vtkChart::BAR);
  plot->SetInputData(table, "image_extents", "image_pops");
  plot->SetColor(0, 0, 255, 255);
  plot->GetPen()->SetLineType(vtkPen::NO_PEN);
  vtkDataArray *arr =
      vtkDataArray::SafeDownCast(table->GetColumnByName("image_pops"));
  if (arr)
    {
    double max = log10(arr->GetRange()[1]);
    vtkAxis *axis = this->Chart->GetAxis(vtkAxis::LEFT);
    axis->SetUnscaledMinimum(1.0);
    axis->SetMaximumLimit(max + 2.0);
    axis->SetMaximum(static_cast<int>(max) + 1.0);
    }
}

} // end of namespace TEM

#include "CentralWidget.moc"
<commit_msg>Use the maximum area to show the bars<commit_after>/******************************************************************************

  This source file is part of the TEM tomography project.

  Copyright Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

  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 "CentralWidget.h"
#include "ui_CentralWidget.h"

#include <pqView.h>
#include <vtkAxis.h>
#include <vtkChartXY.h>
#include <vtkContextMouseEvent.h>
#include <vtkContextScene.h>
#include <vtkContextView.h>
#include <vtkEventQtSlotConnect.h>
#include <vtkFloatArray.h>
#include <vtkImageData.h>
#include <vtkIntArray.h>
#include <vtkMathUtilities.h>
#include <vtkObjectFactory.h>
#include <vtkPlotBar.h>
#include <vtkPointData.h>
#include <vtkSMSourceProxy.h>
#include <vtkSMViewProxy.h>
#include <vtkTable.h>
#include <vtkTransform2D.h>
#include <vtkTrivialProducer.h>
#include <vtkContext2D.h>
#include <vtkPen.h>

#include <QtDebug>
#include <QThread>

#include "ActiveObjects.h"
#include "ComputeHistogram.h"
#include "DataSource.h"
#include "ModuleContour.h"
#include "ModuleManager.h"
#include "Utilities.h"

#ifdef DAX_DEVICE_ADAPTER
# include "dax/ModuleStreamingContour.h"
#endif

namespace TEM
{

//-----------------------------------------------------------------------------
// This is just here for now - quick and dirty historgram calculations...
void PopulateHistogram(vtkImageData *input, vtkTable *output)
{
  // The output table will have the twice the number of columns, they will be
  // the x and y for input column. This is the bin centers, and the population.
  double minmax[2] = { 0.0, 0.0 };
  const int numberOfBins = 256;

  // The bin values are the centers, extending +/- half an inc either side
  switch (input->GetScalarType())
    {
    vtkTemplateMacro(
          TEM::GetScalarRange(reinterpret_cast<VTK_TT *>(input->GetPointData()->GetScalars()->GetVoidPointer(0)),
                         input->GetPointData()->GetScalars()->GetNumberOfTuples(),
                         minmax));
    default:
      break;
    }
  if (minmax[0] == minmax[1])
    {
    minmax[1] = minmax[0] + 1.0;
    }

  double inc = (minmax[1] - minmax[0]) / numberOfBins;
  double halfInc = inc / 2.0;
  vtkSmartPointer<vtkFloatArray> extents =
      vtkFloatArray::SafeDownCast(
        output->GetColumnByName(vtkStdString("image_extents").c_str()));
  if (!extents)
    {
    extents = vtkSmartPointer<vtkFloatArray>::New();
    extents->SetName(vtkStdString("image_extents").c_str());
    }
  extents->SetNumberOfTuples(numberOfBins);
  double min = minmax[0] + halfInc;
  for (int j = 0; j < numberOfBins; ++j)
    {
    extents->SetValue(j, min + j * inc);
    }
  vtkSmartPointer<vtkIntArray> populations =
      vtkIntArray::SafeDownCast(
        output->GetColumnByName(vtkStdString("image_pops").c_str()));
  if (!populations)
    {
    populations = vtkSmartPointer<vtkIntArray>::New();
    populations->SetName(vtkStdString("image_pops").c_str());
    }
  populations->SetNumberOfTuples(numberOfBins);
  int *pops = static_cast<int *>(populations->GetVoidPointer(0));
  for (int k = 0; k < numberOfBins; ++k)
    {
    pops[k] = 0;
    }

  switch (input->GetScalarType())
    {
    vtkTemplateMacro(
          TEM::CalculateHistogram(reinterpret_cast<VTK_TT *>(input->GetPointData()->GetScalars()->GetVoidPointer(0)),
                             input->GetPointData()->GetScalars()->GetNumberOfTuples(),
                             minmax[0], pops, inc, numberOfBins));
    default:
      cout << "UpdateFromFile: Unknown data type" << endl;
    }

#ifndef NDEBUG
  vtkIdType total = 0;
  for (int i = 0; i < numberOfBins; ++i)
    total += pops[i];
  assert(total == input->GetPointData()->GetScalars()->GetNumberOfTuples());
#endif

  output->AddColumn(extents.GetPointer());
  output->AddColumn(populations.GetPointer());
}

// Quick background thread for the histogram calculation.
class HistogramWorker : public QThread
{
  Q_OBJECT

  void run();

public:
  HistogramWorker(QObject *p = 0) : QThread(p) {}

  vtkSmartPointer<vtkImageData> input;
  vtkSmartPointer<vtkTable> output;
};

void HistogramWorker::run()
{
  if (input && output)
    {
    PopulateHistogram(input.Get(), output.Get());
    }
}

class CentralWidget::CWInternals
{
public:
  Ui::CentralWidget Ui;
};

class vtkHistogramMarker : public vtkPlot
{
public:
  static vtkHistogramMarker * New();
  double PositionX;

  bool Paint(vtkContext2D *painter)
  {
    vtkNew<vtkPen> pen;
    pen->SetColor(255, 0, 0, 255);
    pen->SetWidth(2.0);
    painter->ApplyPen(pen.Get());
    painter->DrawLine(PositionX, 0, PositionX, 1e9);
    return true;
  }
};
vtkStandardNewMacro(vtkHistogramMarker);

class vtkChartHistogram : public vtkChartXY
{
public:
  static vtkChartHistogram * New();

  bool MouseDoubleClickEvent(const vtkContextMouseEvent &mouse);

  vtkNew<vtkTransform2D> Transform;
  double PositionX;
  vtkNew<vtkHistogramMarker> Marker;
};

vtkStandardNewMacro(vtkChartHistogram)

bool vtkChartHistogram::MouseDoubleClickEvent(const vtkContextMouseEvent &m)
{
  // Determine the location of the click, and emit something we can listen to!
  vtkPlotBar *histo = 0;
  if (this->GetNumberOfPlots() > 0)
    {
    histo = vtkPlotBar::SafeDownCast(this->GetPlot(0));
    }
  if (!histo)
    {
    return false;
    }
  this->CalculateUnscaledPlotTransform(histo->GetXAxis(), histo->GetYAxis(),
                                       this->Transform.Get());
  vtkVector2f pos;
  this->Transform->InverseTransformPoints(m.GetScenePos().GetData(), pos.GetData(),
                                          1);
  this->PositionX = pos.GetX();
  this->Marker->PositionX = this->PositionX;
  this->Marker->Modified();
  this->Scene->SetDirty(true);
  if (this->GetNumberOfPlots() == 1)
    {
    this->AddPlot(this->Marker.Get());
    }
  this->InvokeEvent(vtkCommand::CursorChangedEvent);
  return true;
}

//-----------------------------------------------------------------------------
CentralWidget::CentralWidget(QWidget* parentObject, Qt::WindowFlags wflags)
  : Superclass(parentObject, wflags),
    Internals(new CentralWidget::CWInternals()),
    Worker(NULL)
{
  this->Internals->Ui.setupUi(this);

  QList<int> sizes;
  sizes << 200 << 200;
  this->Internals->Ui.splitter->setSizes(sizes);
  this->Internals->Ui.splitter->setStretchFactor(0, 0);
  this->Internals->Ui.splitter->setStretchFactor(1, 1);

  // Set up our little chart.
  this->Histogram
      ->SetInteractor(this->Internals->Ui.histogramWidget->GetInteractor());
  this->Internals->Ui.histogramWidget
      ->SetRenderWindow(this->Histogram->GetRenderWindow());
  vtkChartHistogram* chart = this->Chart.Get();
  this->Histogram->GetScene()->AddItem(chart);
  chart->SetBarWidthFraction(1.0);
  chart->SetRenderEmpty(true);
  chart->SetAutoAxes(false);
  chart->GetAxis(vtkAxis::LEFT)->SetTitle("");
  chart->GetAxis(vtkAxis::BOTTOM)->SetTitle("");
  chart->GetAxis(vtkAxis::LEFT)->SetBehavior(vtkAxis::FIXED);
  chart->GetAxis(vtkAxis::LEFT)->SetRange(0.0001, 10);
  chart->GetAxis(vtkAxis::LEFT)->SetMinimumLimit(1);
  chart->GetAxis(vtkAxis::LEFT)->SetLogScale(true);

  this->EventLink->Connect(chart, vtkCommand::CursorChangedEvent, this,
                           SLOT(histogramClicked(vtkObject*)));
}

//-----------------------------------------------------------------------------
CentralWidget::~CentralWidget()
{
}

//-----------------------------------------------------------------------------
void CentralWidget::setDataSource(DataSource* source)
{
  if (this->ADataSource)
    {
    this->disconnect(this->ADataSource);
    }
  this->ADataSource = source;
  if (source)
    {
    this->connect(source, SIGNAL(dataChanged()), SLOT(refreshHistogram()));
    }

  // Whenever the data source changes clear the plot, and then populate when
  // ready (or use the cached histogram values.
  this->Chart->ClearPlots();

  if (!source)
    {
    return;
    }

  // Get the actual data source, build a histogram out of it.
  vtkTrivialProducer *t = vtkTrivialProducer::SafeDownCast(
    source->producer()->GetClientSideObject());
  vtkImageData *data = vtkImageData::SafeDownCast(t->GetOutputDataObject(0));

  // Check our cache, and use that if appopriate (or update it).
  if (this->HistogramCache.contains(data))
    {
    vtkTable *cachedTable = this->HistogramCache[data];
    if (cachedTable->GetMTime() > data->GetMTime())
      {
      this->setHistogramTable(cachedTable);
      return;
      }
    else
      {
      // Should this ever happen? Do we want to support this? -- YES!!!
      //qDebug() << "Image data changed after histogram calculation.";
      //return;
      this->HistogramCache.remove(data);
      }
    }

  // Calculate a histogram.
  vtkNew<vtkTable> table;
  this->HistogramCache[data] = table.Get();

  if (!this->Worker)
    {
    this->Worker = new HistogramWorker(this);
    connect(this->Worker, SIGNAL(finished()), SLOT(histogramReady()));
    }
  else if (this->Worker->isRunning())
    {
    // FIXME: Queue, abort, something.
    qDebug() << "Worker already running, skipping this one.";
    return;
    }
  this->Worker->input = data;
  this->Worker->output = table.Get();
  this->Worker->start();
}

void CentralWidget::refreshHistogram()
{
  this->setDataSource(this->ADataSource);
}

void CentralWidget::histogramReady()
{
  if (!this->Worker || !this->Worker->input || !this->Worker->output)
    return;

  this->setHistogramTable(this->Worker->output.Get());

  this->Worker->input = NULL;
  this->Worker->output = NULL;
}

void CentralWidget::histogramClicked(vtkObject *caller)
{
  //qDebug() << "Histogram clicked at" << this->Chart->PositionX
  //         << "making this a great spot to ask for an isosurface at value"
  //         << this->Chart->PositionX;
  Q_ASSERT(this->ADataSource);

  vtkSMViewProxy* view = ActiveObjects::instance().activeView();
  if (!view)
    {
    return;
    }

  // Use active ModuleContour is possible. Otherwise, find the first existing
  // ModuleContour instance or just create a new one, if none exists.
#ifdef DAX_DEVICE_ADAPTER
  typedef ModuleStreamingContour ModuleContourType;
#else
  typedef ModuleContour ModuleContourType;
#endif

  ModuleContourType* contour = qobject_cast<ModuleContourType*>(
    ActiveObjects::instance().activeModule());
  if (!contour)
    {
    QList<ModuleContourType*> contours =
      ModuleManager::instance().findModules<ModuleContourType*>(this->ADataSource, view);
    if (contours.size() == 0)
      {
      contour = qobject_cast<ModuleContourType*>(ModuleManager::instance().createAndAddModule(
          "Contour", this->ADataSource, view));
      }
    else
      {
      contour = contours[0];
      }
    ActiveObjects::instance().setActiveModule(contour);
    }
  Q_ASSERT(contour);
  contour->setIsoValue(this->Chart->PositionX);
  TEM::convert<pqView*>(view)->render();
}

void CentralWidget::setHistogramTable(vtkTable *table)
{
  this->Chart->ClearPlots();
  vtkPlot *plot = this->Chart->AddPlot(vtkChart::BAR);
  plot->SetInputData(table, "image_extents", "image_pops");
  plot->SetColor(0, 0, 255, 255);
  plot->GetPen()->SetLineType(vtkPen::NO_PEN);
  vtkDataArray *arr =
      vtkDataArray::SafeDownCast(table->GetColumnByName("image_pops"));
  if (arr)
    {
    double max = log10(arr->GetRange()[1]);
    vtkAxis *axis = this->Chart->GetAxis(vtkAxis::LEFT);
    axis->SetUnscaledMinimum(1.0);
    axis->SetMaximumLimit(max + 2.0);
    axis->SetMaximum(static_cast<int>(max) + 1.0);
    }
  arr = vtkDataArray::SafeDownCast(table->GetColumnByName("image_extents"));
  if (arr && arr->GetNumberOfTuples() > 2)
    {
    double range[2];
    arr->GetRange(range);
    double halfInc = (arr->GetTuple1(1) - arr->GetTuple1(0)) / 2.0;
    vtkAxis *axis = this->Chart->GetAxis(vtkAxis::BOTTOM);
    axis->SetBehavior(vtkAxis::FIXED);
    axis->SetRange(range[0] - halfInc , range[1] + halfInc);
    }
}

} // end of namespace TEM

#include "CentralWidget.moc"
<|endoftext|>


<commit_before>#include <QFileDialog>
#include <QDir>

#include "include/swri_console/bag_reader.h"

#include <rosbag/bag.h>
#include <rosbag/view.h>

using namespace swri_console;

void BagReader::readBagFile(const QString& filename)
{
  rosbag::Bag bag;
  bag.open(filename.toStdString(), rosbag::bagmode::Read);

  std::vector<std::string> topics;
  topics.push_back(std::string("/rosout"));

  rosbag::View view(bag, rosbag::TopicQuery(topics));
  rosbag::View::const_iterator iter;

  for(iter = view.begin(); iter != view.end(); iter++)
  {
    rosgraph_msgs::LogConstPtr log = iter->instantiate<rosgraph_msgs::Log>();
    if (log != NULL ) {
      emit logReceived(log);
    }
    else {
      qWarning("Got a message that was not a log message but a: %s", iter->getDataType().c_str());
    }
  }

  emit finishedReading();
}

void BagReader::promptForBagFile()
{
  QString filename = QFileDialog::getOpenFileName(NULL,
                                                  tr("Open Bag File"),
                                                  QDir::homePath(),
                                                  tr("Bag Files (*.bag)"));

  if (filename != NULL)
  {
    readBagFile(filename);
  }
}
<commit_msg>Fixing a cppcheck issue.<commit_after>#include <QFileDialog>
#include <QDir>

#include "include/swri_console/bag_reader.h"

#include <rosbag/bag.h>
#include <rosbag/view.h>

using namespace swri_console;

void BagReader::readBagFile(const QString& filename)
{
  rosbag::Bag bag;
  bag.open(filename.toStdString(), rosbag::bagmode::Read);

  std::vector<std::string> topics;
  topics.push_back(std::string("/rosout"));

  rosbag::View view(bag, rosbag::TopicQuery(topics));
  rosbag::View::const_iterator iter;

  for(iter = view.begin(); iter != view.end(); ++iter)
  {
    rosgraph_msgs::LogConstPtr log = iter->instantiate<rosgraph_msgs::Log>();
    if (log != NULL ) {
      emit logReceived(log);
    }
    else {
      qWarning("Got a message that was not a log message but a: %s", iter->getDataType().c_str());
    }
  }

  emit finishedReading();
}

void BagReader::promptForBagFile()
{
  QString filename = QFileDialog::getOpenFileName(NULL,
                                                  tr("Open Bag File"),
                                                  QDir::homePath(),
                                                  tr("Bag Files (*.bag)"));

  if (filename != NULL)
  {
    readBagFile(filename);
  }
}
<|endoftext|>


<commit_before>#include <zip.hpp>
#include <chain.hpp>
#include <range.hpp>

#include <vector>
#include <string>
#include <iostream>
#include <array>

using iter::zip;

int main() {
    //Ryan's test
    {
        std::vector<int> ivec{1, 4, 9, 16, 25, 36};
        std::vector<std::string> svec{"hello", "good day", "goodbye"};

        for (auto e : zip(ivec, svec)) {
            auto &i = std::get<0>(e);
            std::cout << i << std::endl;
            i = 69;
            std::cout << std::get<1>(e) << std::endl;
        }

        for (auto e : zip(ivec, svec)) {
            std::cout << std::get<0>(e) << std::endl;
            std::cout << std::get<1>(e) << std::endl;
        }

        for (auto e : zip(iter::range(10), iter::range(10, 20))) {
            std::cout << std::get<0>(e) << '\n';
            std::cout << std::get<1>(e) << '\n';
        }
    }
    //Aaron's test
    {
        std::array<int,4> i{{1,2,3,4}};
        std::vector<float> f{1.2,1.4,12.3,4.5,9.9};
        std::vector<std::string> s{"i","like","apples","alot","dude"};
        std::array<double,5> d{{1.2,1.2,1.2,1.2,1.2}};
        std::cout << std::endl << "Variadic template zip iterator" << std::endl;
        for (auto e : iter::zip(i,f,s,d)) {
            std::cout << std::get<0>(e) << " "
                << std::get<1>(e) << " "
                << std::get<2>(e) << " "
                << std::get<3>(e) << std::endl;
            std::get<1>(e)=2.2f; //modify the float array
        }
        std::cout<<std::endl;
        for (auto e : iter::zip(i,s,f,d)) {
            std::cout << std::get<0>(e) << " "
                << std::get<1>(e) << " "
                << std::get<2>(e) << " "
                << std::get<3>(e) << std::endl;
        }
        std::cout << std::endl << "Try some weird range differences" << std::endl;
        std::vector<int> empty{};
        for (auto e : iter::zip(empty,f,s,d)) {
            std::cout << std::get<0>(e) << " "
                << std::get<1>(e) << " "
                << std::get<2>(e) << " "
                << std::get<3>(e) << std::endl;
        }
        std::cout<<std::endl;
        for (auto e : iter::zip(f,s,empty,d)) {
            std::cout << std::get<0>(e) << " "
                << std::get<1>(e) << " "
                << std::get<2>(e) << " "
                << std::get<3>(e) << std::endl;
        }//both should print nothing
        std::cout<<std::endl;
        for (auto e : iter::zip(f,s,i,d)) { //i should be the limiter now
            std::cout << std::get<0>(e) << " "
                << std::get<1>(e) << " "
                << std::get<2>(e) << " "
                << std::get<3>(e) << std::endl;
        }
        std::cout<<std::endl;
        const std::vector<double> constvector{1.1,2.2,3.3,4.4};
        for (auto e : zip(iter::chain(std::vector<int>(5,5),std::array<int,2>{{1,2}}),
                    std::initializer_list<std::string>{"asdfas","aaron","ryan","apple","juice"},
                    constvector))
        {

            std::cout << std::get<0>(e) << " "
                << std::get<1>(e) << " "
                << std::get<2>(e) << std::endl;
        }
    }

    return 0;
}

<commit_msg>adds zip test with statically sized array<commit_after>#include <zip.hpp>
#include <chain.hpp>
#include <range.hpp>

#include <vector>
#include <string>
#include <iostream>
#include <array>

using iter::zip;

int main() {
    //Ryan's test
    {
        std::vector<int> ivec{1, 4, 9, 16, 25, 36};
        std::vector<std::string> svec{"hello", "good day", "goodbye"};

        for (auto e : zip(ivec, svec)) {
            auto &i = std::get<0>(e);
            std::cout << i << std::endl;
            i = 69;
            std::cout << std::get<1>(e) << std::endl;
        }
        for (auto e : zip(ivec, svec)) {
            std::cout << std::get<0>(e) << std::endl;
            std::cout << std::get<1>(e) << std::endl;
        }

        for (auto e : zip(iter::range(10), iter::range(10, 20))) {
            std::cout << std::get<0>(e) << '\n';
            std::cout << std::get<1>(e) << '\n';
        }

        int arr[] = {1,2,3,3,4};
        for (auto e : zip(iter::range(10), arr)) {
            std::cout << std::get<0>(e) << '\n';
            std::cout << std::get<1>(e) << '\n';
        }

    }
    //Aaron's test
    {
        std::array<int,4> i{{1,2,3,4}};
        std::vector<float> f{1.2,1.4,12.3,4.5,9.9};
        std::vector<std::string> s{"i","like","apples","alot","dude"};
        std::array<double,5> d{{1.2,1.2,1.2,1.2,1.2}};
        std::cout << std::endl << "Variadic template zip iterator" << std::endl;
        for (auto e : iter::zip(i,f,s,d)) {
            std::cout << std::get<0>(e) << " "
                << std::get<1>(e) << " "
                << std::get<2>(e) << " "
                << std::get<3>(e) << std::endl;
            std::get<1>(e)=2.2f; //modify the float array
        }
        std::cout<<std::endl;
        for (auto e : iter::zip(i,s,f,d)) {
            std::cout << std::get<0>(e) << " "
                << std::get<1>(e) << " "
                << std::get<2>(e) << " "
                << std::get<3>(e) << std::endl;
        }
        std::cout << std::endl << "Try some weird range differences" << std::endl;
        std::vector<int> empty{};
        for (auto e : iter::zip(empty,f,s,d)) {
            std::cout << std::get<0>(e) << " "
                << std::get<1>(e) << " "
                << std::get<2>(e) << " "
                << std::get<3>(e) << std::endl;
        }
        std::cout<<std::endl;
        for (auto e : iter::zip(f,s,empty,d)) {
            std::cout << std::get<0>(e) << " "
                << std::get<1>(e) << " "
                << std::get<2>(e) << " "
                << std::get<3>(e) << std::endl;
        }//both should print nothing
        std::cout<<std::endl;
        for (auto e : iter::zip(f,s,i,d)) { //i should be the limiter now
            std::cout << std::get<0>(e) << " "
                << std::get<1>(e) << " "
                << std::get<2>(e) << " "
                << std::get<3>(e) << std::endl;
        }
        std::cout<<std::endl;
        const std::vector<double> constvector{1.1,2.2,3.3,4.4};
        for (auto e : zip(iter::chain(std::vector<int>(5,5),std::array<int,2>{{1,2}}),
                    std::initializer_list<std::string>{"asdfas","aaron","ryan","apple","juice"},
                    constvector))
        {

            std::cout << std::get<0>(e) << " "
                << std::get<1>(e) << " "
                << std::get<2>(e) << std::endl;
        }
    }

    return 0;
}

<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2017, OpenNebula Project, OpenNebula Systems                */
/*                                                                            */
/* 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 "MarketPlaceManager.h"
#include "MarketPlacePool.h"
#include "MarketPlaceAppPool.h"
#include "MarketPlaceManagerDriver.h"

#include "Nebula.h"

const char * MarketPlaceManager::market_driver_name = "market_exe";

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

extern "C" void * marketplace_action_loop(void *arg)
{
    MarketPlaceManager * mpm;

    if ( arg == 0 )
    {
        return 0;
    }

    NebulaLog::log("MKP", Log::INFO, "Marketplace Manager started.");

    mpm = static_cast<MarketPlaceManager *>(arg);

    mpm->am.loop(mpm->timer_period);

    NebulaLog::log("MKP", Log::INFO, "Marketplace Manager stopped.");

    return 0;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

MarketPlaceManager::MarketPlaceManager(
            time_t _timer_period,
            time_t _monitor_period,
            std::vector<const VectorAttribute*>& _mads):
        MadManager(_mads),
        timer_period(_timer_period),
        monitor_period(_monitor_period),
        imagem(0)
{
    Nebula& nd = Nebula::instance();

    mppool  = nd.get_marketpool();
    apppool = nd.get_apppool();
    dspool  = nd.get_dspool();
    ipool   = nd.get_ipool();

    am.addListener(this);
};

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int MarketPlaceManager::load_mads(int uid)
{
    MarketPlaceManagerDriver * marketm_mad;

    std::ostringstream      oss;
    const VectorAttribute * vattr = 0;

    int rc;

    NebulaLog::log("MKP", Log::INFO,"Loading Marketplace Manager driver.");

    if ( mad_conf.size() > 0 )
    {
        vattr = static_cast<const VectorAttribute *>(mad_conf[0]);
    }

    if ( vattr == 0 )
    {
        NebulaLog::log("MKP", Log::INFO,"Failed to load Marketplace Manager driver.");
        return -1;
    }

    VectorAttribute market_conf("MARKET_MAD", vattr->value());

    market_conf.replace("NAME", market_driver_name);

    marketm_mad= new MarketPlaceManagerDriver(0, market_conf.value(), false,
            mppool, apppool, this);

    rc = add(marketm_mad);

    if ( rc == 0 )
    {
        oss.str("");
        oss << "\tMarketplace Manager loaded";

        NebulaLog::log("MKP", Log::INFO, oss);
    }

    return rc;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void MarketPlaceManager::init_managers()
{
    Nebula& nd = Nebula::instance();

    imagem = nd.get_imagem();
    raftm  = nd.get_raftm();
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int MarketPlaceManager::start()
{
    int            rc;
    pthread_attr_t pattr;

    rc = MadManager::start();

    if ( rc != 0 )
    {
        return -1;
    }

    NebulaLog::log("ImM",Log::INFO,"Starting Marketplace Manager...");

    pthread_attr_init (&pattr);
    pthread_attr_setdetachstate (&pattr, PTHREAD_CREATE_JOINABLE);

    rc = pthread_create(&marketm_thread, &pattr, marketplace_action_loop,
            (void *) this);

    return rc;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

string * MarketPlaceManager::format_message(
    const string& app_data,
    const string& market_data,
    const string& extra_data)
{
    ostringstream oss;

    oss << "<MARKET_DRIVER_ACTION_DATA>"
        << app_data
        << market_data
        << extra_data
        << "</MARKET_DRIVER_ACTION_DATA>";

    return one_util::base64_encode(oss.str());
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void MarketPlaceManager::timer_action(const ActionRequest& ar)
{
    static int mark = 0;
    static int tics = monitor_period - 5; //first monitor in 5 secs

    mark += timer_period;
    tics += timer_period;

    if ( mark >= 600 )
    {
        NebulaLog::log("MKP",Log::INFO,"--Mark--");
        mark = 0;
    }

    if ( tics < monitor_period )
    {
        return;
    }

    tics = 0;

    if ( !raftm->is_leader() && !raftm->is_solo() )
    {
        return;
    }

    int rc;

    std::vector<int> markets;
    std::vector<int>::iterator it;

    rc = mppool->list(markets);

    if ( rc != 0 )
    {
        return;
    }

    for(it = markets.begin() ; it != markets.end(); it++)
    {
        monitor_market(*it);
    }

    return;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void MarketPlaceManager::monitor_market(int mp_id)
{
    std::string  mp_data;
    std::string  mp_name;
    std::string* drv_msg;

    std::ostringstream oss;

    const MarketPlaceManagerDriver* mpmd = get();

    if ( mpmd == 0 )
    {
        oss << "Error getting MarketPlaceManagerDriver";

        NebulaLog::log("MKP", Log::ERROR, oss);
        return;
    }

    MarketPlace * mp = mppool->get(mp_id, true);

    if ( mp == 0 )
    {
        return;
    }

    mp_name = mp->get_name();

    if ( !mp->is_action_supported(MarketPlaceApp::MONITOR) )
    {
        NebulaLog::log("MKP", Log::DEBUG, "Monitoring disabled for market: " +
                mp_name);

        mp->unlock();

        return;
    }

    if ( mp->get_zone_id() != Nebula::instance().get_zone_id() )
    {
        mp->unlock();
        return;
    }

    mp->to_xml(mp_data);

    mp->unlock();

    drv_msg = MarketPlaceManager::format_message("", mp_data, "");

    oss << "Monitoring marketplace " << mp_name  << " (" << mp_id << ")";

    NebulaLog::log("MKP", Log::DEBUG, oss);

    mpmd->monitor(mp_id, *drv_msg);

    delete drv_msg;
}
<commit_msg>Fix race condition in MarketPlaceManager when drivers load takes too long<commit_after>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2017, OpenNebula Project, OpenNebula Systems                */
/*                                                                            */
/* 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 "MarketPlaceManager.h"
#include "MarketPlacePool.h"
#include "MarketPlaceAppPool.h"
#include "MarketPlaceManagerDriver.h"

#include "Nebula.h"

const char * MarketPlaceManager::market_driver_name = "market_exe";

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

extern "C" void * marketplace_action_loop(void *arg)
{
    MarketPlaceManager * mpm;

    if ( arg == 0 )
    {
        return 0;
    }

    NebulaLog::log("MKP", Log::INFO, "Marketplace Manager started.");

    mpm = static_cast<MarketPlaceManager *>(arg);

    mpm->am.loop(mpm->timer_period);

    NebulaLog::log("MKP", Log::INFO, "Marketplace Manager stopped.");

    return 0;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

MarketPlaceManager::MarketPlaceManager(
            time_t _timer_period,
            time_t _monitor_period,
            std::vector<const VectorAttribute*>& _mads):
        MadManager(_mads),
        timer_period(_timer_period),
        monitor_period(_monitor_period),
        imagem(0),
        raftm(0)
{
    Nebula& nd = Nebula::instance();

    mppool  = nd.get_marketpool();
    apppool = nd.get_apppool();
    dspool  = nd.get_dspool();
    ipool   = nd.get_ipool();

    am.addListener(this);
};

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int MarketPlaceManager::load_mads(int uid)
{
    MarketPlaceManagerDriver * marketm_mad;

    std::ostringstream      oss;
    const VectorAttribute * vattr = 0;

    int rc;

    NebulaLog::log("MKP", Log::INFO,"Loading Marketplace Manager driver.");

    if ( mad_conf.size() > 0 )
    {
        vattr = static_cast<const VectorAttribute *>(mad_conf[0]);
    }

    if ( vattr == 0 )
    {
        NebulaLog::log("MKP", Log::INFO,"Failed to load Marketplace Manager driver.");
        return -1;
    }

    VectorAttribute market_conf("MARKET_MAD", vattr->value());

    market_conf.replace("NAME", market_driver_name);

    marketm_mad= new MarketPlaceManagerDriver(0, market_conf.value(), false,
            mppool, apppool, this);

    rc = add(marketm_mad);

    if ( rc == 0 )
    {
        oss.str("");
        oss << "\tMarketplace Manager loaded";

        NebulaLog::log("MKP", Log::INFO, oss);
    }

    return rc;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void MarketPlaceManager::init_managers()
{
    Nebula& nd = Nebula::instance();

    imagem = nd.get_imagem();
    raftm  = nd.get_raftm();
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

int MarketPlaceManager::start()
{
    int            rc;
    pthread_attr_t pattr;

    rc = MadManager::start();

    if ( rc != 0 )
    {
        return -1;
    }

    NebulaLog::log("ImM",Log::INFO,"Starting Marketplace Manager...");

    pthread_attr_init (&pattr);
    pthread_attr_setdetachstate (&pattr, PTHREAD_CREATE_JOINABLE);

    rc = pthread_create(&marketm_thread, &pattr, marketplace_action_loop,
            (void *) this);

    return rc;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

string * MarketPlaceManager::format_message(
    const string& app_data,
    const string& market_data,
    const string& extra_data)
{
    ostringstream oss;

    oss << "<MARKET_DRIVER_ACTION_DATA>"
        << app_data
        << market_data
        << extra_data
        << "</MARKET_DRIVER_ACTION_DATA>";

    return one_util::base64_encode(oss.str());
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void MarketPlaceManager::timer_action(const ActionRequest& ar)
{
    static int mark = 0;
    static int tics = monitor_period - 5; //first monitor in 5 secs

    mark += timer_period;
    tics += timer_period;

    if ( mark >= 600 )
    {
        NebulaLog::log("MKP",Log::INFO,"--Mark--");
        mark = 0;
    }

    if ( tics < monitor_period )
    {
        return;
    }

    tics = 0;

    if (raftm == 0 || (!raftm->is_leader() && !raftm->is_solo()))
    {
        return;
    }

    int rc;

    std::vector<int> markets;
    std::vector<int>::iterator it;

    rc = mppool->list(markets);

    if ( rc != 0 )
    {
        return;
    }

    for(it = markets.begin() ; it != markets.end(); it++)
    {
        monitor_market(*it);
    }

    return;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void MarketPlaceManager::monitor_market(int mp_id)
{
    std::string  mp_data;
    std::string  mp_name;
    std::string* drv_msg;

    std::ostringstream oss;

    const MarketPlaceManagerDriver* mpmd = get();

    if ( mpmd == 0 )
    {
        oss << "Error getting MarketPlaceManagerDriver";

        NebulaLog::log("MKP", Log::ERROR, oss);
        return;
    }

    MarketPlace * mp = mppool->get(mp_id, true);

    if ( mp == 0 )
    {
        return;
    }

    mp_name = mp->get_name();

    if ( !mp->is_action_supported(MarketPlaceApp::MONITOR) )
    {
        NebulaLog::log("MKP", Log::DEBUG, "Monitoring disabled for market: " +
                mp_name);

        mp->unlock();

        return;
    }

    if ( mp->get_zone_id() != Nebula::instance().get_zone_id() )
    {
        mp->unlock();
        return;
    }

    mp->to_xml(mp_data);

    mp->unlock();

    drv_msg = MarketPlaceManager::format_message("", mp_data, "");

    oss << "Monitoring marketplace " << mp_name  << " (" << mp_id << ")";

    NebulaLog::log("MKP", Log::DEBUG, oss);

    mpmd->monitor(mp_id, *drv_msg);

    delete drv_msg;
}
<|endoftext|>