<commit_before>#include <mujincontrollerclient/binpickingtask.h>
#include <iostream>

#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#undef GetUserName // classes with ControllerClient::GetUserName

#include <boost/typeof/std/string.hpp>
#include <boost/typeof/std/vector.hpp>
#include <boost/typeof/std/list.hpp>
#include <boost/typeof/std/map.hpp>
#include <boost/typeof/std/set.hpp>
#include <boost/typeof/std/string.hpp>

#define FOREACH(it, v) for(BOOST_TYPEOF(v) ::iterator it = (v).begin(), __itend__=(v).end(); it != __itend__; ++(it))
#define FOREACH_NOINC(it, v) for(BOOST_TYPEOF(v) ::iterator it = (v).begin(), __itend__=(v).end(); it != __itend__; )

#define FOREACHC(it, v) for(BOOST_TYPEOF(v) ::const_iterator it = (v).begin(), __itend__=(v).end(); it != __itend__; ++(it))
#define FOREACHC_NOINC(it, v) for(BOOST_TYPEOF(v) ::const_iterator it = (v).begin(), __itend__=(v).end(); it != __itend__; )

#else

#define FOREACH(it, v) for(typeof((v).begin())it = (v).begin(); it != (v).end(); (it)++)
#define FOREACH_NOINC(it, v) for(typeof((v).begin())it = (v).begin(); it != (v).end(); )

#define FOREACHC FOREACH
#define FOREACHC_NOINC FOREACH_NOINC

//#define FORIT(it, v) for(it = (v).begin(); it != (v).end(); (it)++)

#if BOOST_VERSION >= 104400
// boost filesystem v3 is present after v1.44, so force using it
#define BOOST_FILESYSTEM_VERSION 3
#endif

// only use boost filesystem on linux since it is difficult to get working correctly with windows
#include <boost/filesystem/operations.hpp>
#include <sys/stat.h>
#include <fcntl.h>

#endif // defined(_WIN32) || defined(_WIN64)

#ifndef MUJIN_TIME
#define MUJIN_TIME
#include <time.h>

#ifndef _WIN32
#if !(defined(CLOCK_GETTIME_FOUND) && (POSIX_TIMERS > 0 || _POSIX_TIMERS > 0))
#include <sys/time.h>
#endif
#else
#include <sys/timeb.h>    // ftime(), struct timeb
inline void usleep(unsigned long microseconds) {
    Sleep((microseconds+999)/1000);
}
#endif

#ifdef _WIN32
inline unsigned long long GetMilliTime()
{
    LARGE_INTEGER count, freq;
    QueryPerformanceCounter(&count);
    QueryPerformanceFrequency(&freq);
    return (unsigned long long)((count.QuadPart * 1000) / freq.QuadPart);
}

inline unsigned long long GetMicroTime()
{
    LARGE_INTEGER count, freq;
    QueryPerformanceCounter(&count);
    QueryPerformanceFrequency(&freq);
    return (count.QuadPart * 1000000) / freq.QuadPart;
}

inline unsigned long long GetNanoTime()
{
    LARGE_INTEGER count, freq;
    QueryPerformanceCounter(&count);
    QueryPerformanceFrequency(&freq);
    return (count.QuadPart * 1000000000) / freq.QuadPart;
}

inline static unsigned long long GetNanoPerformanceTime() {
    return GetNanoTime();
}

#else

inline void GetWallTime(unsigned int& sec, unsigned int& nsec)
{
#if defined(CLOCK_GETTIME_FOUND) && (POSIX_TIMERS > 0 || _POSIX_TIMERS > 0)
    struct timespec start;
    clock_gettime(CLOCK_REALTIME, &start);
    sec  = start.tv_sec;
    nsec = start.tv_nsec;
#else
    struct timeval timeofday;
    gettimeofday(&timeofday,NULL);
    sec  = timeofday.tv_sec;
    nsec = timeofday.tv_usec * 1000;
#endif
}

inline unsigned long long GetMilliTimeOfDay()
{
    struct timeval timeofday;
    gettimeofday(&timeofday,NULL);
    return (unsigned long long)timeofday.tv_sec*1000+(unsigned long long)timeofday.tv_usec/1000;
}

inline unsigned long long GetNanoTime()
{
    unsigned int sec,nsec;
    GetWallTime(sec,nsec);
    return (unsigned long long)sec*1000000000 + (unsigned long long)nsec;
}

inline unsigned long long GetMicroTime()
{
    unsigned int sec,nsec;
    GetWallTime(sec,nsec);
    return (unsigned long long)sec*1000000 + (unsigned long long)nsec/1000;
}

inline unsigned long long GetMilliTime()
{
    unsigned int sec,nsec;
    GetWallTime(sec,nsec);
    return (unsigned long long)sec*1000 + (unsigned long long)nsec/1000000;
}

inline static unsigned long long GetNanoPerformanceTime()
{
#if defined(CLOCK_GETTIME_FOUND) && (POSIX_TIMERS > 0 || _POSIX_TIMERS > 0) && defined(_POSIX_MONOTONIC_CLOCK)
    struct timespec start;
    unsigned int sec, nsec;
    clock_gettime(CLOCK_MONOTONIC, &start);
    sec  = start.tv_sec;
    nsec = start.tv_nsec;
    return (unsigned long long)sec*1000000000 + (unsigned long long)nsec;
#else
    return GetNanoTime();
#endif
}
#endif
#endif

#ifdef _MSC_VER
#ifndef __PRETTY_FUNCTION__
#define __PRETTY_FUNCTION__ __FUNCDNAME__
#endif
#endif

using namespace mujinclient;

#include <boost/program_options.hpp>

int main(int argc, char ** argv)
{
    // parse command line arguments
    namespace bpo = boost::program_options;
    bpo::options_description desc("Options");

    desc.add_options()
        ("help,h", "produce help message")
        ("controller_ip", bpo::value<std::string>()->required(), "ip of the mujin controller, e.g. controller")
        ("controller_port", bpo::value<unsigned int>()->default_value(80), "port of the mujin controller, e.g. 80")
        ("controller_username_password", bpo::value<std::string>()->required(), "username and password to the mujin controller, e.g. username:password")
        ("controller_command_timeout", bpo::value<double>()->default_value(10), "command timeout in seconds, e.g. 10")
        ("locale", bpo::value<std::string>()->default_value("en_US"), "locale to use for the mujin controller client")
        ("binpicking_task_scenepk", bpo::value<std::string>()->required(), "scene pk of the binpicking task on the mujin controller, e.g. officeboltpicking.mujin.dae")
        ("taskparameters", bpo::value<std::string>()->required(), "binpicking task parameters, e.g. {'robotname': 'robot', 'robots':{'robot': {'externalCollisionIO':{}, 'gripperControlInfo':{}, 'robotControllerUri': '', robotDeviceIOUri': '', 'toolname': 'tool'}}}")
        ("slaverequestid", bpo::value<std::string>()->required(), "slaverequestid, e.g. hostname_slave0")
        ("robotname", bpo::value<std::string>()->required(), "robot name, e.g. VS060A3-AV6-NNN-NNN")
        ("regionname", bpo::value<std::string>()->required(), "regionname, e.g. smallcontainer")
        ("objectupdatename", bpo::value<std::string>()->required(), "target object name, e.g. detected")
        ("objecturi", bpo::value<std::string>()->required(), "target object uri, e.g. mujin:/bolt0.mujin.dae")
        ("objectconfidence", bpo::value<std::string>()->default_value("{\"global_confidence\":1.0}"), "target object confidence")
        ("objectextra", bpo::value<std::string>()->default_value(""), "target object extras, e.g. {\"randombox\": {\"height\":100,\"width\":100,\"length\":100}}")
        ("waitinterval", bpo::value<unsigned int>()->default_value(500), "update interval in ms")
        ("pointsfilename", bpo::value<std::string>()->required(), "path to text file containing commaseparated point xyz positions in meter")
        ("pointsize", bpo::value<double>()->default_value(0.005), "pointcloud pointsize")
        ("obstaclename", bpo::value<std::string>()->default_value("__dynamicobstacle__"), "pointcloud obstacle name")
    ;

    bpo::variables_map opts;
    bpo::store(bpo::parse_command_line(argc, argv, desc), opts);
    bool badargs = false;
    try {
        bpo::notify(opts);
    }
    catch(...) {
        badargs = true;
    }
    if(opts.count("help") || badargs) {
        std::cout << "Usage: " << argv[0] << " [OPTS]" << std::endl;
        std::cout << std::endl;
        std::cout << desc << std::endl;
        return (1);
    }

    const std::string controllerIp = opts["controller_ip"].as<std::string>();
    const unsigned int controllerPort = opts["controller_port"].as<unsigned int>();
    const std::string controllerUsernamePass = opts["controller_username_password"].as<std::string>();
    const double controllerCommandTimeout = opts["controller_command_timeout"].as<double>();
    const std::string binpickingTaskScenePk = opts["binpicking_task_scenepk"].as<std::string>();
    const std::string robotname = opts["robotname"].as<std::string>();
    const std::string objectupdatename = opts["objectupdatename"].as<std::string>();
    const std::string objecturi = opts["objecturi"].as<std::string>();
    const std::string objectconfidence = opts["objectconfidence"].as<std::string>();
    const std::string objectextra = opts["objectextra"].as<std::string>();
    const std::string regionname = opts["regionname"].as<std::string>();
    const std::string taskparameters = opts["taskparameters"].as<std::string>();
    const std::string slaverequestid = opts["slaverequestid"].as<std::string>();
    const unsigned int waitinterval = opts["waitinterval"].as<unsigned int>();
    const std::string pointsfilename = opts["pointsfilename"].as<std::string>();
    const double pointsize = opts["pointsize"].as<double>();
    const std::string obstaclename = opts["obstaclename"].as<std::string>();
    const std::string locale = opts["locale"].as<std::string>();

    std::string tasktype = "binpicking";
    try {
        // connect to mujin controller
        std::stringstream url_ss;
        url_ss << "http://"<< controllerIp << ":" << controllerPort;
        ControllerClientPtr controllerclient = CreateControllerClient(controllerUsernamePass, url_ss.str());
        SceneResourcePtr scene(new SceneResource(controllerclient, binpickingTaskScenePk));

        // initialize binpicking task
        BinPickingTaskResourcePtr pBinpickingTask = scene->GetOrCreateBinPickingTaskFromName_UTF8(tasktype+std::string("task1"), tasktype);
        std::string userinfo_json = "{\"username\": \"" + controllerclient->GetUserName() + "\", \"locale\": \"" + locale + "\"}";
        std::cout << "initialzing binpickingtask in UpdateEnvironmentThread with userinfo=" + userinfo_json << " taskparameters=" << taskparameters << " slaverequestid=" << slaverequestid << std::endl;
        pBinpickingTask->Initialize(taskparameters, controllerCommandTimeout, userinfo_json, slaverequestid);

        // populate dummy data
        std::vector<BinPickingTaskResource::DetectedObject> detectedobjects;
        std::vector<Real> points;
        std::string resultstate;

        BinPickingTaskResource::DetectedObject detectedobject;
        detectedobject.name = str(boost::format("%s_%d") % objectupdatename % 0);
        detectedobject.object_uri = objecturi;
        Transform transform;
        transform.quaternion[0] = 1; transform.quaternion[1] = 0; transform.quaternion[2] = 0; transform.quaternion[3] = 0;
        transform.translate[0] = 450; transform.translate[1] = 0; transform.translate[2] = 60;
        detectedobject.transform = transform;
        detectedobject.confidence = objectconfidence;
        detectedobject.timestamp = GetMilliTime();
        detectedobject.extra = objectextra;

        std::ifstream pointsfile(pointsfilename.c_str());
        while (pointsfile) {
            std::string s;
            if (!std::getline(pointsfile, s)) {
                break;
            }
            std::istringstream ss(s);
            while (ss) {
                std::string s;
                if (!std::getline(ss, s, ',')) {
                    break;
                }
                points.push_back(boost::lexical_cast<double>(s));
            }
        }
        std::cout << "loaded " << points.size() / 3 << " points from " << pointsfilename << std::endl;
        while (1) {
            try {
                pBinpickingTask->UpdateEnvironmentState(objectupdatename, detectedobjects, points, resultstate, pointsize, obstaclename, "m", 10);
                std::cout << "UpdateEnvironmentState with " << detectedobjects.size() << " objects " << (points.size()/3.) << " points" << std::endl;
            }
            catch(const std::exception& ex) {
                std::cerr << "Failed to update environment state: " << ex.what() << "." << std::endl;

            }
            boost::this_thread::sleep(boost::posix_time::milliseconds(waitinterval));
        }
    }
    catch(const std::exception& ex) {
        std::stringstream errss;
        errss << "Caught exception " << ex.what();
        std::cerr << errss.str() << std::endl;
    }
    catch (...) {
        std::stringstream errss;
        errss << "Caught unknown exception!";
        std::cerr << errss.str() << std::endl;
    }
}
<commit_msg>cleaned up sample<commit_after>#include <mujincontrollerclient/binpickingtask.h>
#include <iostream>

#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#undef GetUserName // clashes with ControllerClient::GetUserName
#endif // defined(_WIN32) || defined(_WIN64)

#ifndef MUJIN_TIME
#define MUJIN_TIME
#include <time.h>

#ifndef _WIN32
#if !(defined(CLOCK_GETTIME_FOUND) && (POSIX_TIMERS > 0 || _POSIX_TIMERS > 0))
#include <sys/time.h>
#endif // !(defined(CLOCK_GETTIME_FOUND) && (POSIX_TIMERS > 0 || _POSIX_TIMERS > 0))
#else
#include <sys/timeb.h>    // ftime(), struct timeb
inline void usleep(unsigned long microseconds) {
    Sleep((microseconds+999)/1000);
}
#endif  // _WIN32

#ifdef _WIN32
inline unsigned long long GetMilliTime()
{
    LARGE_INTEGER count, freq;
    QueryPerformanceCounter(&count);
    QueryPerformanceFrequency(&freq);
    return (unsigned long long)((count.QuadPart * 1000) / freq.QuadPart);
}

#else

inline void GetWallTime(unsigned int& sec, unsigned int& nsec)
{
#if defined(CLOCK_GETTIME_FOUND) && (POSIX_TIMERS > 0 || _POSIX_TIMERS > 0)
    struct timespec start;
    clock_gettime(CLOCK_REALTIME, &start);
    sec  = start.tv_sec;
    nsec = start.tv_nsec;
#else
    struct timeval timeofday;
    gettimeofday(&timeofday,NULL);
    sec  = timeofday.tv_sec;
    nsec = timeofday.tv_usec * 1000;
#endif //defined(CLOCK_GETTIME_FOUND) && (POSIX_TIMERS > 0 || _POSIX_TIMERS > 0)
}

inline unsigned long long GetMilliTime()
{
    unsigned int sec,nsec;
    GetWallTime(sec,nsec);
    return (unsigned long long)sec*1000 + (unsigned long long)nsec/1000000;
}
#endif // _WIN32
#endif // MUJIN_TIME

#ifdef _MSC_VER
#ifndef __PRETTY_FUNCTION__
#define __PRETTY_FUNCTION__ __FUNCDNAME__
#endif
#endif

using namespace mujinclient;

#include <boost/program_options.hpp>

int main(int argc, char ** argv)
{
    // parse command line arguments
    namespace bpo = boost::program_options;
    bpo::options_description desc("Options");

    desc.add_options()
        ("help,h", "produce help message")
        ("controller_ip", bpo::value<std::string>()->required(), "ip of the mujin controller, e.g. controller")
        ("controller_port", bpo::value<unsigned int>()->default_value(80), "port of the mujin controller, e.g. 80")
        ("controller_username_password", bpo::value<std::string>()->required(), "username and password to the mujin controller, e.g. username:password")
        ("controller_command_timeout", bpo::value<double>()->default_value(10), "command timeout in seconds, e.g. 10")
        ("locale", bpo::value<std::string>()->default_value("en_US"), "locale to use for the mujin controller client")
        ("binpicking_task_scenepk", bpo::value<std::string>()->required(), "scene pk of the binpicking task on the mujin controller, e.g. officeboltpicking.mujin.dae")
        ("taskparameters", bpo::value<std::string>()->required(), "binpicking task parameters, e.g. {'robotname': 'robot', 'robots':{'robot': {'externalCollisionIO':{}, 'gripperControlInfo':{}, 'robotControllerUri': '', robotDeviceIOUri': '', 'toolname': 'tool'}}}")
        ("slaverequestid", bpo::value<std::string>()->required(), "slaverequestid, e.g. hostname_slave0")
        ("robotname", bpo::value<std::string>()->required(), "robot name, e.g. VS060A3-AV6-NNN-NNN")
        ("regionname", bpo::value<std::string>()->required(), "regionname, e.g. smallcontainer")
        ("objectupdatename", bpo::value<std::string>()->required(), "target object name, e.g. detected")
        ("objecturi", bpo::value<std::string>()->required(), "target object uri, e.g. mujin:/bolt0.mujin.dae")
        ("objectconfidence", bpo::value<std::string>()->default_value("{\"global_confidence\":1.0}"), "target object confidence")
        ("objectextra", bpo::value<std::string>()->default_value(""), "target object extras, e.g. {\"randombox\": {\"height\":100,\"width\":100,\"length\":100}}")
        ("waitinterval", bpo::value<unsigned int>()->default_value(500), "update interval in ms")
        ("pointsfilename", bpo::value<std::string>()->required(), "path to text file containing commaseparated point xyz positions in meter")
        ("pointsize", bpo::value<double>()->default_value(0.005), "pointcloud pointsize")
        ("obstaclename", bpo::value<std::string>()->default_value("__dynamicobstacle__"), "pointcloud obstacle name")
    ;

    bpo::variables_map opts;
    bpo::store(bpo::parse_command_line(argc, argv, desc), opts);
    bool badargs = false;
    try {
        bpo::notify(opts);
    }
    catch(...) {
        badargs = true;
    }
    if(opts.count("help") || badargs) {
        std::cout << "Usage: " << argv[0] << " [OPTS]" << std::endl;
        std::cout << std::endl;
        std::cout << desc << std::endl;
        return (1);
    }

    const std::string controllerIp = opts["controller_ip"].as<std::string>();
    const unsigned int controllerPort = opts["controller_port"].as<unsigned int>();
    const std::string controllerUsernamePass = opts["controller_username_password"].as<std::string>();
    const double controllerCommandTimeout = opts["controller_command_timeout"].as<double>();
    const std::string binpickingTaskScenePk = opts["binpicking_task_scenepk"].as<std::string>();
    const std::string robotname = opts["robotname"].as<std::string>();
    const std::string objectupdatename = opts["objectupdatename"].as<std::string>();
    const std::string objecturi = opts["objecturi"].as<std::string>();
    const std::string objectconfidence = opts["objectconfidence"].as<std::string>();
    const std::string objectextra = opts["objectextra"].as<std::string>();
    const std::string regionname = opts["regionname"].as<std::string>();
    const std::string taskparameters = opts["taskparameters"].as<std::string>();
    const std::string slaverequestid = opts["slaverequestid"].as<std::string>();
    const unsigned int waitinterval = opts["waitinterval"].as<unsigned int>();
    const std::string pointsfilename = opts["pointsfilename"].as<std::string>();
    const double pointsize = opts["pointsize"].as<double>();
    const std::string obstaclename = opts["obstaclename"].as<std::string>();
    const std::string locale = opts["locale"].as<std::string>();

    std::string tasktype = "binpicking";
    try {
        // connect to mujin controller
        std::stringstream url_ss;
        url_ss << "http://"<< controllerIp << ":" << controllerPort;
        ControllerClientPtr controllerclient = CreateControllerClient(controllerUsernamePass, url_ss.str());
        SceneResourcePtr scene(new SceneResource(controllerclient, binpickingTaskScenePk));

        // initialize binpicking task
        BinPickingTaskResourcePtr pBinpickingTask = scene->GetOrCreateBinPickingTaskFromName_UTF8(tasktype+std::string("task1"), tasktype);
        std::string userinfo_json = "{\"username\": \"" + controllerclient->GetUserName() + "\", \"locale\": \"" + locale + "\"}";
        std::cout << "initialzing binpickingtask in UpdateEnvironmentThread with userinfo=" + userinfo_json << " taskparameters=" << taskparameters << " slaverequestid=" << slaverequestid << std::endl;
        pBinpickingTask->Initialize(taskparameters, controllerCommandTimeout, userinfo_json, slaverequestid);

        // populate dummy data
        std::vector<BinPickingTaskResource::DetectedObject> detectedobjects;
        std::vector<Real> points;
        std::string resultstate;

        // create object
        BinPickingTaskResource::DetectedObject detectedobject;
        detectedobject.name = str(boost::format("%s_%d") % objectupdatename % 0);
        detectedobject.object_uri = objecturi;
        Transform transform;
        transform.quaternion[0] = 1; transform.quaternion[1] = 0; transform.quaternion[2] = 0; transform.quaternion[3] = 0;
        transform.translate[0] = 450; transform.translate[1] = 0; transform.translate[2] = 60;  // in milimeter
        detectedobject.transform = transform;
        detectedobject.confidence = objectconfidence;
        detectedobject.timestamp = GetMilliTime();
        detectedobject.extra = objectextra;

        // load pointcloud from file, assuming comma seprated xyz coordinates in meter
        std::ifstream pointsfile(pointsfilename.c_str());
        while (pointsfile) {
            std::string s;
            if (!std::getline(pointsfile, s)) {
                break;
            }
            std::istringstream ss(s);
            while (ss) {
                std::string s;
                if (!std::getline(ss, s, ',')) {
                    break;
                }
                points.push_back(boost::lexical_cast<double>(s));
            }
        }
        std::cout << "loaded " << points.size() / 3 << " points from " << pointsfilename << std::endl;

        // update environment loop
        while (1) {
            try {
                pBinpickingTask->UpdateEnvironmentState(objectupdatename, detectedobjects, points, resultstate, pointsize, obstaclename);
                std::cout << "UpdateEnvironmentState with " << detectedobjects.size() << " objects " << (points.size()/3.) << " points" << std::endl;
            }
            catch(const std::exception& ex) {
                std::cerr << "Failed to update environment state: " << ex.what() << "." << std::endl;

            }
            boost::this_thread::sleep(boost::posix_time::milliseconds(waitinterval));
        }
    }
    catch(const std::exception& ex) {
        std::stringstream errss;
        errss << "Caught exception " << ex.what();
        std::cerr << errss.str() << std::endl;
    }
    catch (...) {
        std::stringstream errss;
        errss << "Caught unknown exception!";
        std::cerr << errss.str() << std::endl;
    }
}
<|endoftext|>


<commit_before>#include <UnitTest++.h>

#include "stdio_fixture.h"

SUITE(PrintF)
{
    TEST_FIXTURE(StdIOFixture, BasicPrintFTest)
    {
        int r = fslc_fprintf(&stream, "Hello, World!\n");

        CHECK_EQUAL("Hello, World!\n", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFStringTest)
    {
        int r = fslc_fprintf(&stream, "Hello from %s!\n", "PrintF");

        CHECK_EQUAL("Hello from PrintF!\n", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintF2StringsTest)
    {
        int r = fslc_fprintf(&stream, "%s from %s!\n", "Hello", "PrintF");

        CHECK_EQUAL("Hello from PrintF!\n", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFCharTest)
    {
        int r = fslc_fprintf(&stream, "Hello, %corld!\n", 'W');

        CHECK_EQUAL("Hello, World!\n", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFPercentTest)
    {
        int r = fslc_fprintf(&stream, "Special offer: 50%% off!");

        CHECK_EQUAL("Special offer: 50% off!", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFPrePostMultiTest)
    {
        stream.pre_output = fixture_preop;
        stream.post_output = fixture_postop;

        int r = fslc_fprintf(&stream, "Testing %s%% of all possibilities%c", "a few ", '!');

        const char *expected_str = "Testing a few % of all possibilities!";

        std::vector<FuncCallItem> expected_calls;
        expected_calls.push_back({ CalledFunc::PreOp, 0 });
        for (const char *c = expected_str; *c; ++c)
            expected_calls.push_back({ CalledFunc::PutC, *c });
        expected_calls.push_back({ CalledFunc::PostOp, 0 });

        CHECK(r >= 0);
        CHECK_EQUAL(expected_calls.size(), FuncCallLog.size());
        CHECK_ARRAY_EQUAL(expected_calls, FuncCallLog, FuncCallLog.size());

        CHECK_EQUAL(expected_str, ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFPrePostMultiEofTest)
    {
        stream.pre_output = fixture_preop;
        stream.post_output = fixture_postop;

        eof_counter = 20;

        int r = fslc_fprintf(&stream, "Testing %s%% of all possibilities%c", "a few ", '!');

        const char *expected_str = "Testing a few % of a";
        //                          12345678901234567890

        std::vector<FuncCallItem> expected_calls;
        expected_calls.push_back({ CalledFunc::PreOp, 0 });
        for (const char *c = expected_str; *c; ++c)
            expected_calls.push_back({ CalledFunc::PutC, *c });
        expected_calls.push_back({ CalledFunc::PutC, 'l' }); // EOF
        expected_calls.push_back({ CalledFunc::PostOp, 0 });

        CHECK(r < 0);
        CHECK_EQUAL(expected_calls.size(), FuncCallLog.size());
        CHECK_ARRAY_EQUAL(expected_calls, FuncCallLog, FuncCallLog.size());

        CHECK_EQUAL(expected_str, ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFBasicIntTest)
    {
        int r = fslc_fprintf(&stream, "The answer is %d!", 42);

        CHECK(r >= 0);
        CHECK_EQUAL("The answer is 42!", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFZeroIntTest)
    {
        int r = fslc_fprintf(&stream, "Zero is %d!", 0);

        CHECK(r >= 0);
        CHECK_EQUAL("Zero is 0!", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFNegativeTest)
    {
        int r = fslc_fprintf(&stream, "Less than zero: %d!", -54);

        CHECK(r >= 0);
        CHECK_EQUAL("Less than zero: -54!", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFUIntMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max uint is %u\n", -1);

        CHECK(r >= 0);
        CHECK_EQUAL("Max uint is 4294967295\n", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFBasicHexTest)
    {
        int r = fslc_fprintf(&stream, "The answer is %x!", 0x74df);

        CHECK(r >= 0);
        CHECK_EQUAL("The answer is 74df!", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFBasicUpperHexTest)
    {
        int r = fslc_fprintf(&stream, "The answer is %X!", 0x74df);

        CHECK(r >= 0);
        CHECK_EQUAL("The answer is 74DF!", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFZeroHexTest)
    {
        int r = fslc_fprintf(&stream, "Zero is %x!", 0);

        CHECK(r >= 0);
        CHECK_EQUAL("Zero is 0!", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFUHExMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max hex is %x\n", -1);

        CHECK(r >= 0);
        CHECK_EQUAL("Max hex is ffffffff\n", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFLongIntTest)
    {
        int r = fslc_fprintf(&stream, "The answer is %ld!", 435434432L);

        int e = eprintf("The answer is %ld!", 435434432L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFLongNegativeTest)
    {
        int r = fslc_fprintf(&stream, "Less than zero: %ld!", -57299223L);

        int e = eprintf("Less than zero: %ld!", -57299223L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFULongMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max ulong is %lu\n", -1L);

        int e = eprintf("Max ulong is %lu\n", -1L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFULongHexMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max long hex is %lx\n", -1L);

        int e = eprintf("Max long hex is %lx\n", -1L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFULongHexUpperMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max long hex is %lX\n", -1L);

        int e = eprintf("Max long hex is %lX\n", -1L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFVeryLongIntTest)
    {
        int r = fslc_fprintf(&stream, "The answer is %lld!", 435432343442444432LL);

        int e = eprintf("The answer is %lld!", 435432343442444432LL);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFVeryLongNegativeTest)
    {
        int r = fslc_fprintf(&stream, "Less than zero: %lld!", -5729932434424223L);

        int e = eprintf("Less than zero: %lld!", -5729932434424223L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFUVeryLongMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max ulonglong is %llu\n", -1L);

        int e = eprintf("Max ulonglong is %llu\n", -1L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFUVeryLongHexMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max long long hex is %llx\n", -1L);

        int e = eprintf("Max long long hex is %llx\n", -1L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFUVeryLongUpperHexMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max long long hex is %llX\n", -1L);

        int e = eprintf("Max long long hex is %llX\n", -1L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFIntSignEofTest)
    {
        eof_counter = 10;
        int r = fslc_fprintf(&stream, "Will stop %d", -1223);

        int e = eprintf("Will stop ");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFIntMiddleEofTest)
    {
        eof_counter = 12;
        int r = fslc_fprintf(&stream, "Will stop %d", -1223);

        int e = eprintf("Will stop -1");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFStringMiddleEofTest)
    {
        eof_counter = 12;
        int r = fslc_fprintf(&stream, "Will stop %s", "1223");

        int e = eprintf("Will stop 12");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFCharEofTest)
    {
        eof_counter = 10;
        int r = fslc_fprintf(&stream, "Will stop %c", 'a');

        int e = eprintf("Will stop ");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFPercentEofTest)
    {
        eof_counter = 10;
        int r = fslc_fprintf(&stream, "Will stop %%");

        int e = eprintf("Will stop ");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFUIntMiddleEofTest)
    {
        eof_counter = 12;
        int r = fslc_fprintf(&stream, "Will stop %u", 1223);

        int e = eprintf("Will stop 12");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFUHexMiddleEofTest)
    {
        eof_counter = 12;
        int r = fslc_fprintf(&stream, "Will stop %x", 0xA223);

        int e = eprintf("Will stop a2");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFUHEXMiddleEofTest)
    {
        eof_counter = 12;
        int r = fslc_fprintf(&stream, "Will stop %X", 0xA223);

        int e = eprintf("Will stop A2");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintPPointerTest)
    {
        void *ptr = (void *)0x032D47F2;

        int r = fslc_fprintf(&stream, "A pointer: %p!", ptr);

        int e = eprintf("A pointer: %p!", ptr);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintPPointerEofPrefixTest)
    {
        void *ptr = (void *)0x032D47F2;

        int e = eprintf("A pointer: 0");

        eof_counter = e;

        int r = fslc_fprintf(&stream, "A pointer: %p!", ptr);

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintPPointerEofMiddleTest)
    {
        void *ptr = (void *)0x032D47F2;

        int e = eprintf("A pointer: 0x32");

        eof_counter = e;

        int r = fslc_fprintf(&stream, "A pointer: %p!", ptr);

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

}
<commit_msg>Updated Multi print test using eprintf()<commit_after>#include <UnitTest++.h>

#include "stdio_fixture.h"

SUITE(PrintF)
{
    TEST_FIXTURE(StdIOFixture, BasicPrintFTest)
    {
        int r = fslc_fprintf(&stream, "Hello, World!\n");

        CHECK_EQUAL("Hello, World!\n", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFStringTest)
    {
        int r = fslc_fprintf(&stream, "Hello from %s!\n", "PrintF");

        CHECK_EQUAL("Hello from PrintF!\n", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintF2StringsTest)
    {
        int r = fslc_fprintf(&stream, "%s from %s!\n", "Hello", "PrintF");

        CHECK_EQUAL("Hello from PrintF!\n", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFCharTest)
    {
        int r = fslc_fprintf(&stream, "Hello, %corld!\n", 'W');

        CHECK_EQUAL("Hello, World!\n", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFPercentTest)
    {
        int r = fslc_fprintf(&stream, "Special offer: 50%% off!");

        CHECK_EQUAL("Special offer: 50% off!", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFPrePostMultiTest)
    {
        stream.pre_output = fixture_preop;
        stream.post_output = fixture_postop;

        int r = fslc_fprintf(&stream, "Testing %s%% of all possibilities%c", "a few ", '!');

        int e = eprintf("Testing %s%% of all possibilities%c", "a few ", '!');

        const char *expected_str = expected_fstring.get();

        std::vector<FuncCallItem> expected_calls;
        expected_calls.push_back({ CalledFunc::PreOp, 0 });
        for (const char *c = expected_str; *c; ++c)
            expected_calls.push_back({ CalledFunc::PutC, *c });
        expected_calls.push_back({ CalledFunc::PostOp, 0 });

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_calls.size(), FuncCallLog.size());
        CHECK_ARRAY_EQUAL(expected_calls, FuncCallLog, FuncCallLog.size());

        CHECK_EQUAL(expected_str, ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFPrePostMultiEofTest)
    {
        stream.pre_output = fixture_preop;
        stream.post_output = fixture_postop;

        eof_counter = 20;

        int r = fslc_fprintf(&stream, "Testing %s%% of all possibilities%c", "a few ", '!');

        const char *expected_str = "Testing a few % of a";
        //                          12345678901234567890

        std::vector<FuncCallItem> expected_calls;
        expected_calls.push_back({ CalledFunc::PreOp, 0 });
        for (const char *c = expected_str; *c; ++c)
            expected_calls.push_back({ CalledFunc::PutC, *c });
        expected_calls.push_back({ CalledFunc::PutC, 'l' }); // EOF
        expected_calls.push_back({ CalledFunc::PostOp, 0 });

        CHECK(r < 0);
        CHECK_EQUAL(expected_calls.size(), FuncCallLog.size());
        CHECK_ARRAY_EQUAL(expected_calls, FuncCallLog, FuncCallLog.size());

        CHECK_EQUAL(expected_str, ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFBasicIntTest)
    {
        int r = fslc_fprintf(&stream, "The answer is %d!", 42);

        CHECK(r >= 0);
        CHECK_EQUAL("The answer is 42!", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFZeroIntTest)
    {
        int r = fslc_fprintf(&stream, "Zero is %d!", 0);

        CHECK(r >= 0);
        CHECK_EQUAL("Zero is 0!", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFNegativeTest)
    {
        int r = fslc_fprintf(&stream, "Less than zero: %d!", -54);

        CHECK(r >= 0);
        CHECK_EQUAL("Less than zero: -54!", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFUIntMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max uint is %u\n", -1);

        CHECK(r >= 0);
        CHECK_EQUAL("Max uint is 4294967295\n", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFBasicHexTest)
    {
        int r = fslc_fprintf(&stream, "The answer is %x!", 0x74df);

        CHECK(r >= 0);
        CHECK_EQUAL("The answer is 74df!", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFBasicUpperHexTest)
    {
        int r = fslc_fprintf(&stream, "The answer is %X!", 0x74df);

        CHECK(r >= 0);
        CHECK_EQUAL("The answer is 74DF!", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFZeroHexTest)
    {
        int r = fslc_fprintf(&stream, "Zero is %x!", 0);

        CHECK(r >= 0);
        CHECK_EQUAL("Zero is 0!", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFUHExMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max hex is %x\n", -1);

        CHECK(r >= 0);
        CHECK_EQUAL("Max hex is ffffffff\n", ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFLongIntTest)
    {
        int r = fslc_fprintf(&stream, "The answer is %ld!", 435434432L);

        int e = eprintf("The answer is %ld!", 435434432L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFLongNegativeTest)
    {
        int r = fslc_fprintf(&stream, "Less than zero: %ld!", -57299223L);

        int e = eprintf("Less than zero: %ld!", -57299223L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFULongMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max ulong is %lu\n", -1L);

        int e = eprintf("Max ulong is %lu\n", -1L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFULongHexMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max long hex is %lx\n", -1L);

        int e = eprintf("Max long hex is %lx\n", -1L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFULongHexUpperMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max long hex is %lX\n", -1L);

        int e = eprintf("Max long hex is %lX\n", -1L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFVeryLongIntTest)
    {
        int r = fslc_fprintf(&stream, "The answer is %lld!", 435432343442444432LL);

        int e = eprintf("The answer is %lld!", 435432343442444432LL);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFVeryLongNegativeTest)
    {
        int r = fslc_fprintf(&stream, "Less than zero: %lld!", -5729932434424223L);

        int e = eprintf("Less than zero: %lld!", -5729932434424223L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFUVeryLongMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max ulonglong is %llu\n", -1L);

        int e = eprintf("Max ulonglong is %llu\n", -1L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFUVeryLongHexMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max long long hex is %llx\n", -1L);

        int e = eprintf("Max long long hex is %llx\n", -1L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFUVeryLongUpperHexMaxTest)
    {
        int r = fslc_fprintf(&stream, "Max long long hex is %llX\n", -1L);

        int e = eprintf("Max long long hex is %llX\n", -1L);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintFIntSignEofTest)
    {
        eof_counter = 10;
        int r = fslc_fprintf(&stream, "Will stop %d", -1223);

        int e = eprintf("Will stop ");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFIntMiddleEofTest)
    {
        eof_counter = 12;
        int r = fslc_fprintf(&stream, "Will stop %d", -1223);

        int e = eprintf("Will stop -1");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFStringMiddleEofTest)
    {
        eof_counter = 12;
        int r = fslc_fprintf(&stream, "Will stop %s", "1223");

        int e = eprintf("Will stop 12");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFCharEofTest)
    {
        eof_counter = 10;
        int r = fslc_fprintf(&stream, "Will stop %c", 'a');

        int e = eprintf("Will stop ");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFPercentEofTest)
    {
        eof_counter = 10;
        int r = fslc_fprintf(&stream, "Will stop %%");

        int e = eprintf("Will stop ");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFUIntMiddleEofTest)
    {
        eof_counter = 12;
        int r = fslc_fprintf(&stream, "Will stop %u", 1223);

        int e = eprintf("Will stop 12");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFUHexMiddleEofTest)
    {
        eof_counter = 12;
        int r = fslc_fprintf(&stream, "Will stop %x", 0xA223);

        int e = eprintf("Will stop a2");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintFUHEXMiddleEofTest)
    {
        eof_counter = 12;
        int r = fslc_fprintf(&stream, "Will stop %X", 0xA223);

        int e = eprintf("Will stop A2");

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintPPointerTest)
    {
        void *ptr = (void *)0x032D47F2;

        int r = fslc_fprintf(&stream, "A pointer: %p!", ptr);

        int e = eprintf("A pointer: %p!", ptr);

        CHECK_EQUAL(e, r);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
        CHECK_EQUAL(ostring.str().size(), r);
    }

    TEST_FIXTURE(StdIOFixture, PrintPPointerEofPrefixTest)
    {
        void *ptr = (void *)0x032D47F2;

        int e = eprintf("A pointer: 0");

        eof_counter = e;

        int r = fslc_fprintf(&stream, "A pointer: %p!", ptr);

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

    TEST_FIXTURE(StdIOFixture, PrintPPointerEofMiddleTest)
    {
        void *ptr = (void *)0x032D47F2;

        int e = eprintf("A pointer: 0x32");

        eof_counter = e;

        int r = fslc_fprintf(&stream, "A pointer: %p!", ptr);

        CHECK(r < 0);
        CHECK_EQUAL(expected_fstring.get(), ostring.str());
    }

}
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "datastore.h"
#include "transaction.h"
#include "internal/internal_datastore.h"
#include "internal/internal_transaction.h"
#include "internal/operations/post_operation.h"
#include "internal/operations/put_operation.h"
#include "internal/operations/erase_operation.h"
#include "dummy_values.h"

using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;

namespace You {
namespace DataStore {
namespace UnitTests {
TEST_CLASS(DataStoreApiTest) {
public:
	TEST_METHOD(pushOperationsToTransaction) {
		Transaction sut(DataStore::get().begin());

		std::unique_ptr<Internal::IOperation> postOp =
			std::make_unique<Internal::PostOperation>(0, task1);
		sut->push(std::move(postOp));
		Assert::AreEqual(1U, sut->operationsQueue.size());

		std::unique_ptr<Internal::IOperation> putOp =
			std::make_unique<Internal::PutOperation>(0, task2);
		sut->push(std::move(putOp));
		Assert::AreEqual(2U, sut->operationsQueue.size());

		std::unique_ptr<Internal::IOperation> eraseOp =
			std::make_unique<Internal::EraseOperation>(0);
		sut->push(std::move(eraseOp));
		Assert::AreEqual(3U, sut->operationsQueue.size());

		sut->operationsQueue.clear();
	}

	TEST_METHOD(transactionRollback) {
		Transaction sut(DataStore::get().begin());
		Assert::AreEqual(1U, Internal::DataStore::get().transactionStack.size());

		sut.rollback();
		Assert::AreEqual(0U, Internal::DataStore::get().transactionStack.size());
	}

	TEST_METHOD(constructTransactionWithDataStoreBegin) {
		Transaction sut(DataStore::get().begin());

		DataStore::get().post(0, task1);
		Assert::AreEqual(1U, sut->operationsQueue.size());

		DataStore::get().put(0, task2);
		Assert::AreEqual(2U, sut->operationsQueue.size());

		DataStore::get().erase(0);
		Assert::AreEqual(3U, sut->operationsQueue.size());
		sut.commit();

		Internal::DataStore::get().document.reset();
		Internal::DataStore::get().saveData();
	}

	TEST_METHOD(commitTransaction) {
		Transaction sut(DataStore::get().begin());
		DataStore::get().post(0, task1);
		DataStore::get().post(1, task2);
		DataStore::get().erase(0);
		auto sizeBefore = DataStore::get().getAllTasks().size();
		sut.commit();
		auto sizeAfter = DataStore::get().getAllTasks().size();
		Assert::AreEqual(sizeBefore + 1, sizeAfter);

		Internal::DataStore::get().document.reset();
		Internal::DataStore::get().saveData();
	}

	TEST_METHOD(nestedTransaction) {
		Transaction sut(DataStore::get().begin());
		DataStore::get().post(0, task1);

		Transaction sut2(DataStore::get().begin());
		DataStore::get().post(1, task2);

		; {
			auto sizeBefore = DataStore::get().getAllTasks().size();
			sut2.commit();
			auto sizeAfter = DataStore::get().getAllTasks().size();
			Assert::AreEqual(sizeBefore, sizeAfter);
		}

		Transaction sut3(DataStore::get().begin());
		DataStore::get().erase(0);
		; {
			auto sizeBefore = DataStore::get().getAllTasks().size();
			sut3.commit();
			auto sizeAfter = DataStore::get().getAllTasks().size();
			// Hence, this one should be 1U
			Assert::AreEqual(sizeAfter, sizeBefore);
		}

		; {
			auto sizeBefore = DataStore::get().getAllTasks().size();
			sut.commit();
			auto sizeAfter = DataStore::get().getAllTasks().size();
			Assert::AreEqual(sizeBefore, sizeAfter - 1);
		}

		Internal::DataStore::get().document.reset();
		Internal::DataStore::get().saveData();
	}
};

}  // namespace UnitTests
}  // namespace DataStore
}  // namespace You
<commit_msg>Reset state in the beginning of test too<commit_after>#include "stdafx.h"
#include "datastore.h"
#include "transaction.h"
#include "internal/internal_datastore.h"
#include "internal/internal_transaction.h"
#include "internal/operations/post_operation.h"
#include "internal/operations/put_operation.h"
#include "internal/operations/erase_operation.h"
#include "dummy_values.h"

using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;

namespace You {
namespace DataStore {
namespace UnitTests {
TEST_CLASS(DataStoreApiTest) {
public:
	TEST_METHOD(pushOperationsToTransaction) {
		Transaction sut(DataStore::get().begin());

		std::unique_ptr<Internal::IOperation> postOp =
			std::make_unique<Internal::PostOperation>(0, task1);
		sut->push(std::move(postOp));
		Assert::AreEqual(1U, sut->operationsQueue.size());

		std::unique_ptr<Internal::IOperation> putOp =
			std::make_unique<Internal::PutOperation>(0, task2);
		sut->push(std::move(putOp));
		Assert::AreEqual(2U, sut->operationsQueue.size());

		std::unique_ptr<Internal::IOperation> eraseOp =
			std::make_unique<Internal::EraseOperation>(0);
		sut->push(std::move(eraseOp));
		Assert::AreEqual(3U, sut->operationsQueue.size());

		sut->operationsQueue.clear();
	}

	TEST_METHOD(transactionRollback) {
		Transaction sut(DataStore::get().begin());
		Assert::AreEqual(1U, Internal::DataStore::get().transactionStack.size());

		sut.rollback();
		Assert::AreEqual(0U, Internal::DataStore::get().transactionStack.size());
	}

	TEST_METHOD(constructTransactionWithDataStoreBegin) {
		Internal::DataStore::get().document.reset();
		Internal::DataStore::get().saveData();
		Transaction sut(DataStore::get().begin());

		DataStore::get().post(0, task1);
		Assert::AreEqual(1U, sut->operationsQueue.size());

		DataStore::get().put(0, task2);
		Assert::AreEqual(2U, sut->operationsQueue.size());

		DataStore::get().erase(0);
		Assert::AreEqual(3U, sut->operationsQueue.size());
		sut.commit();

		Internal::DataStore::get().document.reset();
		Internal::DataStore::get().saveData();
	}

	TEST_METHOD(commitTransaction) {
		Internal::DataStore::get().document.reset();
		Internal::DataStore::get().saveData();
		Transaction sut(DataStore::get().begin());
		DataStore::get().post(0, task1);
		DataStore::get().post(1, task2);
		DataStore::get().erase(0);
		auto sizeBefore = DataStore::get().getAllTasks().size();
		sut.commit();
		auto sizeAfter = DataStore::get().getAllTasks().size();
		Assert::AreEqual(sizeBefore + 1, sizeAfter);

		Internal::DataStore::get().document.reset();
		Internal::DataStore::get().saveData();
	}

	TEST_METHOD(nestedTransaction) {
		Internal::DataStore::get().document.reset();
		Internal::DataStore::get().saveData();
		Transaction sut(DataStore::get().begin());
		DataStore::get().post(0, task1);

		Transaction sut2(DataStore::get().begin());
		DataStore::get().post(1, task2);

		; {
			auto sizeBefore = DataStore::get().getAllTasks().size();
			sut2.commit();
			auto sizeAfter = DataStore::get().getAllTasks().size();
			Assert::AreEqual(sizeBefore, sizeAfter);
		}

		Transaction sut3(DataStore::get().begin());
		DataStore::get().erase(0);
		; {
			auto sizeBefore = DataStore::get().getAllTasks().size();
			sut3.commit();
			auto sizeAfter = DataStore::get().getAllTasks().size();
			// Hence, this one should be 1U
			Assert::AreEqual(sizeAfter, sizeBefore);
		}

		; {
			auto sizeBefore = DataStore::get().getAllTasks().size();
			sut.commit();
			auto sizeAfter = DataStore::get().getAllTasks().size();
			Assert::AreEqual(sizeBefore + 1, sizeAfter);
		}

		Internal::DataStore::get().document.reset();
		Internal::DataStore::get().saveData();
	}
};

}  // namespace UnitTests
}  // namespace DataStore
}  // namespace You
<|endoftext|>


<commit_before>/*
 * This file is part of the Electron Orbital Explorer. The Electron
 * Orbital Explorer is distributed under the Simplified BSD License
 * (also called the "BSD 2-Clause License"), in hopes that these
 * rendering techniques might be used by other programmers in
 * applications such as scientific visualization, video gaming, and so
 * on. If you find value in this software and use its technologies for
 * another purpose, I would love to hear back from you at bjthinks (at)
 * gmail (dot) com. If you improve this software and agree to release
 * your modifications under the below license, I encourage you to fork
 * the development tree on github and push your modifications. The
 * Electron Orbital Explorer's development URL is:
 * https://github.com/bjthinks/orbital-explorer
 * (This paragraph is not part of the software license and may be
 * removed.)
 *
 * Copyright (c) 2013, Brian W. Johnson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * + Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * + Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdexcept>
#include <vector>
#include <set>
#include <map>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <complex>
#include <sys/time.h>

#include "glprocs.hh"
#include "callbacks.hh"
#include "shaders.hh"
#include "array.hh"
#include "genericops.hh"
#include "vector.hh"
#include "matrix.hh"
#include "quaternion.hh"
#include "transform.hh"
#include "util.hh"
#include "delaunay.hh"
#include "function.hh"
#include "polynomial.hh"
#include "wavefunction.hh"
#include "tetrahedralize.hh"
#include "oopengl.hh"
#include "mouseevents.hh"
#include "controls.hh"
#include "solid.hh"
#include "cloud.hh"
#include "final.hh"

using namespace std;

// Vertex array objects
static VertexArrayObject *cloud;

// Framebuffer names
static GLuint solidFBO, cloudFBO;

// Textures
static Texture *solidRGBTex, *solidDepthTex, *cloudDensityTex;

// This records the number of primitives, not the number of indices
static unsigned num_tetrahedra;

// The function to visualize
static Orbital *orbital = NULL;

// Subdivision of space into tetrahedra
static TetrahedralSubdivision *ts = NULL;

Texture *attachNewTexture(GLint internalformat, GLenum format,
                          GLenum attachment)
{
  Texture *tex = new Texture();
  glBindTexture(GL_TEXTURE_2D, *tex);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexImage2D(GL_TEXTURE_2D, 0, internalformat, 1, 1, 0,
               format, GL_BYTE, NULL);
  glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D,
                         *tex, 0);
  return tex;
}

void resizeTexture(Texture *name, GLint internalformat, GLenum format,
                   GLuint width, GLuint height)
{
  glBindTexture(GL_TEXTURE_2D, *name);
  glTexImage2D(GL_TEXTURE_2D, 0, internalformat, width, height, 0,
               format, GL_BYTE, NULL);
}

static void checkFramebufferCompleteness()
{
  GLenum isComplete = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
  if (isComplete != GL_FRAMEBUFFER_COMPLETE) {
    printf("Framebuffer not complete!\n");
    printf("glCheckFramebufferStatus returned %x\n", isComplete);
    GetGLError();
    exit(1);
  }
}

void initialize()
{
  initSolids();
  initClouds();
  initFinal();

  // Clouds
  cloud = new VertexArrayObject();

  GetGLError();

  ////////////////////////////////////////////////
  // Set up OpenGL state that will never change //
  ////////////////////////////////////////////////

  glBlendEquation(GL_FUNC_ADD);
  glBlendFunc(GL_ONE, GL_ONE);

  glClearColor(0., 0., 0., 1.);

  GetGLError();

  /////////////////////////////////////////////////////////////////
  // Set up auxiliary FBOs and textures for multistage rendering //
  /////////////////////////////////////////////////////////////////

  // Solid objects
  glGenFramebuffers(1, &solidFBO);
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, solidFBO);
  solidRGBTex = attachNewTexture(GL_RGB8, GL_RGB, GL_COLOR_ATTACHMENT0);
  solidDepthTex = attachNewTexture(GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT,
                                   GL_DEPTH_ATTACHMENT);
  checkFramebufferCompleteness();

  // Clouds
  glGenFramebuffers(1, &cloudFBO);
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, cloudFBO);
  cloudDensityTex = attachNewTexture(GL_RGBA16F, GL_RGB, GL_COLOR_ATTACHMENT0);
  checkFramebufferCompleteness();

  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
  GetGLError();
}

void resizeTextures()
{
  int width = getWidth();
  int height = getHeight();

  // Resize statically-sized textures
  resizeTexture(solidRGBTex, GL_RGB8, GL_RGB, width, height);
  resizeTexture(solidDepthTex, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT,
                width, height);
  resizeTexture(cloudDensityTex, GL_RGBA16F, GL_RGB, width, height);

  GetGLError();
}

Matrix<4,4> generateMvpm(int width, int height, double near, double far)
{
  // Generate the so-called model-view-projection matrix

  double rectangular_imbalance = sqrt(double(width) / double(height));
  double L = -rectangular_imbalance;
  double R =  rectangular_imbalance;
  double B = -1.0 / rectangular_imbalance;
  double T =  1.0 / rectangular_imbalance;
  Matrix<4,4> frustum = transformFrustum(L, R, B, T, near, far);

  Matrix<4,4> translation =
    transformTranslation(-getCameraRadius() * basisVector<3>(2));

  Matrix<4,4> rotation = transformRotation(getCameraRotation());

  return frustum * translation * rotation;
}

void display()
{
  static bool need_full_redraw = true;

  int width = getWidth();
  int height = getHeight();

  static int num_points = 0;
  bool just_started = false;

  // Are we just starting up, or did the wave function change?
  Orbital newOrbital = getOrbital();
  // Did the detail level change?
  static int saved_detail = 0;
  int detail = getDetail();
  if (!orbital || *orbital != newOrbital || saved_detail != detail) {
    saved_detail = detail;

    // Stop any running thread
    if (ts)
      ts->kill();
    // delete checks for NULL, so we don't have to
    delete orbital;
    delete ts;
    orbital = new Orbital(newOrbital);
    ts = new TetrahedralSubdivision(*orbital, orbital->radius());
    num_points = 0;

    // Golden ratio
    const double phi = (1.0 + sqrt(5.0)) / 2.0;
    // 500, 800, 1300, 2100, 3400, 5500, 8900, 14400, 23300, 37700
    int v = 100 * int(pow(phi, double(detail) + 4.0) / sqrt(5.0) + 0.5);
    ts->runUntil(v);
    just_started = true;
  }

  // Only suck down new vertices and tetrahedra if at least 100 more
  // have been calculated -- because locking the mutex for the time it
  // takes to suck down primitives slows down subdivision substantially
  if ((ts->isRunning() && ts->numVertices() > num_points + 100) ||
      ts->isFinished() || just_started) {
    cloud->bind();

    // Tetrahedron indices
    std::vector<unsigned> indices = ts->tetrahedronVertexIndices();
    num_tetrahedra = indices.size() / 4;
    cloud->buffer(GL_ELEMENT_ARRAY_BUFFER, indices);

    GetGLError();

    // Vertex positions
    std::vector<float> positions = ts->vertexPositions();
    num_points = positions.size() / 3;

    // Vertex varying data
    std::vector<float> varyings(positions.size() * 2);
    Vector<3> x;
    for (int p = 0; p < num_points; ++p) {
      x[0] = varyings[6 * p]     = positions[3 * p];
      x[1] = varyings[6 * p + 1] = positions[3 * p + 1];
      x[2] = varyings[6 * p + 2] = positions[3 * p + 2];
      complex<double> density = (*orbital)(x);
      double a = arg(density);
      double r = 0.06;
      varyings[6 * p + 3] = r * cos(a);
      varyings[6 * p + 4] = r * sin(a);
      varyings[6 * p + 5] = abs(density);
    }
    cloud->buffer(GL_ARRAY_BUFFER, varyings);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), 0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float),
                          (void *)(3 * sizeof(float)));

    GetGLError();

    setVerticesTetrahedra(int(num_points), int(num_tetrahedra));

    need_full_redraw = true;
  }

  GetGLError();

  double near = 1.0;
  double far = getCameraRadius() + orbital->radius() * sqrt(2.0);
  Matrix<4,4> mvpm = generateMvpm(width, height, near, far);

  static Matrix<4,4> old_mvpm;
  if (mvpm != old_mvpm)
    need_full_redraw = true;
  old_mvpm = mvpm;
  static int old_width = 0;
  static int old_height = 0;
  if (width != old_width || height != old_height)
    need_full_redraw = true;
  old_width = width;
  old_height = height;

  GetGLError();

  if (need_full_redraw) {
    drawSolids(mvpm, width, height, solidFBO);
    drawClouds(mvpm, width, height, near, far, solidDepthTex,
               cloudFBO, cloud, num_tetrahedra);
    need_full_redraw = false;
  }
  double brightness = pow(1.618, getBrightness());
  if (orbital->square)
    brightness *= brightness;
  drawFinal(width, height, brightness, solidRGBTex, cloudDensityTex);

  glFinish();

  GetGLError();
}

void cleanup()
{}
<commit_msg>Change clear color to all zero -- allows fast clears<commit_after>/*
 * This file is part of the Electron Orbital Explorer. The Electron
 * Orbital Explorer is distributed under the Simplified BSD License
 * (also called the "BSD 2-Clause License"), in hopes that these
 * rendering techniques might be used by other programmers in
 * applications such as scientific visualization, video gaming, and so
 * on. If you find value in this software and use its technologies for
 * another purpose, I would love to hear back from you at bjthinks (at)
 * gmail (dot) com. If you improve this software and agree to release
 * your modifications under the below license, I encourage you to fork
 * the development tree on github and push your modifications. The
 * Electron Orbital Explorer's development URL is:
 * https://github.com/bjthinks/orbital-explorer
 * (This paragraph is not part of the software license and may be
 * removed.)
 *
 * Copyright (c) 2013, Brian W. Johnson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * + Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * + Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdexcept>
#include <vector>
#include <set>
#include <map>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <complex>
#include <sys/time.h>

#include "glprocs.hh"
#include "callbacks.hh"
#include "shaders.hh"
#include "array.hh"
#include "genericops.hh"
#include "vector.hh"
#include "matrix.hh"
#include "quaternion.hh"
#include "transform.hh"
#include "util.hh"
#include "delaunay.hh"
#include "function.hh"
#include "polynomial.hh"
#include "wavefunction.hh"
#include "tetrahedralize.hh"
#include "oopengl.hh"
#include "mouseevents.hh"
#include "controls.hh"
#include "solid.hh"
#include "cloud.hh"
#include "final.hh"

using namespace std;

// Vertex array objects
static VertexArrayObject *cloud;

// Framebuffer names
static GLuint solidFBO, cloudFBO;

// Textures
static Texture *solidRGBTex, *solidDepthTex, *cloudDensityTex;

// This records the number of primitives, not the number of indices
static unsigned num_tetrahedra;

// The function to visualize
static Orbital *orbital = NULL;

// Subdivision of space into tetrahedra
static TetrahedralSubdivision *ts = NULL;

Texture *attachNewTexture(GLint internalformat, GLenum format,
                          GLenum attachment)
{
  Texture *tex = new Texture();
  glBindTexture(GL_TEXTURE_2D, *tex);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexImage2D(GL_TEXTURE_2D, 0, internalformat, 1, 1, 0,
               format, GL_BYTE, NULL);
  glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D,
                         *tex, 0);
  return tex;
}

void resizeTexture(Texture *name, GLint internalformat, GLenum format,
                   GLuint width, GLuint height)
{
  glBindTexture(GL_TEXTURE_2D, *name);
  glTexImage2D(GL_TEXTURE_2D, 0, internalformat, width, height, 0,
               format, GL_BYTE, NULL);
}

static void checkFramebufferCompleteness()
{
  GLenum isComplete = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
  if (isComplete != GL_FRAMEBUFFER_COMPLETE) {
    printf("Framebuffer not complete!\n");
    printf("glCheckFramebufferStatus returned %x\n", isComplete);
    GetGLError();
    exit(1);
  }
}

void initialize()
{
  initSolids();
  initClouds();
  initFinal();

  // Clouds
  cloud = new VertexArrayObject();

  GetGLError();

  ////////////////////////////////////////////////
  // Set up OpenGL state that will never change //
  ////////////////////////////////////////////////

  glBlendEquation(GL_FUNC_ADD);
  glBlendFunc(GL_ONE, GL_ONE);

  glClearColor(0., 0., 0., 0.);

  GetGLError();

  /////////////////////////////////////////////////////////////////
  // Set up auxiliary FBOs and textures for multistage rendering //
  /////////////////////////////////////////////////////////////////

  // Solid objects
  glGenFramebuffers(1, &solidFBO);
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, solidFBO);
  solidRGBTex = attachNewTexture(GL_RGB8, GL_RGB, GL_COLOR_ATTACHMENT0);
  solidDepthTex = attachNewTexture(GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT,
                                   GL_DEPTH_ATTACHMENT);
  checkFramebufferCompleteness();

  // Clouds
  glGenFramebuffers(1, &cloudFBO);
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, cloudFBO);
  cloudDensityTex = attachNewTexture(GL_RGBA16F, GL_RGB, GL_COLOR_ATTACHMENT0);
  checkFramebufferCompleteness();

  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
  GetGLError();
}

void resizeTextures()
{
  int width = getWidth();
  int height = getHeight();

  // Resize statically-sized textures
  resizeTexture(solidRGBTex, GL_RGB8, GL_RGB, width, height);
  resizeTexture(solidDepthTex, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT,
                width, height);
  resizeTexture(cloudDensityTex, GL_RGBA16F, GL_RGB, width, height);

  GetGLError();
}

Matrix<4,4> generateMvpm(int width, int height, double near, double far)
{
  // Generate the so-called model-view-projection matrix

  double rectangular_imbalance = sqrt(double(width) / double(height));
  double L = -rectangular_imbalance;
  double R =  rectangular_imbalance;
  double B = -1.0 / rectangular_imbalance;
  double T =  1.0 / rectangular_imbalance;
  Matrix<4,4> frustum = transformFrustum(L, R, B, T, near, far);

  Matrix<4,4> translation =
    transformTranslation(-getCameraRadius() * basisVector<3>(2));

  Matrix<4,4> rotation = transformRotation(getCameraRotation());

  return frustum * translation * rotation;
}

void display()
{
  static bool need_full_redraw = true;

  int width = getWidth();
  int height = getHeight();

  static int num_points = 0;
  bool just_started = false;

  // Are we just starting up, or did the wave function change?
  Orbital newOrbital = getOrbital();
  // Did the detail level change?
  static int saved_detail = 0;
  int detail = getDetail();
  if (!orbital || *orbital != newOrbital || saved_detail != detail) {
    saved_detail = detail;

    // Stop any running thread
    if (ts)
      ts->kill();
    // delete checks for NULL, so we don't have to
    delete orbital;
    delete ts;
    orbital = new Orbital(newOrbital);
    ts = new TetrahedralSubdivision(*orbital, orbital->radius());
    num_points = 0;

    // Golden ratio
    const double phi = (1.0 + sqrt(5.0)) / 2.0;
    // 500, 800, 1300, 2100, 3400, 5500, 8900, 14400, 23300, 37700
    int v = 100 * int(pow(phi, double(detail) + 4.0) / sqrt(5.0) + 0.5);
    ts->runUntil(v);
    just_started = true;
  }

  // Only suck down new vertices and tetrahedra if at least 100 more
  // have been calculated -- because locking the mutex for the time it
  // takes to suck down primitives slows down subdivision substantially
  if ((ts->isRunning() && ts->numVertices() > num_points + 100) ||
      ts->isFinished() || just_started) {
    cloud->bind();

    // Tetrahedron indices
    std::vector<unsigned> indices = ts->tetrahedronVertexIndices();
    num_tetrahedra = indices.size() / 4;
    cloud->buffer(GL_ELEMENT_ARRAY_BUFFER, indices);

    GetGLError();

    // Vertex positions
    std::vector<float> positions = ts->vertexPositions();
    num_points = positions.size() / 3;

    // Vertex varying data
    std::vector<float> varyings(positions.size() * 2);
    Vector<3> x;
    for (int p = 0; p < num_points; ++p) {
      x[0] = varyings[6 * p]     = positions[3 * p];
      x[1] = varyings[6 * p + 1] = positions[3 * p + 1];
      x[2] = varyings[6 * p + 2] = positions[3 * p + 2];
      complex<double> density = (*orbital)(x);
      double a = arg(density);
      double r = 0.06;
      varyings[6 * p + 3] = r * cos(a);
      varyings[6 * p + 4] = r * sin(a);
      varyings[6 * p + 5] = abs(density);
    }
    cloud->buffer(GL_ARRAY_BUFFER, varyings);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), 0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float),
                          (void *)(3 * sizeof(float)));

    GetGLError();

    setVerticesTetrahedra(int(num_points), int(num_tetrahedra));

    need_full_redraw = true;
  }

  GetGLError();

  double near = 1.0;
  double far = getCameraRadius() + orbital->radius() * sqrt(2.0);
  Matrix<4,4> mvpm = generateMvpm(width, height, near, far);

  static Matrix<4,4> old_mvpm;
  if (mvpm != old_mvpm)
    need_full_redraw = true;
  old_mvpm = mvpm;
  static int old_width = 0;
  static int old_height = 0;
  if (width != old_width || height != old_height)
    need_full_redraw = true;
  old_width = width;
  old_height = height;

  GetGLError();

  if (need_full_redraw) {
    drawSolids(mvpm, width, height, solidFBO);
    drawClouds(mvpm, width, height, near, far, solidDepthTex,
               cloudFBO, cloud, num_tetrahedra);
    need_full_redraw = false;
  }
  double brightness = pow(1.618, getBrightness());
  if (orbital->square)
    brightness *= brightness;
  drawFinal(width, height, brightness, solidRGBTex, cloudDensityTex);

  glFinish();

  GetGLError();
}

void cleanup()
{}
<|endoftext|>


<commit_before>/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * artnetdevice.cpp
 * Art-Net device
 * Copyright (C) 2005  Simon Newton
 *
 * An Art-Net device is an instance of libartnet bound to a single IP address
 * Art-Net is limited to four ports per direction per IP, so in this case
 * our device has 8 ports :
 *
 * Ids 0-3 : Input ports (recv dmx)
 * Ids 4-7 : Output ports (send dmx)
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/service.h>

#include <lla/Logging.h>
#include <llad/Preferences.h>
#include <artnet/artnet.h>

#include "ArtNetDevice.h"
#include "ArtNetPort.h"

using lla::plugin::ArtNetDevice;
using lla::plugin::ArtNetPort;
using lla::Preferences;

/*
 * Handle dmx from the network, called from libartnet
 */
int dmx_handler(artnet_node n, int prt, void *d) {
  ArtNetDevice *device = (ArtNetDevice *) d;

  // don't return non zero here else libartnet will stop processing
  // this should never happen anyway
  if (prt < 0 || prt > ARTNET_MAX_PORTS)
    return 0;

  // signal to the port that the data has changed
  ArtNetPort *port = (ArtNetPort*) device->GetPort(prt);
  port->DmxChanged();
  return 0;
}

/*
 * Notify of remote programming
 */
int program_handler(artnet_node n, void *d) {
  ArtNetDevice *device = (ArtNetDevice *) d;
  device->SaveConfig();
  return 0;
}

namespace lla {
namespace plugin {

using google::protobuf::RpcController;
using google::protobuf::Closure;
using lla::plugin::artnet::Request;
using lla::plugin::artnet::Reply;

const string ArtNetDevice::K_SHORT_NAME_KEY = "short_name";
const string ArtNetDevice::K_LONG_NAME_KEY = "long_name";
const string ArtNetDevice::K_SUBNET_KEY = "subnet";
const string ArtNetDevice::K_IP_KEY = "ip";

/*
 * Create a new Artnet Device
 */
ArtNetDevice::ArtNetDevice(AbstractPlugin *owner,
                           const string &name,
                           lla::Preferences *preferences,
                           bool debug):
  Device(owner, name),
  m_preferences(preferences),
  m_socket(NULL),
  m_node(NULL),
  m_short_name(""),
  m_long_name(""),
  m_subnet(0),
  m_enabled(false),
  m_debug(debug) {
}

/*
 * Cleanup
 */
ArtNetDevice::~ArtNetDevice() {
  if (m_enabled)
    Stop();
}

/*
 * Start this device
 * @return true on success, false on failure
 */
bool ArtNetDevice::Start() {
  ArtNetPort *port;
  string value;
  int subnet;

  /* set up ports */
  for (unsigned int i = 0; i < 2 * ARTNET_MAX_PORTS; i++) {
    port = new ArtNetPort(this, i);
    this->AddPort(port);
  }

  // create new artnet node, and and set config values
  if (m_preferences->GetValue(K_IP_KEY).empty())
    m_node = artnet_new(NULL, m_debug);
  else {
    m_node = artnet_new(m_preferences->GetValue(K_IP_KEY).data(), m_debug);
  }

  if (!m_node) {
    LLA_WARN << "artnet_new failed " << artnet_strerror();
    goto e_dev;
  }

  // node config
  if (artnet_setoem(m_node, 0x04, 0x31)) {
    LLA_WARN << "artnet_setoem failed: " << artnet_strerror();
    goto e_artnet_start;
  }

  value = m_preferences->GetValue(K_SHORT_NAME_KEY);
  if (artnet_set_short_name(m_node, value.data())) {
    LLA_WARN << "artnet_set_short_name failed: " << artnet_strerror();
    goto e_artnet_start;
  }
  m_short_name = value;

  value = m_preferences->GetValue(K_LONG_NAME_KEY);
  if (artnet_set_long_name(m_node, value.data())) {
    LLA_WARN << "artnet_set_long_name failed: " << artnet_strerror();
    goto e_artnet_start;
  }
  m_long_name = value;

  if (artnet_set_node_type(m_node, ARTNET_SRV)) {
    LLA_WARN << "artnet_set_node_type failed: " << artnet_strerror();
    goto e_artnet_start;
  }

  value = m_preferences->GetValue(K_SUBNET_KEY);
  subnet = atoi(value.data());
  if (artnet_set_subnet_addr(m_node, subnet)) {
    LLA_WARN << "artnet_set_subnet_addr failed: " << artnet_strerror();
    goto e_artnet_start;
  }
  m_subnet = subnet;

  // we want to be notified when the node config changes
  if (artnet_set_program_handler(m_node, ::program_handler, (void*) this)) {
    LLA_WARN << "artnet_set_program_handler failed: " << artnet_strerror();
    goto e_artnet_start;
  }

  if (artnet_set_dmx_handler(m_node, ::dmx_handler, (void*) this)) {
    LLA_WARN << "artnet_set_dmx_handler failed: " << artnet_strerror();
    goto e_artnet_start;
  }

  for (int i=0; i < ARTNET_MAX_PORTS; i++) {
    // output ports
    if (artnet_set_port_type(m_node, i, ARTNET_ENABLE_OUTPUT, ARTNET_PORT_DMX)) {
      LLA_WARN << "artnet_set_port_type failed %s", artnet_strerror();
      goto e_artnet_start;
    }

    if (artnet_set_port_addr(m_node, i, ARTNET_OUTPUT_PORT, i)) {
      LLA_WARN << "artnet_set_port_addr failed %s", artnet_strerror();
      goto e_artnet_start;
    }

    if (artnet_set_port_type(m_node, i, ARTNET_ENABLE_INPUT, ARTNET_PORT_DMX)) {
      LLA_WARN << "artnet_set_port_type failed %s", artnet_strerror();
      goto e_artnet_start;
    }

    if (artnet_set_port_addr(m_node, i, ARTNET_INPUT_PORT, i)) {
      LLA_WARN << "artnet_set_port_addr failed %s", artnet_strerror();
      goto e_artnet_start;
    }
  }

  if (artnet_start(m_node)) {
    LLA_WARN << "artnet_start failed: " << artnet_strerror();
    goto e_artnet_start;
  }
  int fd = artnet_get_sd(m_node);
  m_socket = new ConnectedSocket(fd, fd);
  m_enabled = true;
  return true;

e_artnet_start:
  if(artnet_destroy(m_node))
    LLA_WARN << "artnet_destroy failed: " << artnet_strerror();

e_dev:
  DeleteAllPorts();

  return false;
}

/*
 * Stop this device
 */
bool ArtNetDevice::Stop() {
  if (!m_enabled)
    return true;

  DeleteAllPorts();

  if (artnet_stop(m_node)) {
    LLA_WARN << "artnet_stop failed: " << artnet_strerror();
    return false;
  }

  if (artnet_destroy(m_node)) {
    LLA_WARN << "artnet_destroy failed: " << artnet_strerror();
    return false;
  }
  delete m_socket;
  m_socket = NULL;
  m_node = NULL;
  m_enabled = false;
  return true;
}

/*
 * Return the Art-Net node associated with this device
 */
artnet_node ArtNetDevice::GetArtnetNode() const {
  return m_node;
}

/*
 * Called when there is activity on our socket
 * @param socket the socket with activity
 */
int ArtNetDevice::SocketReady(ConnectedSocket *socket) {
  if (artnet_read(m_node, 0)) {
    LLA_WARN << "artnet_read failed: " << artnet_strerror();
    return -1;
  }
  return 0;
}

/*
 * Called when the node is remotely programmed.
 */
int ArtNetDevice::SaveConfig() const {
  //TODO: implement this

  return 0;
}

/*
 * Handle device config messages
 * @param controller An RpcController
 * @param request the request data
 * @param response the response to return
 * @param done the closure to call once the request is complete
 */
void ArtNetDevice::Configure(RpcController *controller,
                             const string &request,
                             string *response,
                             Closure *done) {
    Request request_pb;
    if (!request_pb.ParseFromString(request)) {
      controller->SetFailed("Invalid Request");
      done->Run();
      return;
    }

    switch (request_pb.type()) {
      case lla::plugin::artnet::Request::ARTNET_OPTIONS_REQUEST:
        HandleOptions(&request_pb, response);
        break;
      default:
        controller->SetFailed("Invalid Request");
    }
    done->Run();
}

/*
 * Handle an options request
 */
void ArtNetDevice::HandleOptions(Request *request, string *response) {
  bool status = true;
  if (request->has_options()) {
    const lla::plugin::artnet::OptionsRequest options = request->options();
    if (options.has_short_name()) {
      if (artnet_set_short_name(m_node, options.short_name().data())) {
        LLA_WARN << "set short name failed: " << artnet_strerror();
        status = false;
      }
      m_short_name = options.short_name().substr(0,
                                                 ARTNET_SHORT_NAME_LENGTH - 1);
    }
    if (options.has_long_name()) {
      if (artnet_set_long_name(m_node, options.long_name().data())) {
        LLA_WARN << "set long name failed: " << artnet_strerror();
        status = false;
      }
      m_long_name = options.long_name().substr(0, ARTNET_LONG_NAME_LENGTH - 1);
    }
    if (options.has_subnet()) {
      if (artnet_set_subnet_addr(m_node, options.subnet())) {
        LLA_WARN << "set subnet failed: " << artnet_strerror();
        status = false;
      }
      m_subnet = options.subnet();
    }
    SaveConfig();
  }

  lla::plugin::artnet::Reply reply;
  reply.set_type(lla::plugin::artnet::Reply::ARTNET_OPTIONS_REPLY);
  lla::plugin::artnet::OptionsReply *options_reply = reply.mutable_options();
  options_reply->set_status(status);
  options_reply->set_short_name(m_short_name);
  options_reply->set_long_name(m_long_name);
  options_reply->set_subnet(m_subnet);
  reply.SerializeToString(response);
}

} //plugin
} //lla
<commit_msg> * Fix compile error<commit_after>/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * artnetdevice.cpp
 * Art-Net device
 * Copyright (C) 2005  Simon Newton
 *
 * An Art-Net device is an instance of libartnet bound to a single IP address
 * Art-Net is limited to four ports per direction per IP, so in this case
 * our device has 8 ports :
 *
 * Ids 0-3 : Input ports (recv dmx)
 * Ids 4-7 : Output ports (send dmx)
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/service.h>

#include <lla/Logging.h>
#include <llad/Preferences.h>
#include <artnet/artnet.h>

#include "ArtNetDevice.h"
#include "ArtNetPort.h"

using lla::plugin::ArtNetDevice;
using lla::plugin::ArtNetPort;
using lla::Preferences;

/*
 * Handle dmx from the network, called from libartnet
 */
int dmx_handler(artnet_node n, int prt, void *d) {
  ArtNetDevice *device = (ArtNetDevice *) d;

  // don't return non zero here else libartnet will stop processing
  // this should never happen anyway
  if (prt < 0 || prt > ARTNET_MAX_PORTS)
    return 0;

  // signal to the port that the data has changed
  ArtNetPort *port = (ArtNetPort*) device->GetPort(prt);
  port->DmxChanged();
  return 0;
}

/*
 * Notify of remote programming
 */
int program_handler(artnet_node n, void *d) {
  ArtNetDevice *device = (ArtNetDevice *) d;
  device->SaveConfig();
  return 0;
}

namespace lla {
namespace plugin {

using google::protobuf::RpcController;
using google::protobuf::Closure;
using lla::plugin::artnet::Request;
using lla::plugin::artnet::Reply;

const string ArtNetDevice::K_SHORT_NAME_KEY = "short_name";
const string ArtNetDevice::K_LONG_NAME_KEY = "long_name";
const string ArtNetDevice::K_SUBNET_KEY = "subnet";
const string ArtNetDevice::K_IP_KEY = "ip";

/*
 * Create a new Artnet Device
 */
ArtNetDevice::ArtNetDevice(AbstractPlugin *owner,
                           const string &name,
                           lla::Preferences *preferences,
                           bool debug):
  Device(owner, name),
  m_preferences(preferences),
  m_socket(NULL),
  m_node(NULL),
  m_short_name(""),
  m_long_name(""),
  m_subnet(0),
  m_enabled(false),
  m_debug(debug) {
}

/*
 * Cleanup
 */
ArtNetDevice::~ArtNetDevice() {
  if (m_enabled)
    Stop();
}

/*
 * Start this device
 * @return true on success, false on failure
 */
bool ArtNetDevice::Start() {
  ArtNetPort *port;
  string value;
  int subnet, fd = 0;

  /* set up ports */
  for (unsigned int i = 0; i < 2 * ARTNET_MAX_PORTS; i++) {
    port = new ArtNetPort(this, i);
    this->AddPort(port);
  }

  // create new artnet node, and and set config values
  if (m_preferences->GetValue(K_IP_KEY).empty())
    m_node = artnet_new(NULL, m_debug);
  else {
    m_node = artnet_new(m_preferences->GetValue(K_IP_KEY).data(), m_debug);
  }

  if (!m_node) {
    LLA_WARN << "artnet_new failed " << artnet_strerror();
    goto e_dev;
  }

  // node config
  if (artnet_setoem(m_node, 0x04, 0x31)) {
    LLA_WARN << "artnet_setoem failed: " << artnet_strerror();
    goto e_artnet_start;
  }

  value = m_preferences->GetValue(K_SHORT_NAME_KEY);
  if (artnet_set_short_name(m_node, value.data())) {
    LLA_WARN << "artnet_set_short_name failed: " << artnet_strerror();
    goto e_artnet_start;
  }
  m_short_name = value;

  value = m_preferences->GetValue(K_LONG_NAME_KEY);
  if (artnet_set_long_name(m_node, value.data())) {
    LLA_WARN << "artnet_set_long_name failed: " << artnet_strerror();
    goto e_artnet_start;
  }
  m_long_name = value;

  if (artnet_set_node_type(m_node, ARTNET_SRV)) {
    LLA_WARN << "artnet_set_node_type failed: " << artnet_strerror();
    goto e_artnet_start;
  }

  value = m_preferences->GetValue(K_SUBNET_KEY);
  subnet = atoi(value.data());
  if (artnet_set_subnet_addr(m_node, subnet)) {
    LLA_WARN << "artnet_set_subnet_addr failed: " << artnet_strerror();
    goto e_artnet_start;
  }
  m_subnet = subnet;

  // we want to be notified when the node config changes
  if (artnet_set_program_handler(m_node, ::program_handler, (void*) this)) {
    LLA_WARN << "artnet_set_program_handler failed: " << artnet_strerror();
    goto e_artnet_start;
  }

  if (artnet_set_dmx_handler(m_node, ::dmx_handler, (void*) this)) {
    LLA_WARN << "artnet_set_dmx_handler failed: " << artnet_strerror();
    goto e_artnet_start;
  }

  for (int i=0; i < ARTNET_MAX_PORTS; i++) {
    // output ports
    if (artnet_set_port_type(m_node, i, ARTNET_ENABLE_OUTPUT, ARTNET_PORT_DMX)) {
      LLA_WARN << "artnet_set_port_type failed %s", artnet_strerror();
      goto e_artnet_start;
    }

    if (artnet_set_port_addr(m_node, i, ARTNET_OUTPUT_PORT, i)) {
      LLA_WARN << "artnet_set_port_addr failed %s", artnet_strerror();
      goto e_artnet_start;
    }

    if (artnet_set_port_type(m_node, i, ARTNET_ENABLE_INPUT, ARTNET_PORT_DMX)) {
      LLA_WARN << "artnet_set_port_type failed %s", artnet_strerror();
      goto e_artnet_start;
    }

    if (artnet_set_port_addr(m_node, i, ARTNET_INPUT_PORT, i)) {
      LLA_WARN << "artnet_set_port_addr failed %s", artnet_strerror();
      goto e_artnet_start;
    }
  }

  if (artnet_start(m_node)) {
    LLA_WARN << "artnet_start failed: " << artnet_strerror();
    goto e_artnet_start;
  }
  fd = artnet_get_sd(m_node);
  m_socket = new ConnectedSocket(fd, fd);
  m_enabled = true;
  return true;

e_artnet_start:
  if(artnet_destroy(m_node))
    LLA_WARN << "artnet_destroy failed: " << artnet_strerror();

e_dev:
  DeleteAllPorts();

  return false;
}

/*
 * Stop this device
 */
bool ArtNetDevice::Stop() {
  if (!m_enabled)
    return true;

  DeleteAllPorts();

  if (artnet_stop(m_node)) {
    LLA_WARN << "artnet_stop failed: " << artnet_strerror();
    return false;
  }

  if (artnet_destroy(m_node)) {
    LLA_WARN << "artnet_destroy failed: " << artnet_strerror();
    return false;
  }
  delete m_socket;
  m_socket = NULL;
  m_node = NULL;
  m_enabled = false;
  return true;
}

/*
 * Return the Art-Net node associated with this device
 */
artnet_node ArtNetDevice::GetArtnetNode() const {
  return m_node;
}

/*
 * Called when there is activity on our socket
 * @param socket the socket with activity
 */
int ArtNetDevice::SocketReady(ConnectedSocket *socket) {
  if (artnet_read(m_node, 0)) {
    LLA_WARN << "artnet_read failed: " << artnet_strerror();
    return -1;
  }
  return 0;
}

/*
 * Called when the node is remotely programmed.
 */
int ArtNetDevice::SaveConfig() const {
  //TODO: implement this

  return 0;
}

/*
 * Handle device config messages
 * @param controller An RpcController
 * @param request the request data
 * @param response the response to return
 * @param done the closure to call once the request is complete
 */
void ArtNetDevice::Configure(RpcController *controller,
                             const string &request,
                             string *response,
                             Closure *done) {
    Request request_pb;
    if (!request_pb.ParseFromString(request)) {
      controller->SetFailed("Invalid Request");
      done->Run();
      return;
    }

    switch (request_pb.type()) {
      case lla::plugin::artnet::Request::ARTNET_OPTIONS_REQUEST:
        HandleOptions(&request_pb, response);
        break;
      default:
        controller->SetFailed("Invalid Request");
    }
    done->Run();
}

/*
 * Handle an options request
 */
void ArtNetDevice::HandleOptions(Request *request, string *response) {
  bool status = true;
  if (request->has_options()) {
    const lla::plugin::artnet::OptionsRequest options = request->options();
    if (options.has_short_name()) {
      if (artnet_set_short_name(m_node, options.short_name().data())) {
        LLA_WARN << "set short name failed: " << artnet_strerror();
        status = false;
      }
      m_short_name = options.short_name().substr(0,
                                                 ARTNET_SHORT_NAME_LENGTH - 1);
    }
    if (options.has_long_name()) {
      if (artnet_set_long_name(m_node, options.long_name().data())) {
        LLA_WARN << "set long name failed: " << artnet_strerror();
        status = false;
      }
      m_long_name = options.long_name().substr(0, ARTNET_LONG_NAME_LENGTH - 1);
    }
    if (options.has_subnet()) {
      if (artnet_set_subnet_addr(m_node, options.subnet())) {
        LLA_WARN << "set subnet failed: " << artnet_strerror();
        status = false;
      }
      m_subnet = options.subnet();
    }
    SaveConfig();
  }

  lla::plugin::artnet::Reply reply;
  reply.set_type(lla::plugin::artnet::Reply::ARTNET_OPTIONS_REPLY);
  lla::plugin::artnet::OptionsReply *options_reply = reply.mutable_options();
  options_reply->set_status(status);
  options_reply->set_short_name(m_short_name);
  options_reply->set_long_name(m_long_name);
  options_reply->set_subnet(m_subnet);
  reply.SerializeToString(response);
}

} //plugin
} //lla
<|endoftext|>