<commit_before>#include "data-course.hpp"
using namespace std;

//////////////
/////////////
// Constructor helper methods
///////////
//////////

void Course::init(string identifier) {
	copy(getCourse(identifier));
}

void Course::copy(const Course& c) {
	id = c.id;
	title = c.title;
	description = c.description;
	majors = c.majors;
	department = c.department;
	concentrations = c.concentrations;
	conversations = c.conversations;
	professor = c.professor;

	half_semester = c.half_semester;
	pass_fail = c.pass_fail;
	credits = c.credits;
	location = c.location;

	courseType = c.courseType;
	geneds = c.geneds;

	for (int i = 0; i < 7; ++i){
		days[i] = c.days[i];
		time[i] = c.time[i];
	}
}

//////////////
/////////////
// Constructors
///////////
//////////

Course::Course() {

}

Course::Course(string str) {
	init(str);
}

Course::Course(const Course& c) {
	copy(c);
}

Course& Course::operator = (const Course &c) {
	if (this == &c) return *this;
	copy(c);
	return *this;
}

Course::Course(istream &is) {
	if (!is) return;

	string tmpLine;
	getline(is, tmpLine); // read in so we can do things with it.

	vector<string> record = split(tmpLine, ',');
	for (vector<string>::iterator i=record.begin(); i != record.end(); ++i) {
		*i = removeAllQuotes(*i);
		*i = removeTrailingSlashes(*i);
	}

	// printEntireRecord(record);

	// Ignore the first column;
	// record.at(0);

	// so, the *first* column (that we care about) has the course id,
	// and the second column has the section,
	string str = record.at(1) + record.at(2);
	id = ID(str);

	// Third holds the lab boolean,
	     if (record.at(3) == "L") courseType = LAB;
	else if (record.at(3) == "S") courseType = SEMINAR;
	else if (record.at(3) == "T") courseType = TOPIC;
	else                          courseType = COURSE;

	// while Fourth contains the title of the course;
	title = record.at(4);

	// Fifth hands over the length (half semester or not)
	// it's actually an int that tells us how many times the course is offered per semester.
	half_semester = stringToInt(record.at(5));
	if (half_semester != 0 && half_semester != 1 && half_semester != 2)
		half_semester = 0;

	// Sixth tells us the number of credits,
	credits = stringToFloat(record.at(6));

	// Seventh shows us if it can be taken pass/no-pass,
	if (record.at(7) == "Y")
		pass_fail = true;
	else
		pass_fail = false;

	// while Eighth gives us the GEs of the course,
	// GEreqs = record.at(8);

	// and Nine spits out the days and times;
	// Times = record.at(9);

	// Ten holds the location,
	location = record.at(10);
	// location = deDoubleString(location);

	// and Eleven knows who teaches.
	if (record.size() == 13) {
		string profLastName = record.at(11);
		string profFirstName = record.at(12);
		profFirstName.erase(0, 1); // remove the extra space from the start of the name
		professor = profFirstName + " " + profLastName;
	}
	else
		professor = record.at(11);

	title = cleanTitle(title);
	record.clear();
}

string Course::cleanTitle(string title) {
	vector<string> badEndings, badBeginnings;

	badEndings.push_back(" Closed");
	badEndings.push_back(" During course submission process");
	badEndings.push_back(" Especially for ");
	badEndings.push_back(" Film screenings");
	badEndings.push_back(" First-Year Students may register only");
	badEndings.push_back(" New course");
	badEndings.push_back(" Not open to first-year students.");
	badEndings.push_back(" Open only to ");
	badEndings.push_back(" Open to ");
	badEndings.push_back(" Permission of ");
	badEndings.push_back(" Prereq");
	badEndings.push_back(" Registration");
	badEndings.push_back(" Taught in English.");
	badEndings.push_back(" This course");
	badEndings.push_back(" This lab has been canceled.");
	badEndings.push_back(" Students in ");

	badBeginnings.push_back("Top: ");
	badBeginnings.push_back("Sem: ");
	badBeginnings.push_back("Res: ");

	for (vector<string>::iterator i=badEndings.begin(); i != badEndings.end(); ++i)
		title = removeTrailingText(title, *i);
	for (vector<string>::iterator i=badBeginnings.begin(); i != badBeginnings.end(); ++i)
		title = removeStartingText(title, *i);

	return title;
}

//////////////
/////////////
// Getters
///////////
//////////

string Course::getType() {
	     if (courseType == LAB    ) return "Lab";
	else if (courseType == SEMINAR) return "Seminar";
	else if (courseType == TOPIC  ) return "Topic";
	else                            return "Course";
}

string Course::getProfessor() {
	return professor;
}

ID Course::getID() {
	return id;
}

Department Course::getDepartment(int i = 0) {
	return department[i];
}

int Course::getNumber() {
	return id.getNumber();
}

string Course::getSection() {
	return id.getSection();
}

//////////////
/////////////
/// Operator overrides and supporting tools
///////////
//////////

bool operator== (Course &c1, Course &c2) {
    return (c1.id == c2.id);
}

bool operator!= (Course &c1, Course &c2) {
    return !(c1 == c2);
}

ostream& Course::getData(ostream &os) {
	os << getType() << ": ";
	os << id;
	os << " - ";
	os << title << " | ";
	if (professor.length() > 0 && professor != " ")
		os << professor;
	return os;
}

ostream &operator<<(ostream &os, Course &item) {
	return item.getData(os);
}

//////////////
/////////////
/// Methods of displaying the class
///////////
//////////

void Course::showAll() {
	cout << id << endl;
	cout << "Title: " << title << endl;
	cout << "Professor: " << professor << endl;
	cout << "Type: " << courseType << endl;
	cout << "Half-semester? " << half_semester << endl;
	cout << "Credits: " << credits << endl;
	cout << "Pass/Fail? " << pass_fail << endl;
	cout << "Location: " << location << endl;
	cout << endl;
}

void Course::display() {
	cout << *this << endl;
}

//////////////
/////////////
/// The all-important getCourse
///////////
//////////

Course getCourse(string identifier) {
	// cout << "called getCourse with '" << identifier << "'" << endl;
	ID id(identifier);
	return mapped_courses[id];
}
<commit_msg>Shrink the number of lines used in the empty Course constructor<commit_after>#include "data-course.hpp"
using namespace std;

//////////////
/////////////
// Constructor helper methods
///////////
//////////

void Course::init(string identifier) {
	copy(getCourse(identifier));
}

void Course::copy(const Course& c) {
	id = c.id;
	title = c.title;
	description = c.description;
	majors = c.majors;
	department = c.department;
	concentrations = c.concentrations;
	conversations = c.conversations;
	professor = c.professor;

	half_semester = c.half_semester;
	pass_fail = c.pass_fail;
	credits = c.credits;
	location = c.location;

	courseType = c.courseType;
	geneds = c.geneds;

	for (int i = 0; i < 7; ++i){
		days[i] = c.days[i];
		time[i] = c.time[i];
	}
}

//////////////
/////////////
// Constructors
///////////
//////////

Course::Course() {}

Course::Course(string str) {
	init(str);
}

Course::Course(const Course& c) {
	copy(c);
}

Course& Course::operator = (const Course &c) {
	if (this == &c) return *this;
	copy(c);
	return *this;
}

Course::Course(istream &is) {
	if (!is) return;

	string tmpLine;
	getline(is, tmpLine); // read in so we can do things with it.

	vector<string> record = split(tmpLine, ',');
	for (vector<string>::iterator i=record.begin(); i != record.end(); ++i) {
		*i = removeAllQuotes(*i);
		*i = removeTrailingSlashes(*i);
	}

	// printEntireRecord(record);

	// Ignore the first column;
	// record.at(0);

	// so, the *first* column (that we care about) has the course id,
	// and the second column has the section,
	string str = record.at(1) + record.at(2);
	id = ID(str);

	// Third holds the lab boolean,
	     if (record.at(3) == "L") courseType = LAB;
	else if (record.at(3) == "S") courseType = SEMINAR;
	else if (record.at(3) == "T") courseType = TOPIC;
	else                          courseType = COURSE;

	// while Fourth contains the title of the course;
	title = record.at(4);

	// Fifth hands over the length (half semester or not)
	// it's actually an int that tells us how many times the course is offered per semester.
	half_semester = stringToInt(record.at(5));
	if (half_semester != 0 && half_semester != 1 && half_semester != 2)
		half_semester = 0;

	// Sixth tells us the number of credits,
	credits = stringToFloat(record.at(6));

	// Seventh shows us if it can be taken pass/no-pass,
	if (record.at(7) == "Y")
		pass_fail = true;
	else
		pass_fail = false;

	// while Eighth gives us the GEs of the course,
	// GEreqs = record.at(8);

	// and Nine spits out the days and times;
	// Times = record.at(9);

	// Ten holds the location,
	location = record.at(10);
	// location = deDoubleString(location);

	// and Eleven knows who teaches.
	if (record.size() == 13) {
		string profLastName = record.at(11);
		string profFirstName = record.at(12);
		profFirstName.erase(0, 1); // remove the extra space from the start of the name
		professor = profFirstName + " " + profLastName;
	}
	else
		professor = record.at(11);

	title = cleanTitle(title);
	record.clear();
}

string Course::cleanTitle(string title) {
	vector<string> badEndings, badBeginnings;

	badEndings.push_back(" Closed");
	badEndings.push_back(" During course submission process");
	badEndings.push_back(" Especially for ");
	badEndings.push_back(" Film screenings");
	badEndings.push_back(" First-Year Students may register only");
	badEndings.push_back(" New course");
	badEndings.push_back(" Not open to first-year students.");
	badEndings.push_back(" Open only to ");
	badEndings.push_back(" Open to ");
	badEndings.push_back(" Permission of ");
	badEndings.push_back(" Prereq");
	badEndings.push_back(" Registration");
	badEndings.push_back(" Taught in English.");
	badEndings.push_back(" This course");
	badEndings.push_back(" This lab has been canceled.");
	badEndings.push_back(" Students in ");

	badBeginnings.push_back("Top: ");
	badBeginnings.push_back("Sem: ");
	badBeginnings.push_back("Res: ");

	for (vector<string>::iterator i=badEndings.begin(); i != badEndings.end(); ++i)
		title = removeTrailingText(title, *i);
	for (vector<string>::iterator i=badBeginnings.begin(); i != badBeginnings.end(); ++i)
		title = removeStartingText(title, *i);

	return title;
}

//////////////
/////////////
// Getters
///////////
//////////

string Course::getType() {
	     if (courseType == LAB    ) return "Lab";
	else if (courseType == SEMINAR) return "Seminar";
	else if (courseType == TOPIC  ) return "Topic";
	else                            return "Course";
}

string Course::getProfessor() {
	return professor;
}

ID Course::getID() {
	return id;
}

Department Course::getDepartment(int i = 0) {
	return department[i];
}

int Course::getNumber() {
	return id.getNumber();
}

string Course::getSection() {
	return id.getSection();
}

//////////////
/////////////
/// Operator overrides and supporting tools
///////////
//////////

bool operator== (Course &c1, Course &c2) {
    return (c1.id == c2.id);
}

bool operator!= (Course &c1, Course &c2) {
    return !(c1 == c2);
}

ostream& Course::getData(ostream &os) {
	os << getType() << ": ";
	os << id;
	os << " - ";
	os << title << " | ";
	if (professor.length() > 0 && professor != " ")
		os << professor;
	return os;
}

ostream &operator<<(ostream &os, Course &item) {
	return item.getData(os);
}

//////////////
/////////////
/// Methods of displaying the class
///////////
//////////

void Course::showAll() {
	cout << id << endl;
	cout << "Title: " << title << endl;
	cout << "Professor: " << professor << endl;
	cout << "Type: " << courseType << endl;
	cout << "Half-semester? " << half_semester << endl;
	cout << "Credits: " << credits << endl;
	cout << "Pass/Fail? " << pass_fail << endl;
	cout << "Location: " << location << endl;
	cout << endl;
}

void Course::display() {
	cout << *this << endl;
}

//////////////
/////////////
/// The all-important getCourse
///////////
//////////

Course getCourse(string identifier) {
	// cout << "called getCourse with '" << identifier << "'" << endl;
	ID id(identifier);
	return mapped_courses[id];
}
<|endoftext|>


<commit_before>#include "robobo.h"

int main(int argc, char** argv) {
	if (argc > 1) { // analyze arguments
		if (strcmp(argv[1], "--help") == 0) {
			// put help here eventually
			return 0;
		}
		if (strcmp(argv[1], "--version") == 0) {
			std::cout << "RoBoBo-IRC-BoBo Pre-alpha Development Version" << std::endl;
			return 0;
		}
	}
	ConfigReader config;
	new ModuleInterface (&config);
	pthread_exit(NULL);
}<commit_msg>Small change.<commit_after>#include "robobo.h"

int main(int argc, char** argv) {
	if (argc > 1) { // analyze arguments
		if (strcmp(argv[1], "--help") == 0) {
			// put help here eventually
			return 0;
		}
		if (strcmp(argv[1], "--version") == 0) {
			std::cout << "RoBoBo-IRC-BoBo Pre-alpha Development Version" << std::endl;
			return 0;
		}
	}
	new ModuleInterface (new ConfigReader ());
	pthread_exit(NULL);
}<|endoftext|>


<commit_before>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * 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
 * FOUNDATION 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 "http_server/http_server.hxx"
#include "http_server/Request.hxx"
#include "http_server/Handler.hxx"
#include "http_headers.hxx"
#include "duplex.hxx"
#include "direct.hxx"
#include "istream/sink_null.hxx"
#include "istream/ByteIstream.hxx"
#include "istream/DelayedIstream.hxx"
#include "istream/HeadIstream.hxx"
#include "istream/istream_hold.hxx"
#include "istream/istream_memory.hxx"
#include "istream/ZeroIstream.hxx"
#include "istream/istream.hxx"
#include "istream/UnusedHoldPtr.hxx"
#include "PInstance.hxx"
#include "pool/pool.hxx"
#include "event/TimerEvent.hxx"
#include "event/ShutdownListener.hxx"
#include "event/net/TemplateServerSocket.hxx"
#include "fb_pool.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "io/UniqueFileDescriptor.hxx"
#include "util/Cancellable.hxx"
#include "util/PrintException.hxx"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Instance;

enum class Mode {
    MODE_NULL,
    MIRROR,

    /**
     * Response body of unknown length with keep-alive disabled.
     * Response body ends when socket is closed.
     */
    CLOSE,

    DUMMY,
    FIXED,
    HUGE_,
    HOLD,
};

class Connection final
    : public boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::auto_unlink>>,
    HttpServerConnectionHandler, Cancellable
{
    Instance &instance;

    PoolPtr pool;

    HttpServerConnection *connection;

    UnusedHoldIstreamPtr request_body;

    TimerEvent timer;

public:
    Connection(Instance &instance,
               UniqueSocketDescriptor &&_fd, SocketAddress address) noexcept;

    ~Connection() noexcept {
        if (connection != nullptr)
            http_server_connection_close(connection);
    }

private:
    void OnTimer() noexcept;

    /* virtual methods from class Cancellable */
    void Cancel() noexcept override {
        request_body.Clear();
        timer.Cancel();
    }

    /* virtual methods from class HttpServerConnectionHandler */
    void HandleHttpRequest(HttpServerRequest &request,
                           CancellablePointer &cancel_ptr) noexcept override;

    void LogHttpRequest(HttpServerRequest &,
                        http_status_t, int64_t,
                        uint64_t, uint64_t) noexcept override {}

    void HttpConnectionError(std::exception_ptr e) noexcept override;
    void HttpConnectionClosed() noexcept override;
};

using Listener = TemplateServerSocket<Connection, Instance &>;

struct Instance final : PInstance {
    ShutdownListener shutdown_listener;

    Mode mode;

    std::unique_ptr<Listener> listener;

    Instance()
        :shutdown_listener(event_loop, BIND_THIS_METHOD(ShutdownCallback)) {}

    void OnConnectionClosed() noexcept;

private:
    void ShutdownCallback() noexcept;
};

Connection::Connection(Instance &_instance,
                       UniqueSocketDescriptor &&fd,
                       SocketAddress address) noexcept
    :instance(_instance),
     pool(PoolPtr::Donate(),
          *pool_new_linear(instance.root_pool, "connection", 2048)),
     connection(http_server_connection_new(pool, instance.event_loop,
                                           std::move(fd), FdType::FD_SOCKET,
                                           nullptr,
                                           nullptr,
                                           address,
                                           true,
                                           *this)),
     timer(instance.event_loop, BIND_THIS_METHOD(OnTimer)) {}

void
Instance::ShutdownCallback() noexcept
{
    listener.reset();
}

void
Connection::OnTimer() noexcept
{
    instance.OnConnectionClosed();
    delete this;
}

void
Instance::OnConnectionClosed() noexcept
{
    if (!listener)
        shutdown_listener.Disable();
}

/*
 * http_server handler
 *
 */

void
Connection::HandleHttpRequest(HttpServerRequest &request,
                              gcc_unused CancellablePointer &cancel_ptr) noexcept
{
    switch (instance.mode) {
        http_status_t status;
        static char data[0x100];

    case Mode::MODE_NULL:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        http_server_response(&request, HTTP_STATUS_NO_CONTENT,
                             HttpHeaders(request.pool), nullptr);
        break;

    case Mode::MIRROR:
        status = request.body
            ? HTTP_STATUS_OK
            : HTTP_STATUS_NO_CONTENT;
        http_server_response(&request, status,
                             HttpHeaders(request.pool),
                             std::move(request.body));
        break;

    case Mode::CLOSE:
        /* disable keep-alive */
        http_server_connection_graceful(&request.connection);

        /* fall through */

    case Mode::DUMMY:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        {
            auto body = istream_head_new(request.pool,
                                         istream_zero_new(request.pool),
                                         256, false);
            body = istream_byte_new(request.pool, std::move(body));

            http_server_response(&request, HTTP_STATUS_OK,
                                 HttpHeaders(request.pool),
                                 std::move(body));
        }

        break;

    case Mode::FIXED:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        http_server_response(&request, HTTP_STATUS_OK, HttpHeaders(request.pool),
                             istream_memory_new(request.pool,
                                                data, sizeof(data)));
        break;

    case Mode::HUGE_:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        http_server_response(&request, HTTP_STATUS_OK,
                             HttpHeaders(request.pool),
                             istream_head_new(request.pool,
                                              istream_zero_new(request.pool),
                                              512 * 1024, true));
        break;

    case Mode::HOLD:
        request_body = UnusedHoldIstreamPtr(request.pool,
                                            std::move(request.body));

        {
            auto delayed = istream_delayed_new(request.pool,
                                               instance.event_loop);
            delayed.second.cancel_ptr = *this;

            http_server_response(&request, HTTP_STATUS_OK,
                                 HttpHeaders(request.pool),
                                 std::move(delayed.first));
        }

        static constexpr struct timeval t{0,0};
        timer.Add(t);
        break;
    }
}

void
Connection::HttpConnectionError(std::exception_ptr e) noexcept
{
    PrintException(e);

    connection = nullptr;

    instance.OnConnectionClosed();
    delete this;
}

void
Connection::HttpConnectionClosed() noexcept
{
    connection = nullptr;
    instance.OnConnectionClosed();
    delete this;
}

/*
 * main
 *
 */

int
main(int argc, char **argv)
try {
    if (argc != 4) {
        fprintf(stderr, "Usage: %s INFD OUTFD {null|mirror|close|dummy|fixed|huge|hold}\n", argv[0]);
        return EXIT_FAILURE;
    }

    UniqueSocketDescriptor listen_fd;
    int in_fd = -1, out_fd = -1;

    if (strcmp(argv[1], "accept") == 0) {
        listen_fd = UniqueSocketDescriptor(atoi(argv[2]));
    } else {
        in_fd = atoi(argv[1]);
        out_fd = atoi(argv[2]);
    }

    direct_global_init();
    const ScopeFbPoolInit fb_pool_init;

    Instance instance;
    instance.shutdown_listener.Enable();

    const char *mode = argv[3];
    if (strcmp(mode, "null") == 0)
        instance.mode = Mode::MODE_NULL;
    else if (strcmp(mode, "mirror") == 0)
        instance.mode = Mode::MIRROR;
    else if (strcmp(mode, "close") == 0)
        instance.mode = Mode::CLOSE;
    else if (strcmp(mode, "dummy") == 0)
        instance.mode = Mode::DUMMY;
    else if (strcmp(mode, "fixed") == 0)
        instance.mode = Mode::FIXED;
    else if (strcmp(mode, "huge") == 0)
        instance.mode = Mode::HUGE_;
    else if (strcmp(mode, "hold") == 0)
        instance.mode = Mode::HOLD;
    else {
        fprintf(stderr, "Unknown mode: %s\n", mode);
        return EXIT_FAILURE;
    }

    if (listen_fd.IsDefined()) {
        instance.listener = std::make_unique<Listener>(instance.event_loop,
                                                       instance);
        instance.listener->Listen(std::move(listen_fd));
    } else {
        UniqueSocketDescriptor sockfd;
        if (in_fd != out_fd) {
            sockfd = duplex_new(instance.event_loop, instance.root_pool,
                                UniqueFileDescriptor(in_fd),
                                UniqueFileDescriptor(out_fd));
        } else
            sockfd = UniqueSocketDescriptor(in_fd);

        new Connection(instance, std::move(sockfd), {});
    }

    instance.event_loop.Dispatch();

    return EXIT_SUCCESS;
} catch (...) {
    PrintException(std::current_exception());
    return EXIT_FAILURE;
}
<commit_msg>test/run_http_server: use TimerEvent::Schedule()<commit_after>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * 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
 * FOUNDATION 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 "http_server/http_server.hxx"
#include "http_server/Request.hxx"
#include "http_server/Handler.hxx"
#include "http_headers.hxx"
#include "duplex.hxx"
#include "direct.hxx"
#include "istream/sink_null.hxx"
#include "istream/ByteIstream.hxx"
#include "istream/DelayedIstream.hxx"
#include "istream/HeadIstream.hxx"
#include "istream/istream_hold.hxx"
#include "istream/istream_memory.hxx"
#include "istream/ZeroIstream.hxx"
#include "istream/istream.hxx"
#include "istream/UnusedHoldPtr.hxx"
#include "PInstance.hxx"
#include "pool/pool.hxx"
#include "event/TimerEvent.hxx"
#include "event/ShutdownListener.hxx"
#include "event/net/TemplateServerSocket.hxx"
#include "fb_pool.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "io/UniqueFileDescriptor.hxx"
#include "util/Cancellable.hxx"
#include "util/PrintException.hxx"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Instance;

enum class Mode {
    MODE_NULL,
    MIRROR,

    /**
     * Response body of unknown length with keep-alive disabled.
     * Response body ends when socket is closed.
     */
    CLOSE,

    DUMMY,
    FIXED,
    HUGE_,
    HOLD,
};

class Connection final
    : public boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::auto_unlink>>,
    HttpServerConnectionHandler, Cancellable
{
    Instance &instance;

    PoolPtr pool;

    HttpServerConnection *connection;

    UnusedHoldIstreamPtr request_body;

    TimerEvent timer;

public:
    Connection(Instance &instance,
               UniqueSocketDescriptor &&_fd, SocketAddress address) noexcept;

    ~Connection() noexcept {
        if (connection != nullptr)
            http_server_connection_close(connection);
    }

private:
    void OnTimer() noexcept;

    /* virtual methods from class Cancellable */
    void Cancel() noexcept override {
        request_body.Clear();
        timer.Cancel();
    }

    /* virtual methods from class HttpServerConnectionHandler */
    void HandleHttpRequest(HttpServerRequest &request,
                           CancellablePointer &cancel_ptr) noexcept override;

    void LogHttpRequest(HttpServerRequest &,
                        http_status_t, int64_t,
                        uint64_t, uint64_t) noexcept override {}

    void HttpConnectionError(std::exception_ptr e) noexcept override;
    void HttpConnectionClosed() noexcept override;
};

using Listener = TemplateServerSocket<Connection, Instance &>;

struct Instance final : PInstance {
    ShutdownListener shutdown_listener;

    Mode mode;

    std::unique_ptr<Listener> listener;

    Instance()
        :shutdown_listener(event_loop, BIND_THIS_METHOD(ShutdownCallback)) {}

    void OnConnectionClosed() noexcept;

private:
    void ShutdownCallback() noexcept;
};

Connection::Connection(Instance &_instance,
                       UniqueSocketDescriptor &&fd,
                       SocketAddress address) noexcept
    :instance(_instance),
     pool(PoolPtr::Donate(),
          *pool_new_linear(instance.root_pool, "connection", 2048)),
     connection(http_server_connection_new(pool, instance.event_loop,
                                           std::move(fd), FdType::FD_SOCKET,
                                           nullptr,
                                           nullptr,
                                           address,
                                           true,
                                           *this)),
     timer(instance.event_loop, BIND_THIS_METHOD(OnTimer)) {}

void
Instance::ShutdownCallback() noexcept
{
    listener.reset();
}

void
Connection::OnTimer() noexcept
{
    instance.OnConnectionClosed();
    delete this;
}

void
Instance::OnConnectionClosed() noexcept
{
    if (!listener)
        shutdown_listener.Disable();
}

/*
 * http_server handler
 *
 */

void
Connection::HandleHttpRequest(HttpServerRequest &request,
                              gcc_unused CancellablePointer &cancel_ptr) noexcept
{
    switch (instance.mode) {
        http_status_t status;
        static char data[0x100];

    case Mode::MODE_NULL:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        http_server_response(&request, HTTP_STATUS_NO_CONTENT,
                             HttpHeaders(request.pool), nullptr);
        break;

    case Mode::MIRROR:
        status = request.body
            ? HTTP_STATUS_OK
            : HTTP_STATUS_NO_CONTENT;
        http_server_response(&request, status,
                             HttpHeaders(request.pool),
                             std::move(request.body));
        break;

    case Mode::CLOSE:
        /* disable keep-alive */
        http_server_connection_graceful(&request.connection);

        /* fall through */

    case Mode::DUMMY:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        {
            auto body = istream_head_new(request.pool,
                                         istream_zero_new(request.pool),
                                         256, false);
            body = istream_byte_new(request.pool, std::move(body));

            http_server_response(&request, HTTP_STATUS_OK,
                                 HttpHeaders(request.pool),
                                 std::move(body));
        }

        break;

    case Mode::FIXED:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        http_server_response(&request, HTTP_STATUS_OK, HttpHeaders(request.pool),
                             istream_memory_new(request.pool,
                                                data, sizeof(data)));
        break;

    case Mode::HUGE_:
        if (request.body)
            sink_null_new(request.pool, std::move(request.body));

        http_server_response(&request, HTTP_STATUS_OK,
                             HttpHeaders(request.pool),
                             istream_head_new(request.pool,
                                              istream_zero_new(request.pool),
                                              512 * 1024, true));
        break;

    case Mode::HOLD:
        request_body = UnusedHoldIstreamPtr(request.pool,
                                            std::move(request.body));

        {
            auto delayed = istream_delayed_new(request.pool,
                                               instance.event_loop);
            delayed.second.cancel_ptr = *this;

            http_server_response(&request, HTTP_STATUS_OK,
                                 HttpHeaders(request.pool),
                                 std::move(delayed.first));
        }

        timer.Schedule(std::chrono::seconds(0));
        break;
    }
}

void
Connection::HttpConnectionError(std::exception_ptr e) noexcept
{
    PrintException(e);

    connection = nullptr;

    instance.OnConnectionClosed();
    delete this;
}

void
Connection::HttpConnectionClosed() noexcept
{
    connection = nullptr;
    instance.OnConnectionClosed();
    delete this;
}

/*
 * main
 *
 */

int
main(int argc, char **argv)
try {
    if (argc != 4) {
        fprintf(stderr, "Usage: %s INFD OUTFD {null|mirror|close|dummy|fixed|huge|hold}\n", argv[0]);
        return EXIT_FAILURE;
    }

    UniqueSocketDescriptor listen_fd;
    int in_fd = -1, out_fd = -1;

    if (strcmp(argv[1], "accept") == 0) {
        listen_fd = UniqueSocketDescriptor(atoi(argv[2]));
    } else {
        in_fd = atoi(argv[1]);
        out_fd = atoi(argv[2]);
    }

    direct_global_init();
    const ScopeFbPoolInit fb_pool_init;

    Instance instance;
    instance.shutdown_listener.Enable();

    const char *mode = argv[3];
    if (strcmp(mode, "null") == 0)
        instance.mode = Mode::MODE_NULL;
    else if (strcmp(mode, "mirror") == 0)
        instance.mode = Mode::MIRROR;
    else if (strcmp(mode, "close") == 0)
        instance.mode = Mode::CLOSE;
    else if (strcmp(mode, "dummy") == 0)
        instance.mode = Mode::DUMMY;
    else if (strcmp(mode, "fixed") == 0)
        instance.mode = Mode::FIXED;
    else if (strcmp(mode, "huge") == 0)
        instance.mode = Mode::HUGE_;
    else if (strcmp(mode, "hold") == 0)
        instance.mode = Mode::HOLD;
    else {
        fprintf(stderr, "Unknown mode: %s\n", mode);
        return EXIT_FAILURE;
    }

    if (listen_fd.IsDefined()) {
        instance.listener = std::make_unique<Listener>(instance.event_loop,
                                                       instance);
        instance.listener->Listen(std::move(listen_fd));
    } else {
        UniqueSocketDescriptor sockfd;
        if (in_fd != out_fd) {
            sockfd = duplex_new(instance.event_loop, instance.root_pool,
                                UniqueFileDescriptor(in_fd),
                                UniqueFileDescriptor(out_fd));
        } else
            sockfd = UniqueSocketDescriptor(in_fd);

        new Connection(instance, std::move(sockfd), {});
    }

    instance.event_loop.Dispatch();

    return EXIT_SUCCESS;
} catch (...) {
    PrintException(std::current_exception());
    return EXIT_FAILURE;
}
<|endoftext|>


<commit_before>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the Licence is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the Licence for the
specific language governing permissions and limitations
under the Licence.

Author(s) / Copyright (s): Damon Hart-Davis 2014--2015
*/

/*
  Alternate POST / setup and loop / main for non-OpenTRV code running on OpenTRV h/w platform.

  DHD20151208: TEST CODE FOR REV7 PRODUCTION 2015Q4.
 */

#include "V0p2_Main.h"

#include "V0p2_Generic_Config.h"
#include "V0p2_Board_IO_Config.h" // I/O pin allocation: include ahead of I/O module headers.

// Arduino libraries.
//#include <Wire.h>
#ifdef ALLOW_CC1_SUPPORT
#include <OTProtocolCC.h>
#endif
#include <OTV0p2Base.h>
#include <OTRadioLink.h>

#include "Power_Management.h"
#include "RFM22_Radio.h"
#include "Serial_IO.h"
#include "V0p2_Sensors.h"

// Link in support for alternate Power On Self-Test and main loop if required.
#if defined(ALT_MAIN_LOOP)

// Mask for Port B input change interrupts.
#define MASK_PB_BASIC 0b00000000 // Nothing.
#ifdef PIN_RFM_NIRQ
  #if (PIN_RFM_NIRQ < 8) || (PIN_RFM_NIRQ > 15)
    #error PIN_RFM_NIRQ expected to be on port B
  #endif
  #define RFM23B_INT_MASK (1 << (PIN_RFM_NIRQ&7))
  #define MASK_PB (MASK_PB_BASIC | RFM23B_INT_MASK)
#else
  #define MASK_PB MASK_PB_BASIC
#endif

// Mask for Port D input change interrupts.
#define MASK_PD_BASIC 0b00000001 // Just RX.
#if defined(ENABLE_VOICE_SENSOR)
#if VOICE_NIRQ > 7
#error voice interrupt on wrong port
#endif
#define VOICE_INT_MASK (1 << (VOICE_NIRQ&7))
#define MASK_PD (MASK_PD_BASIC | VOICE_INT_MASK)
#else
#define MASK_PD MASK_PD_BASIC // Just RX.
#endif

static const OTRadioLink::OTRadioChannelConfig RFMConfig(OTRadValve::FHT8VRadValveBase::FHT8V_RFM23_Reg_Values, true, true, true);

// Called from startup() after some initial setup has been done.
// Can abort with panic() if need be.
void POSTalt()
  {

#if defined(USE_MODULE_RFM22RADIOSIMPLE)
  // Initialise the radio, if configured, ASAP because it can suck a lot of power until properly initialised.
  RFM23B.preinit(NULL);
  // Check that the radio is correctly connected; panic if not...
  if(!RFM23B.configure(1, &RFMConfig) || !RFM23B.begin()) { panic(F("PANIC!")); }
#endif

//  // Force initialisation into low-power state.
//  const int heat = TemperatureC16.read();
//#if 1 && defined(DEBUG)
//  DEBUG_SERIAL_PRINT_FLASHSTRING("temp: ");
//  DEBUG_SERIAL_PRINT(heat);
//  DEBUG_SERIAL_PRINTLN();
//#endif
//  const int light = AmbLight.read();
//#if 1 && defined(DEBUG)
//  DEBUG_SERIAL_PRINT_FLASHSTRING("light: ");
//  DEBUG_SERIAL_PRINT(light);
//  DEBUG_SERIAL_PRINTLN();
//#endif

  // Trailing setup for the run
  // --------------------------

  // Set up async edge interrupts.
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE)
    {
    //PCMSK0 = PB; PCINT  0--7    (LEARN1 and Radio)
    //PCMSK1 = PC; PCINT  8--15
    //PCMSK2 = PD; PCINT 16--24   (LEARN2 and MODE, RX)

    PCICR =
#if defined(MASK_PB) && (MASK_PB != 0) // If PB interrupts required.
        1 | // 0x1 enables PB/PCMSK0.
#endif
#if defined(MASK_PC) && (MASK_PC != 0) // If PC interrupts required.
        2 | // 0x2 enables PC/PCMSK1.
#endif
#if defined(MASK_PD) && (MASK_PD != 0) // If PD interrupts required.
        4 | // 0x4 enables PD/PCMSK2.
#endif
        0;

#if defined(MASK_PB) && (MASK_PB != 0) // If PB interrupts required.
    PCMSK0 = MASK_PB;
#endif
#if defined(MASK_PC) && (MASK_PC != 0) // If PC interrupts required.
    PCMSK1 = MASK_PC;
#endif
#if defined(MASK_PD) && (MASK_PD != 0) // If PD interrupts required.
    PCMSK2 = MASK_PD;
#endif
    }
  }

#if defined(ALT_MAIN_LOOP)

#if defined(MASK_PB) && (MASK_PB != 0) // If PB interrupts required.
//// Interrupt count.  Marked volatile so safe to read without a lock as is a single byte.
//static volatile uint8_t intCountPB;
// Previous state of port B pins to help detect changes.
static volatile uint8_t prevStatePB;
// Interrupt service routine for PB I/O port transition changes.
ISR(PCINT0_vect)
  {
//  ++intCountPB;
  const uint8_t pins = PINB;
  const uint8_t changes = pins ^ prevStatePB;
  prevStatePB = pins;

#if defined(RFM23B_INT_MASK)
  // RFM23B nIRQ falling edge is of interest.
  // Handler routine not required/expected to 'clear' this interrupt.
  // TODO: try to ensure that OTRFM23BLink.handleInterruptSimple() is inlineable to minimise ISR prologue/epilogue time and space.
  if((changes & RFM23B_INT_MASK) && !(pins & RFM23B_INT_MASK))
    { RFM23B.handleInterruptSimple(); }
#endif

  }
#endif

#if defined(MASK_PC) && (MASK_PC != 0) // If PC interrupts required.
// Previous state of port C pins to help detect changes.
static volatile uint8_t prevStatePC;
// Interrupt service routine for PC I/O port transition changes.
ISR(PCINT1_vect)
  {
//  const uint8_t pins = PINC;
//  const uint8_t changes = pins ^ prevStatePC;
//  prevStatePC = pins;
//
// ...
  }
#endif

#if defined(MASK_PD) && (MASK_PD != 0) // If PD interrupts required.
// Previous state of port D pins to help detect changes.
static volatile uint8_t prevStatePD;
// Interrupt service routine for PD I/O port transition changes (including RX).
ISR(PCINT2_vect)
  {

  const uint8_t pins = PIND;
  const uint8_t changes = pins ^ prevStatePD;
  prevStatePD = pins;

#if defined(ENABLE_VOICE_SENSOR)
  //  // Voice detection is a falling edge.
  //  // Handler routine not required/expected to 'clear' this interrupt.
  //  // FIXME: ensure that Voice.handleInterruptSimple() is inlineable to minimise ISR prologue/epilogue time and space.
    // Voice detection is a RISING edge.
    if((changes & VOICE_INT_MASK) && (pins & VOICE_INT_MASK)) {
      Voice.handleInterruptSimple();
    }
#endif // ENABLE_VOICE_SENSOR

//    // If an interrupt arrived from no other masked source then wake the CLI.
//    // The will ensure that the CLI is active, eg from RX activity,
//    // eg it is possible to wake the CLI subsystem with an extra CR or LF.
//    // It is OK to trigger this from other things such as button presses.
//    // FIXME: ensure that resetCLIActiveTimer() is inlineable to minimise ISR prologue/epilogue time and space.
//    if(!(changes & MASK_PD & ~1)) { resetCLIActiveTimer(); }
  }
#endif // defined(MASK_PD) && (MASK_PD != 0)

#endif // ALT_MAIN

// Called from loop().
void loopAlt()
  {
  // Sleep in low-power mode (waiting for interrupts) until seconds roll.
  // NOTE: sleep at the top of the loop to minimise timing jitter/delay from Arduino background activity after loop() returns.
  // DHD20130425: waking up from sleep and getting to start processing below this block may take >10ms.
#if 0 && defined(DEBUG)
  DEBUG_SERIAL_PRINTLN_FLASHSTRING("*E"); // End-of-cycle sleep.
#endif

#if !defined(MIN_ENERGY_BOOT)
  OTV0P2BASE::powerDownSerial(); // Ensure that serial I/O is off.
  // Power down most stuff (except radio for hub RX).
  minimisePowerWithoutSleep();
#endif
  static uint_fast8_t TIME_LSD; // Controller's notion of seconds within major cycle.
  uint_fast8_t newTLSD;
  while(TIME_LSD == (newTLSD = OTV0P2BASE::getSecondsLT()))
    {
    OTV0P2BASE::sleepUntilInt();
    }
  TIME_LSD = newTLSD;

#if 0 && defined(DEBUG)
  DEBUG_SERIAL_PRINTLN_FLASHSTRING("*S"); // Start-of-cycle wake.
#endif

  // START LOOP BODY
  // ===============

//  DEBUG_SERIAL_PRINTLN_FLASHSTRING("*");

//  // Power up serial for the loop body.
//  // May just want to turn it on in POSTalt() and leave it on...
//  const bool neededWaking = powerUpSerialIfDisabled();

  static bool soakTestMode;

  // Flash lights, read sensors.
  LED_HEATCALL_ON();
  LED_UI2_ON();
  const bool m = (LOW == fastDigitalRead(BUTTON_MODE_L));
  const bool l1 = (LOW == fastDigitalRead(BUTTON_LEARN_L));
  const bool l2 = (LOW == fastDigitalRead(BUTTON_LEARN2_L));
  if(m || l1 || l2)
    {
    DEBUG_SERIAL_PRINT_FLASHSTRING("button(s): ");
    DEBUG_SERIAL_PRINT(m ? 'm' : ' ');
    DEBUG_SERIAL_PRINT(l1 ? 'l' : ' ');
    DEBUG_SERIAL_PRINT(l2 ? '2' : ' ');
    DEBUG_SERIAL_PRINTLN();
    }
  // Toggle soak-test mode with mode button held down.
  if(m)
    {
    soakTestMode = !soakTestMode;
    DEBUG_SERIAL_PRINT_FLASHSTRING("soak test mode: ");
    DEBUG_SERIAL_PRINT(soakTestMode);
    DEBUG_SERIAL_PRINTLN();
    }
  // Open or close as directed by learn buttons if not soak-test mode; at most one can be true.
  const bool open = !soakTestMode && l1 && !l2;
  const bool close = !soakTestMode && !l1 && l2;
  if(open || close)
    {
    soakTestMode = !soakTestMode;
    DEBUG_SERIAL_PRINT_FLASHSTRING("manual valve open: ");
    DEBUG_SERIAL_PRINT(open);
    DEBUG_SERIAL_PRINTLN();
    }
  // If not in soak test keep both LEDs on for a while each cycle.
  if(!soakTestMode) { OTV0P2BASE::nap(WDTO_15MS); }
  // Secondary UI LED off first...
  LED_UI2_OFF();

  switch(TIME_LSD >> 1) // 30s cycle to reduce boredom!
    {
    case 1:
      {
      // All LEDs off before measuring ambient light...
      LED_HEATCALL_OFF();
      LED_UI2_OFF();
      const int light = AmbLight.read();
      DEBUG_SERIAL_PRINT_FLASHSTRING("light: ");
      DEBUG_SERIAL_PRINT(light);
      DEBUG_SERIAL_PRINTLN();
      break;
      }

    case 2:
      {
      const int heat = TemperatureC16.read();
      DEBUG_SERIAL_PRINT_FLASHSTRING("temp: ");
      DEBUG_SERIAL_PRINT(heat);
      DEBUG_SERIAL_PRINTLN();
      break;
      }

    case 3:
      {
      const int rh = RelHumidity.read();
      DEBUG_SERIAL_PRINT_FLASHSTRING("RH%: ");
      DEBUG_SERIAL_PRINT(rh);
      DEBUG_SERIAL_PRINTLN();
      break;
      }

    case 4:
      {
      const int tp = TempPot.read();
      DEBUG_SERIAL_PRINT_FLASHSTRING("dial: ");
      DEBUG_SERIAL_PRINT(tp);
      DEBUG_SERIAL_PRINTLN();
      break;
      }
  }

  // Valve controller...
  static OTRadValve::NullHardwareMotorDriverInterfaceCallbackHandler ncbh;
  static OTRadValve::ValveMotorDirectV1HardwareDriver<MOTOR_DRIVE_ML, MOTOR_DRIVE_MR, MOTOR_DRIVE_MI_AIN> ValveDirect;

  // Manual open, manual close, soak test, or nothing...
  if(open)
    {
    ValveDirect.motorRun(128, OTRadValve::HardwareMotorDriverInterface::motorDriveOpening, ncbh);
    }
  else if(close)
    {
    ValveDirect.motorRun(128, OTRadValve::HardwareMotorDriverInterface::motorDriveClosing, ncbh);
    }
  else if(soakTestMode)
    {

    }
  // else leave the pin alone...

  // And ensure that the motor is off before the end of cycle.
  ValveDirect.motorRun(0, OTRadValve::HardwareMotorDriverInterface::motorOff, ncbh);

//  // Reading shaft encoder.
//  // Measure motor count against (fixed) internal reference.
//  power_intermittent_peripherals_enable(true);
//  const uint16_t mc = analogueNoiseReducedRead(MOTOR_DRIVE_MC_AIN, INTERNAL);
//  void power_intermittent_peripherals_disable();
//  DEBUG_SERIAL_PRINT_FLASHSTRING("Count input: ");
//  DEBUG_SERIAL_PRINT(mc);
//  DEBUG_SERIAL_PRINTLN();

  // In soak test keep the LED on for a decent while each minor cycle.
  if(soakTestMode)
    {
    while(OTV0P2BASE::getSubCycleTime() < 128) { }
    }
  // Main LED off...
  LED_HEATCALL_OFF();

//  // Force any pending output before return / possible UART power-down.
//  flushSerialSCTSensitive();
//  if(neededWaking) { OTV0P2BASE::powerDownSerial(); }
  }

#endif
<commit_msg>TODO-606: manual motor/pin drive<commit_after>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the Licence is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the Licence for the
specific language governing permissions and limitations
under the Licence.

Author(s) / Copyright (s): Damon Hart-Davis 2014--2015
*/

/*
  Alternate POST / setup and loop / main for non-OpenTRV code running on OpenTRV h/w platform.

  DHD20151208: TEST CODE FOR REV7 PRODUCTION 2015Q4.
 */

#include "V0p2_Main.h"

#include "V0p2_Generic_Config.h"
#include "V0p2_Board_IO_Config.h" // I/O pin allocation: include ahead of I/O module headers.

// Arduino libraries.
//#include <Wire.h>
#ifdef ALLOW_CC1_SUPPORT
#include <OTProtocolCC.h>
#endif
#include <OTV0p2Base.h>
#include <OTRadioLink.h>

#include "Power_Management.h"
#include "RFM22_Radio.h"
#include "Serial_IO.h"
#include "V0p2_Sensors.h"

// Link in support for alternate Power On Self-Test and main loop if required.
#if defined(ALT_MAIN_LOOP)

// Mask for Port B input change interrupts.
#define MASK_PB_BASIC 0b00000000 // Nothing.
#ifdef PIN_RFM_NIRQ
  #if (PIN_RFM_NIRQ < 8) || (PIN_RFM_NIRQ > 15)
    #error PIN_RFM_NIRQ expected to be on port B
  #endif
  #define RFM23B_INT_MASK (1 << (PIN_RFM_NIRQ&7))
  #define MASK_PB (MASK_PB_BASIC | RFM23B_INT_MASK)
#else
  #define MASK_PB MASK_PB_BASIC
#endif

// Mask for Port D input change interrupts.
#define MASK_PD_BASIC 0b00000001 // Just RX.
#if defined(ENABLE_VOICE_SENSOR)
#if VOICE_NIRQ > 7
#error voice interrupt on wrong port
#endif
#define VOICE_INT_MASK (1 << (VOICE_NIRQ&7))
#define MASK_PD (MASK_PD_BASIC | VOICE_INT_MASK)
#else
#define MASK_PD MASK_PD_BASIC // Just RX.
#endif

static const OTRadioLink::OTRadioChannelConfig RFMConfig(OTRadValve::FHT8VRadValveBase::FHT8V_RFM23_Reg_Values, true, true, true);

// Called from startup() after some initial setup has been done.
// Can abort with panic() if need be.
void POSTalt()
  {

#if defined(USE_MODULE_RFM22RADIOSIMPLE)
  // Initialise the radio, if configured, ASAP because it can suck a lot of power until properly initialised.
  RFM23B.preinit(NULL);
  // Check that the radio is correctly connected; panic if not...
  if(!RFM23B.configure(1, &RFMConfig) || !RFM23B.begin()) { panic(F("PANIC!")); }
#endif

//  // Force initialisation into low-power state.
//  const int heat = TemperatureC16.read();
//#if 1 && defined(DEBUG)
//  DEBUG_SERIAL_PRINT_FLASHSTRING("temp: ");
//  DEBUG_SERIAL_PRINT(heat);
//  DEBUG_SERIAL_PRINTLN();
//#endif
//  const int light = AmbLight.read();
//#if 1 && defined(DEBUG)
//  DEBUG_SERIAL_PRINT_FLASHSTRING("light: ");
//  DEBUG_SERIAL_PRINT(light);
//  DEBUG_SERIAL_PRINTLN();
//#endif

  // Trailing setup for the run
  // --------------------------

  // Set up async edge interrupts.
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE)
    {
    //PCMSK0 = PB; PCINT  0--7    (LEARN1 and Radio)
    //PCMSK1 = PC; PCINT  8--15
    //PCMSK2 = PD; PCINT 16--24   (LEARN2 and MODE, RX)

    PCICR =
#if defined(MASK_PB) && (MASK_PB != 0) // If PB interrupts required.
        1 | // 0x1 enables PB/PCMSK0.
#endif
#if defined(MASK_PC) && (MASK_PC != 0) // If PC interrupts required.
        2 | // 0x2 enables PC/PCMSK1.
#endif
#if defined(MASK_PD) && (MASK_PD != 0) // If PD interrupts required.
        4 | // 0x4 enables PD/PCMSK2.
#endif
        0;

#if defined(MASK_PB) && (MASK_PB != 0) // If PB interrupts required.
    PCMSK0 = MASK_PB;
#endif
#if defined(MASK_PC) && (MASK_PC != 0) // If PC interrupts required.
    PCMSK1 = MASK_PC;
#endif
#if defined(MASK_PD) && (MASK_PD != 0) // If PD interrupts required.
    PCMSK2 = MASK_PD;
#endif
    }
  }

#if defined(ALT_MAIN_LOOP)

#if defined(MASK_PB) && (MASK_PB != 0) // If PB interrupts required.
//// Interrupt count.  Marked volatile so safe to read without a lock as is a single byte.
//static volatile uint8_t intCountPB;
// Previous state of port B pins to help detect changes.
static volatile uint8_t prevStatePB;
// Interrupt service routine for PB I/O port transition changes.
ISR(PCINT0_vect)
  {
//  ++intCountPB;
  const uint8_t pins = PINB;
  const uint8_t changes = pins ^ prevStatePB;
  prevStatePB = pins;

#if defined(RFM23B_INT_MASK)
  // RFM23B nIRQ falling edge is of interest.
  // Handler routine not required/expected to 'clear' this interrupt.
  // TODO: try to ensure that OTRFM23BLink.handleInterruptSimple() is inlineable to minimise ISR prologue/epilogue time and space.
  if((changes & RFM23B_INT_MASK) && !(pins & RFM23B_INT_MASK))
    { RFM23B.handleInterruptSimple(); }
#endif

  }
#endif

#if defined(MASK_PC) && (MASK_PC != 0) // If PC interrupts required.
// Previous state of port C pins to help detect changes.
static volatile uint8_t prevStatePC;
// Interrupt service routine for PC I/O port transition changes.
ISR(PCINT1_vect)
  {
//  const uint8_t pins = PINC;
//  const uint8_t changes = pins ^ prevStatePC;
//  prevStatePC = pins;
//
// ...
  }
#endif

#if defined(MASK_PD) && (MASK_PD != 0) // If PD interrupts required.
// Previous state of port D pins to help detect changes.
static volatile uint8_t prevStatePD;
// Interrupt service routine for PD I/O port transition changes (including RX).
ISR(PCINT2_vect)
  {

  const uint8_t pins = PIND;
  const uint8_t changes = pins ^ prevStatePD;
  prevStatePD = pins;

#if defined(ENABLE_VOICE_SENSOR)
  //  // Voice detection is a falling edge.
  //  // Handler routine not required/expected to 'clear' this interrupt.
  //  // FIXME: ensure that Voice.handleInterruptSimple() is inlineable to minimise ISR prologue/epilogue time and space.
    // Voice detection is a RISING edge.
    if((changes & VOICE_INT_MASK) && (pins & VOICE_INT_MASK)) {
      Voice.handleInterruptSimple();
    }
#endif // ENABLE_VOICE_SENSOR

//    // If an interrupt arrived from no other masked source then wake the CLI.
//    // The will ensure that the CLI is active, eg from RX activity,
//    // eg it is possible to wake the CLI subsystem with an extra CR or LF.
//    // It is OK to trigger this from other things such as button presses.
//    // FIXME: ensure that resetCLIActiveTimer() is inlineable to minimise ISR prologue/epilogue time and space.
//    if(!(changes & MASK_PD & ~1)) { resetCLIActiveTimer(); }
  }
#endif // defined(MASK_PD) && (MASK_PD != 0)

#endif // ALT_MAIN

// Called from loop().
void loopAlt()
  {
  // Sleep in low-power mode (waiting for interrupts) until seconds roll.
  // NOTE: sleep at the top of the loop to minimise timing jitter/delay from Arduino background activity after loop() returns.
  // DHD20130425: waking up from sleep and getting to start processing below this block may take >10ms.
#if 0 && defined(DEBUG)
  DEBUG_SERIAL_PRINTLN_FLASHSTRING("*E"); // End-of-cycle sleep.
#endif

#if !defined(MIN_ENERGY_BOOT)
  OTV0P2BASE::powerDownSerial(); // Ensure that serial I/O is off.
  // Power down most stuff (except radio for hub RX).
  minimisePowerWithoutSleep();
#endif
  static uint_fast8_t TIME_LSD; // Controller's notion of seconds within major cycle.
  uint_fast8_t newTLSD;
  while(TIME_LSD == (newTLSD = OTV0P2BASE::getSecondsLT()))
    {
    OTV0P2BASE::sleepUntilInt();
    }
  TIME_LSD = newTLSD;

#if 0 && defined(DEBUG)
  DEBUG_SERIAL_PRINTLN_FLASHSTRING("*S"); // Start-of-cycle wake.
#endif

  // START LOOP BODY
  // ===============

//  DEBUG_SERIAL_PRINTLN_FLASHSTRING("*");

//  // Power up serial for the loop body.
//  // May just want to turn it on in POSTalt() and leave it on...
//  const bool neededWaking = powerUpSerialIfDisabled();

  static bool soakTestMode;

  // Flash lights, read sensors.
  LED_HEATCALL_ON();
  LED_UI2_ON();
  const bool m = (LOW == fastDigitalRead(BUTTON_MODE_L));
  const bool l1 = (LOW == fastDigitalRead(BUTTON_LEARN_L));
  const bool l2 = (LOW == fastDigitalRead(BUTTON_LEARN2_L));
  if(m || l1 || l2)
    {
    DEBUG_SERIAL_PRINT_FLASHSTRING("button(s): ");
    DEBUG_SERIAL_PRINT(m ? 'm' : ' ');
    DEBUG_SERIAL_PRINT(l1 ? 'l' : ' ');
    DEBUG_SERIAL_PRINT(l2 ? '2' : ' ');
    DEBUG_SERIAL_PRINTLN();
    }
  // Toggle soak-test mode with mode button held down.
  if(m)
    {
    soakTestMode = !soakTestMode;
    DEBUG_SERIAL_PRINT_FLASHSTRING("soak test mode: ");
    DEBUG_SERIAL_PRINT(soakTestMode);
    DEBUG_SERIAL_PRINTLN();
    }
  // Open or close as directed by learn buttons if not soak-test mode; at most one can be true.
  const bool open = !soakTestMode && l1 && !l2;
  const bool close = !soakTestMode && !l1 && l2;
  if(open || close)
    {
    DEBUG_SERIAL_PRINT_FLASHSTRING("manual valve open: ");
    DEBUG_SERIAL_PRINT(open);
    DEBUG_SERIAL_PRINTLN();
    }
  // If not in soak test keep both LEDs on for a while each cycle.
  if(!soakTestMode) { OTV0P2BASE::nap(WDTO_15MS); }
  // Secondary UI LED off first...
  LED_UI2_OFF();

  switch(TIME_LSD >> 1) // 30s cycle to reduce boredom!
    {
    case 1:
      {
      // All LEDs off before measuring ambient light...
      LED_HEATCALL_OFF();
      LED_UI2_OFF();
      const int light = AmbLight.read();
      DEBUG_SERIAL_PRINT_FLASHSTRING("light: ");
      DEBUG_SERIAL_PRINT(light);
      DEBUG_SERIAL_PRINTLN();
      break;
      }

    case 2:
      {
      const int heat = TemperatureC16.read();
      DEBUG_SERIAL_PRINT_FLASHSTRING("temp: ");
      DEBUG_SERIAL_PRINT(heat);
      DEBUG_SERIAL_PRINTLN();
      break;
      }

    case 3:
      {
      const int rh = RelHumidity.read();
      DEBUG_SERIAL_PRINT_FLASHSTRING("RH%: ");
      DEBUG_SERIAL_PRINT(rh);
      DEBUG_SERIAL_PRINTLN();
      break;
      }

    case 4:
      {
      const int tp = TempPot.read();
      DEBUG_SERIAL_PRINT_FLASHSTRING("dial: ");
      DEBUG_SERIAL_PRINT(tp);
      DEBUG_SERIAL_PRINTLN();
      break;
      }

    case 5:
      {
      const int mV = Supply_mV.read();
      DEBUG_SERIAL_PRINT_FLASHSTRING("battery mV: ");
      DEBUG_SERIAL_PRINT(mV);
      DEBUG_SERIAL_PRINTLN();
      break;
      }
  }

  // Valve controller...
  static OTRadValve::NullHardwareMotorDriverInterfaceCallbackHandler ncbh;
  static OTRadValve::ValveMotorDirectV1HardwareDriver<MOTOR_DRIVE_ML, MOTOR_DRIVE_MR, MOTOR_DRIVE_MI_AIN> ValveDirect;

  // Manual open, manual close, soak test, or nothing...
  if(open)
    {
    ValveDirect.motorRun(128, OTRadValve::HardwareMotorDriverInterface::motorDriveOpening, ncbh);
    }
  else if(close)
    {
    ValveDirect.motorRun(128, OTRadValve::HardwareMotorDriverInterface::motorDriveClosing, ncbh);
    }
  else if(soakTestMode)
    {

    }
  // else leave the pin alone...

  // And ensure that the motor is off before the end of cycle.
  ValveDirect.motorRun(0, OTRadValve::HardwareMotorDriverInterface::motorOff, ncbh);

//  // Reading shaft encoder.
//  // Measure motor count against (fixed) internal reference.
//  power_intermittent_peripherals_enable(true);
//  const uint16_t mc = analogueNoiseReducedRead(MOTOR_DRIVE_MC_AIN, INTERNAL);
//  void power_intermittent_peripherals_disable();
//  DEBUG_SERIAL_PRINT_FLASHSTRING("Count input: ");
//  DEBUG_SERIAL_PRINT(mc);
//  DEBUG_SERIAL_PRINTLN();

  // In soak test keep the LED on for a decent while each minor cycle.
  if(soakTestMode)
    {
    while(OTV0P2BASE::getSubCycleTime() < 128) { }
    }
  // Main LED off...
  LED_HEATCALL_OFF();

//  // Force any pending output before return / possible UART power-down.
//  flushSerialSCTSensitive();
//  if(neededWaking) { OTV0P2BASE::powerDownSerial(); }
  }

#endif
<|endoftext|>


<commit_before><commit_msg>CXX, Objects.hxx: Python 3.9 Unicode compilation warning fixes suggested by wmayer on the FC forum.<commit_after><|endoftext|>


<commit_before>/*
 * bacteria-core, core for cellular automaton
 * Copyright (C) 2016 Pavel Dolgov
 *
 * See the LICENSE file for terms of use.
 */

#include <boost/foreach.hpp>
#include <boost/test/unit_test.hpp>

#include "Model.hpp"

typedef int (Implementation::Model::*IntOneArgMethod) (
    const Abstract::Point& coordinates
) const;

typedef void (Implementation::Model::*OneArgMethod) (
    const Abstract::Point& coordinates
);

typedef void (Implementation::Model::*TwoArgsMethod) (
    const Abstract::Point& coordinates,
    int change
);

typedef void (Implementation::Model::*MultiArgsMethod) (
    const Abstract::Point& coordinates,
    int mass,
    int direction,
    int team,
    int instruction
);

template<typename Func>
static void checkOneArgMethod(
    Implementation::Model* model,
    Func model_method,
    int arg1,
    int arg2
) {
    Abstract::Point coordinates(arg1, arg2);
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(coordinates), Exception
    );
}

template<typename Func>
void checkModelMethodForThrow(
    Implementation::Model* model,
    Func model_method,
    int arg1,
    int arg2
) {
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(arg1, arg2), Exception
    );
}

template<>
void checkModelMethodForThrow<IntOneArgMethod>(
    Implementation::Model* model,
    IntOneArgMethod model_method,
    int arg1,
    int arg2
) {
    checkOneArgMethod(model, model_method, arg1, arg2);
}

template<>
void checkModelMethodForThrow<OneArgMethod>(
    Implementation::Model* model,
    OneArgMethod model_method,
    int arg1,
    int arg2
) {
    checkOneArgMethod(model, model_method, arg1, arg2);
}

template<>
void checkModelMethodForThrow<TwoArgsMethod>(
    Implementation::Model* model,
    TwoArgsMethod model_method,
    int arg1,
    int arg2
) {
    Abstract::Point coordinates(arg1, arg2);
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(coordinates, 0), Exception
    );
}

template<>
void checkModelMethodForThrow<MultiArgsMethod>(
    Implementation::Model* model,
    MultiArgsMethod model_method,
    int arg1,
    int arg2
) {
    Abstract::Point coordinates(arg1, arg2);
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(
            coordinates,
            DEFAULT_MASS,
            0,
            0,
            0
        ),
        Exception
    );
}

// "dead" test: attempt to do something with dead bacterium
template<typename Func>
static void deadTest(
    Implementation::Model* model,
    Func model_method
) {
    model->kill(0, 0);
    checkModelMethodForThrow(
        model,
        model_method,
        0,
        0
    );
}

template<typename Func>
static void checkErrorHandling(
    Implementation::Model* model,
    Func model_method,
    bool dead_test
) {
    // Range errors: test all combinations of
    // "wrong" (outside of correct range) arguments.
    // This solution works for coordinates and non-coordinates
    // methods of Model. (< 0, = 0, > MAX)
    int wrong_args[] = {-1, 0, MIN_WIDTH};
    BOOST_FOREACH (int arg1, wrong_args) {
        BOOST_FOREACH (int arg2, wrong_args) {
            if ((arg1 != 0) || (arg2 != 0)) {
                // (0, 0) is correct
                checkModelMethodForThrow(
                    model,
                    model_method,
                    arg1,
                    arg2
                );
            }
        }
    }
    if (dead_test) {
        // "dead" error
        // (attempt to do something with dead bacterium)
        deadTest(model, model_method);
    }
}

static Abstract::Point createInBaseCoordinates(
    Implementation::Model* model
) {
    Abstract::Point coordinates(0, 0);
    model->createNewByCoordinates(
        coordinates,
        DEFAULT_MASS,
        0,
        0,
        0
    );
    return coordinates;
}

static Implementation::Model* createBaseModel(
    int bacteria = 0,
    int teams = 1
) {
    Implementation::Model* model =
        Abstract::makeModel<Implementation::Model>(
            MIN_WIDTH,
            MIN_HEIGHT,
            bacteria,
            teams
        );
    return model;
}

BOOST_AUTO_TEST_CASE (get_team_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    int team = model->getTeamByCoordinates(coordinates);
    BOOST_REQUIRE(team == 0);
    checkErrorHandling<IntOneArgMethod>(
        model,
        &Implementation::Model::getTeamByCoordinates,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (width_test) {
    Implementation::Model* model = createBaseModel();
    BOOST_REQUIRE(model->getWidth() == MIN_WIDTH);
    delete model;
}

BOOST_AUTO_TEST_CASE (height_test) {
    Implementation::Model* model = createBaseModel();
    BOOST_REQUIRE(model->getHeight() == MIN_HEIGHT);
    delete model;
}

BOOST_AUTO_TEST_CASE (bacteria_number_test) {
    Implementation::Model* model = createBaseModel();
    int bacteria_number = model->getBacteriaNumber(0);
    BOOST_REQUIRE(bacteria_number == 0);
    createInBaseCoordinates(model);
    bacteria_number = model->getBacteriaNumber(0);
    BOOST_REQUIRE(bacteria_number == 1);
    // range errors
    BOOST_REQUIRE_THROW(model->getBacteriaNumber(-1), Exception);
    BOOST_REQUIRE_THROW(model->getBacteriaNumber(1), Exception);
    delete model;
}

BOOST_AUTO_TEST_CASE (is_alive_test) {
    Implementation::Model* model = createBaseModel(1, 1);
    checkErrorHandling(
        model,
        &Implementation::Model::isAlive,
        false
    );
    BOOST_REQUIRE(model->isAlive(0, 0) == true);
    model->kill(0, 0);
    BOOST_REQUIRE(model->isAlive(0, 0) == false);
    delete model;
}

BOOST_AUTO_TEST_CASE (get_instruction_test) {
    Implementation::Model* model = createBaseModel(1, 1);
    int instruction = model->getInstruction(0, 0);
    BOOST_REQUIRE(instruction == 0);
    checkErrorHandling(
        model,
        &Implementation::Model::getInstruction,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (get_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    Abstract::Point derived_coordinates = model->getCoordinates(0, 0);
    BOOST_REQUIRE(derived_coordinates == coordinates);
    checkErrorHandling(
        model,
        &Implementation::Model::getCoordinates,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (get_direction_test) {
    Implementation::Model* model = createBaseModel();
    createInBaseCoordinates(model);
    int direction = model->getDirection(0, 0);
    BOOST_REQUIRE(direction == Abstract::LEFT);
    checkErrorHandling(
        model,
        &Implementation::Model::getDirection,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (get_mass_test) {
    Implementation::Model* model = createBaseModel(1, 1);
    int mass = model->getMass(0, 0);
    BOOST_REQUIRE(mass == DEFAULT_MASS);
    checkErrorHandling(
        model,
        &Implementation::Model::getMass,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (kill_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    model->kill(0, 0);
    Abstract::CellState state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::EMPTY);
    BOOST_REQUIRE(model->isAlive(0, 0) == false);
    // error handling checks
    createInBaseCoordinates(model);
    model->clearBeforeMove(0);
    checkErrorHandling(
        model,
        &Implementation::Model::kill,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (kill_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    model->killByCoordinates(coordinates);
    Abstract::CellState state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::EMPTY);
    BOOST_REQUIRE(model->isAlive(0, 0) == false);
    // error handling checks
    createInBaseCoordinates(model);
    model->clearBeforeMove(0);
    checkErrorHandling<OneArgMethod>(
        model,
        &Implementation::Model::killByCoordinates,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (change_mass_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    int test_val = 1;
    model->changeMassByCoordinates(coordinates, test_val);
    int new_mass = model->getMassByCoordinates(coordinates);
    BOOST_REQUIRE(new_mass == (DEFAULT_MASS + test_val));
    model->changeMassByCoordinates(coordinates, -test_val);
    new_mass = model->getMassByCoordinates(coordinates);
    BOOST_REQUIRE(new_mass == DEFAULT_MASS);
    checkErrorHandling<TwoArgsMethod>(
        model,
        &Implementation::Model::changeMassByCoordinates,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (create_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    Abstract::CellState state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::BACTERIUM);
    checkErrorHandling<MultiArgsMethod>(
        model,
        &Implementation::Model::createNewByCoordinates,
        false
    );
    delete model;
}
<commit_msg>Model tests: add typedef OneArgMethod2<commit_after>/*
 * bacteria-core, core for cellular automaton
 * Copyright (C) 2016 Pavel Dolgov
 *
 * See the LICENSE file for terms of use.
 */

#include <boost/foreach.hpp>
#include <boost/test/unit_test.hpp>

#include "Model.hpp"

typedef int (Implementation::Model::*IntOneArgMethod) (
    const Abstract::Point& coordinates
) const;

typedef Abstract::CellState (Implementation::Model::*OneArgMethod2) (
    const Abstract::Point& coordinates
) const;

typedef void (Implementation::Model::*OneArgMethod) (
    const Abstract::Point& coordinates
);

typedef void (Implementation::Model::*TwoArgsMethod) (
    const Abstract::Point& coordinates,
    int change
);

typedef void (Implementation::Model::*MultiArgsMethod) (
    const Abstract::Point& coordinates,
    int mass,
    int direction,
    int team,
    int instruction
);

template<typename Func>
static void checkOneArgMethod(
    Implementation::Model* model,
    Func model_method,
    int arg1,
    int arg2
) {
    Abstract::Point coordinates(arg1, arg2);
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(coordinates), Exception
    );
}

template<typename Func>
void checkModelMethodForThrow(
    Implementation::Model* model,
    Func model_method,
    int arg1,
    int arg2
) {
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(arg1, arg2), Exception
    );
}

template<>
void checkModelMethodForThrow<IntOneArgMethod>(
    Implementation::Model* model,
    IntOneArgMethod model_method,
    int arg1,
    int arg2
) {
    checkOneArgMethod(model, model_method, arg1, arg2);
}

template<>
void checkModelMethodForThrow<OneArgMethod>(
    Implementation::Model* model,
    OneArgMethod model_method,
    int arg1,
    int arg2
) {
    checkOneArgMethod(model, model_method, arg1, arg2);
}

template<>
void checkModelMethodForThrow<TwoArgsMethod>(
    Implementation::Model* model,
    TwoArgsMethod model_method,
    int arg1,
    int arg2
) {
    Abstract::Point coordinates(arg1, arg2);
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(coordinates, 0), Exception
    );
}

template<>
void checkModelMethodForThrow<MultiArgsMethod>(
    Implementation::Model* model,
    MultiArgsMethod model_method,
    int arg1,
    int arg2
) {
    Abstract::Point coordinates(arg1, arg2);
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(
            coordinates,
            DEFAULT_MASS,
            0,
            0,
            0
        ),
        Exception
    );
}

// "dead" test: attempt to do something with dead bacterium
template<typename Func>
static void deadTest(
    Implementation::Model* model,
    Func model_method
) {
    model->kill(0, 0);
    checkModelMethodForThrow(
        model,
        model_method,
        0,
        0
    );
}

template<typename Func>
static void checkErrorHandling(
    Implementation::Model* model,
    Func model_method,
    bool dead_test
) {
    // Range errors: test all combinations of
    // "wrong" (outside of correct range) arguments.
    // This solution works for coordinates and non-coordinates
    // methods of Model. (< 0, = 0, > MAX)
    int wrong_args[] = {-1, 0, MIN_WIDTH};
    BOOST_FOREACH (int arg1, wrong_args) {
        BOOST_FOREACH (int arg2, wrong_args) {
            if ((arg1 != 0) || (arg2 != 0)) {
                // (0, 0) is correct
                checkModelMethodForThrow(
                    model,
                    model_method,
                    arg1,
                    arg2
                );
            }
        }
    }
    if (dead_test) {
        // "dead" error
        // (attempt to do something with dead bacterium)
        deadTest(model, model_method);
    }
}

static Abstract::Point createInBaseCoordinates(
    Implementation::Model* model
) {
    Abstract::Point coordinates(0, 0);
    model->createNewByCoordinates(
        coordinates,
        DEFAULT_MASS,
        0,
        0,
        0
    );
    return coordinates;
}

static Implementation::Model* createBaseModel(
    int bacteria = 0,
    int teams = 1
) {
    Implementation::Model* model =
        Abstract::makeModel<Implementation::Model>(
            MIN_WIDTH,
            MIN_HEIGHT,
            bacteria,
            teams
        );
    return model;
}

BOOST_AUTO_TEST_CASE (get_team_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    int team = model->getTeamByCoordinates(coordinates);
    BOOST_REQUIRE(team == 0);
    checkErrorHandling<IntOneArgMethod>(
        model,
        &Implementation::Model::getTeamByCoordinates,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (width_test) {
    Implementation::Model* model = createBaseModel();
    BOOST_REQUIRE(model->getWidth() == MIN_WIDTH);
    delete model;
}

BOOST_AUTO_TEST_CASE (height_test) {
    Implementation::Model* model = createBaseModel();
    BOOST_REQUIRE(model->getHeight() == MIN_HEIGHT);
    delete model;
}

BOOST_AUTO_TEST_CASE (bacteria_number_test) {
    Implementation::Model* model = createBaseModel();
    int bacteria_number = model->getBacteriaNumber(0);
    BOOST_REQUIRE(bacteria_number == 0);
    createInBaseCoordinates(model);
    bacteria_number = model->getBacteriaNumber(0);
    BOOST_REQUIRE(bacteria_number == 1);
    // range errors
    BOOST_REQUIRE_THROW(model->getBacteriaNumber(-1), Exception);
    BOOST_REQUIRE_THROW(model->getBacteriaNumber(1), Exception);
    delete model;
}

BOOST_AUTO_TEST_CASE (is_alive_test) {
    Implementation::Model* model = createBaseModel(1, 1);
    checkErrorHandling(
        model,
        &Implementation::Model::isAlive,
        false
    );
    BOOST_REQUIRE(model->isAlive(0, 0) == true);
    model->kill(0, 0);
    BOOST_REQUIRE(model->isAlive(0, 0) == false);
    delete model;
}

BOOST_AUTO_TEST_CASE (get_instruction_test) {
    Implementation::Model* model = createBaseModel(1, 1);
    int instruction = model->getInstruction(0, 0);
    BOOST_REQUIRE(instruction == 0);
    checkErrorHandling(
        model,
        &Implementation::Model::getInstruction,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (get_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    Abstract::Point derived_coordinates = model->getCoordinates(0, 0);
    BOOST_REQUIRE(derived_coordinates == coordinates);
    checkErrorHandling(
        model,
        &Implementation::Model::getCoordinates,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (get_direction_test) {
    Implementation::Model* model = createBaseModel();
    createInBaseCoordinates(model);
    int direction = model->getDirection(0, 0);
    BOOST_REQUIRE(direction == Abstract::LEFT);
    checkErrorHandling(
        model,
        &Implementation::Model::getDirection,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (get_mass_test) {
    Implementation::Model* model = createBaseModel(1, 1);
    int mass = model->getMass(0, 0);
    BOOST_REQUIRE(mass == DEFAULT_MASS);
    checkErrorHandling(
        model,
        &Implementation::Model::getMass,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (kill_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    model->kill(0, 0);
    Abstract::CellState state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::EMPTY);
    BOOST_REQUIRE(model->isAlive(0, 0) == false);
    // error handling checks
    createInBaseCoordinates(model);
    model->clearBeforeMove(0);
    checkErrorHandling(
        model,
        &Implementation::Model::kill,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (kill_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    model->killByCoordinates(coordinates);
    Abstract::CellState state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::EMPTY);
    BOOST_REQUIRE(model->isAlive(0, 0) == false);
    // error handling checks
    createInBaseCoordinates(model);
    model->clearBeforeMove(0);
    checkErrorHandling<OneArgMethod>(
        model,
        &Implementation::Model::killByCoordinates,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (change_mass_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    int test_val = 1;
    model->changeMassByCoordinates(coordinates, test_val);
    int new_mass = model->getMassByCoordinates(coordinates);
    BOOST_REQUIRE(new_mass == (DEFAULT_MASS + test_val));
    model->changeMassByCoordinates(coordinates, -test_val);
    new_mass = model->getMassByCoordinates(coordinates);
    BOOST_REQUIRE(new_mass == DEFAULT_MASS);
    checkErrorHandling<TwoArgsMethod>(
        model,
        &Implementation::Model::changeMassByCoordinates,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (create_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    Abstract::CellState state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::BACTERIUM);
    checkErrorHandling<MultiArgsMethod>(
        model,
        &Implementation::Model::createNewByCoordinates,
        false
    );
    delete model;
}
<|endoftext|>


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

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.

Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.

============================================================================*/

#include "QmitkVolumeVisualizationView.h"

#include <vtkSmartVolumeMapper.h>

#include <mitkImage.h>

#include <mitkTransferFunction.h>
#include <mitkTransferFunctionInitializer.h>
#include <mitkTransferFunctionProperty.h>

#include <mitkNodePredicateDataType.h>
#include <mitkNodePredicateDimension.h>
#include <mitkNodePredicateAnd.h>
#include <mitkNodePredicateNot.h>
#include <mitkNodePredicateOr.h>
#include <mitkNodePredicateProperty.h>

#include <mitkProperties.h>

const std::string QmitkVolumeVisualizationView::VIEW_ID = "org.mitk.views.volumevisualization";

enum
{
  DEFAULT_RENDERMODE = 0,
  RAYCAST_RENDERMODE = 1,
  GPU_RENDERMODE = 2
};

QmitkVolumeVisualizationView::QmitkVolumeVisualizationView()
  : QmitkAbstractView()
  , m_Controls(nullptr)
{
}

void QmitkVolumeVisualizationView::SetFocus()
{
}

void QmitkVolumeVisualizationView::CreateQtPartControl(QWidget* parent)
{
  m_Controls = new Ui::QmitkVolumeVisualizationViewControls;
  m_Controls->setupUi(parent);

  m_Controls->volumeSelectionWidget->SetDataStorage(GetDataStorage());
  m_Controls->volumeSelectionWidget->SetNodePredicate(mitk::NodePredicateAnd::New(
    mitk::TNodePredicateDataType<mitk::Image>::New(),
    mitk::NodePredicateOr::New(mitk::NodePredicateDimension::New(3), mitk::NodePredicateDimension::New(4)),
    mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))));
  m_Controls->volumeSelectionWidget->SetSelectionIsOptional(true);
  m_Controls->volumeSelectionWidget->SetAutoSelectNewNodes(true);
  m_Controls->volumeSelectionWidget->SetEmptyInfo(QString("Please select a 3D / 4D image volume"));
  m_Controls->volumeSelectionWidget->SetPopUpTitel(QString("Select image volume"));

  // Fill the transfer function presets in the generator widget
  std::vector<std::string> names;
  mitk::TransferFunctionInitializer::GetPresetNames(names);
  for (const auto& name : names)
  {
    m_Controls->transferFunctionGeneratorWidget->AddPreset(QString::fromStdString(name));
  }

  // see enum in vtkSmartVolumeMapper
  m_Controls->renderMode->addItem("Default");
  m_Controls->renderMode->addItem("RayCast");
  m_Controls->renderMode->addItem("GPU");

  // see vtkVolumeMapper::BlendModes
  m_Controls->blendMode->addItem("Comp");
  m_Controls->blendMode->addItem("Max");
  m_Controls->blendMode->addItem("Min");
  m_Controls->blendMode->addItem("Avg");
  m_Controls->blendMode->addItem("Add");

  connect(m_Controls->volumeSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged,
    this, &QmitkVolumeVisualizationView::OnCurrentSelectionChanged);
  connect(m_Controls->enableRenderingCB, SIGNAL(toggled(bool)), this, SLOT(OnEnableRendering(bool)));
  connect(m_Controls->renderMode, SIGNAL(activated(int)), this, SLOT(OnRenderMode(int)));
  connect(m_Controls->blendMode, SIGNAL(activated(int)), this, SLOT(OnBlendMode(int)));

  connect(m_Controls->transferFunctionGeneratorWidget, SIGNAL(SignalUpdateCanvas()),
          m_Controls->transferFunctionWidget, SLOT(OnUpdateCanvas()));
  connect(m_Controls->transferFunctionGeneratorWidget, SIGNAL(SignalTransferFunctionModeChanged(int)),
          SLOT(OnMitkInternalPreset(int)));

  m_Controls->enableRenderingCB->setEnabled(false);
  m_Controls->blendMode->setEnabled(false);
  m_Controls->renderMode->setEnabled(false);
  m_Controls->transferFunctionWidget->setEnabled(false);
  m_Controls->transferFunctionGeneratorWidget->setEnabled(false);

  m_Controls->volumeSelectionWidget->SetAutoSelectNewNodes(true);
}

void QmitkVolumeVisualizationView::OnMitkInternalPreset(int mode)
{
  if (m_SelectedNode.IsExpired())
  {
    return;
  }

  auto node = m_SelectedNode.Lock();
  mitk::TransferFunctionProperty::Pointer transferFuncProp;
  if (node->GetProperty(transferFuncProp, "TransferFunction"))
  {
    // first item is only information
    if (--mode == -1)
      return;

    // -- Creat new TransferFunction
    mitk::TransferFunctionInitializer::Pointer tfInit = mitk::TransferFunctionInitializer::New(transferFuncProp->GetValue());
    tfInit->SetTransferFunctionMode(mode);
    RequestRenderWindowUpdate();
    m_Controls->transferFunctionWidget->OnUpdateCanvas();
  }
}

void QmitkVolumeVisualizationView::OnCurrentSelectionChanged(QList<mitk::DataNode::Pointer> nodes)
{
  m_SelectedNode = nullptr;

  if (nodes.empty() || nodes.front().IsNull())
  {
    UpdateInterface();
    return;
  }

  auto selectedNode = nodes.front();
  auto image = dynamic_cast<mitk::Image*>(selectedNode->GetData());
  if (nullptr != image)
  {
    m_SelectedNode = selectedNode;
  }

  UpdateInterface();
}

void QmitkVolumeVisualizationView::OnEnableRendering(bool state)
{
  if (m_SelectedNode.IsExpired())
  {
    return;
  }

  m_SelectedNode.Lock()->SetProperty("volumerendering", mitk::BoolProperty::New(state));
  UpdateInterface();
  RequestRenderWindowUpdate();
}

void QmitkVolumeVisualizationView::OnRenderMode(int mode)
{
  if (m_SelectedNode.IsExpired())
  {
    return;
  }

  auto selectedNode = m_SelectedNode.Lock();

  bool usegpu = false;
  bool useray = false;
  if (DEFAULT_RENDERMODE == mode)
  {
    useray = true;
    usegpu = true;
  }
  else if (GPU_RENDERMODE == mode)
  {
    usegpu = true;
  }
  else if (RAYCAST_RENDERMODE == mode)
  {
    useray = true;
  }

  selectedNode->SetProperty("volumerendering.usegpu", mitk::BoolProperty::New(usegpu));
  selectedNode->SetProperty("volumerendering.useray", mitk::BoolProperty::New(useray));

  RequestRenderWindowUpdate();
}

void QmitkVolumeVisualizationView::OnBlendMode(int mode)
{
  if (m_SelectedNode.IsExpired())
  {
    return;
  }

  auto selectedNode = m_SelectedNode.Lock();

  bool usemip = false;
  if (vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND == mode)
  {
    usemip = true;
  }

  selectedNode->SetProperty("volumerendering.usemip", mitk::BoolProperty::New(usemip));
  selectedNode->SetProperty("volumerendering.blendmode", mitk::IntProperty::New(mode));

  RequestRenderWindowUpdate();
}

void QmitkVolumeVisualizationView::UpdateInterface()
{
  if (m_SelectedNode.IsExpired())
  {
    // turnoff all
    m_Controls->enableRenderingCB->setChecked(false);
    m_Controls->enableRenderingCB->setEnabled(false);

    m_Controls->blendMode->setCurrentIndex(0);
    m_Controls->blendMode->setEnabled(false);

    m_Controls->renderMode->setCurrentIndex(0);
    m_Controls->renderMode->setEnabled(false);

    m_Controls->transferFunctionWidget->SetDataNode(nullptr);
    m_Controls->transferFunctionWidget->setEnabled(false);

    m_Controls->transferFunctionGeneratorWidget->SetDataNode(nullptr);
    m_Controls->transferFunctionGeneratorWidget->setEnabled(false);
    return;
  }

  bool enabled = false;
  auto selectedNode = m_SelectedNode.Lock();

  selectedNode->GetBoolProperty("volumerendering", enabled);
  m_Controls->enableRenderingCB->setEnabled(true);
  m_Controls->enableRenderingCB->setChecked(enabled);

  if (!enabled)
  {
    // turnoff all except volumerendering checkbox
    m_Controls->blendMode->setCurrentIndex(0);
    m_Controls->blendMode->setEnabled(false);

    m_Controls->renderMode->setCurrentIndex(0);
    m_Controls->renderMode->setEnabled(false);

    m_Controls->transferFunctionWidget->SetDataNode(nullptr);
    m_Controls->transferFunctionWidget->setEnabled(false);

    m_Controls->transferFunctionGeneratorWidget->SetDataNode(nullptr);
    m_Controls->transferFunctionGeneratorWidget->setEnabled(false);
    return;
  }

  // otherwise we can activate em all
  m_Controls->blendMode->setEnabled(true);
  m_Controls->renderMode->setEnabled(true);

  // Determine Combo Box mode
  {
    bool usegpu = false;
    bool useray = false;
    bool usemip = false;
    selectedNode->GetBoolProperty("volumerendering.usegpu", usegpu);
    selectedNode->GetBoolProperty("volumerendering.useray", useray);
    selectedNode->GetBoolProperty("volumerendering.usemip", usemip);

    int blendMode;
    if (selectedNode->GetIntProperty("volumerendering.blendmode", blendMode))
      m_Controls->blendMode->setCurrentIndex(blendMode);

    if (usemip)
      m_Controls->blendMode->setCurrentIndex(vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND);

    int mode = DEFAULT_RENDERMODE;

    if (useray)
      mode = RAYCAST_RENDERMODE;
    else if (usegpu)
      mode = GPU_RENDERMODE;

    m_Controls->renderMode->setCurrentIndex(mode);
  }

  m_Controls->transferFunctionWidget->SetDataNode(selectedNode);
  m_Controls->transferFunctionWidget->setEnabled(true);
  m_Controls->transferFunctionGeneratorWidget->SetDataNode(selectedNode);
  m_Controls->transferFunctionGeneratorWidget->setEnabled(true);
}
<commit_msg>Fix connecting of OnCurrentSelectionChanged to signal<commit_after>/*============================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.

Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.

============================================================================*/

#include "QmitkVolumeVisualizationView.h"

#include <vtkSmartVolumeMapper.h>

#include <mitkImage.h>

#include <mitkTransferFunction.h>
#include <mitkTransferFunctionInitializer.h>
#include <mitkTransferFunctionProperty.h>

#include <mitkNodePredicateDataType.h>
#include <mitkNodePredicateDimension.h>
#include <mitkNodePredicateAnd.h>
#include <mitkNodePredicateNot.h>
#include <mitkNodePredicateOr.h>
#include <mitkNodePredicateProperty.h>

#include <mitkProperties.h>

const std::string QmitkVolumeVisualizationView::VIEW_ID = "org.mitk.views.volumevisualization";

enum
{
  DEFAULT_RENDERMODE = 0,
  RAYCAST_RENDERMODE = 1,
  GPU_RENDERMODE = 2
};

QmitkVolumeVisualizationView::QmitkVolumeVisualizationView()
  : QmitkAbstractView()
  , m_Controls(nullptr)
{
}

void QmitkVolumeVisualizationView::SetFocus()
{
  UpdateInterface();
}

void QmitkVolumeVisualizationView::CreateQtPartControl(QWidget* parent)
{
  m_Controls = new Ui::QmitkVolumeVisualizationViewControls;
  m_Controls->setupUi(parent);

  m_Controls->volumeSelectionWidget->SetDataStorage(GetDataStorage());
  m_Controls->volumeSelectionWidget->SetNodePredicate(mitk::NodePredicateAnd::New(
    mitk::TNodePredicateDataType<mitk::Image>::New(),
    mitk::NodePredicateOr::New(mitk::NodePredicateDimension::New(3), mitk::NodePredicateDimension::New(4)),
    mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))));
  m_Controls->volumeSelectionWidget->SetSelectionIsOptional(true);
  m_Controls->volumeSelectionWidget->SetEmptyInfo(QString("Please select a 3D / 4D image volume"));
  m_Controls->volumeSelectionWidget->SetPopUpTitel(QString("Select image volume"));

  // Fill the transfer function presets in the generator widget
  std::vector<std::string> names;
  mitk::TransferFunctionInitializer::GetPresetNames(names);
  for (const auto& name : names)
  {
    m_Controls->transferFunctionGeneratorWidget->AddPreset(QString::fromStdString(name));
  }

  // see enum in vtkSmartVolumeMapper
  m_Controls->renderMode->addItem("Default");
  m_Controls->renderMode->addItem("RayCast");
  m_Controls->renderMode->addItem("GPU");

  // see vtkVolumeMapper::BlendModes
  m_Controls->blendMode->addItem("Comp");
  m_Controls->blendMode->addItem("Max");
  m_Controls->blendMode->addItem("Min");
  m_Controls->blendMode->addItem("Avg");
  m_Controls->blendMode->addItem("Add");

  connect(m_Controls->volumeSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged,
    this, &QmitkVolumeVisualizationView::OnCurrentSelectionChanged);
  connect(m_Controls->enableRenderingCB, SIGNAL(toggled(bool)), this, SLOT(OnEnableRendering(bool)));
  connect(m_Controls->renderMode, SIGNAL(activated(int)), this, SLOT(OnRenderMode(int)));
  connect(m_Controls->blendMode, SIGNAL(activated(int)), this, SLOT(OnBlendMode(int)));

  connect(m_Controls->transferFunctionGeneratorWidget, SIGNAL(SignalUpdateCanvas()),
          m_Controls->transferFunctionWidget, SLOT(OnUpdateCanvas()));
  connect(m_Controls->transferFunctionGeneratorWidget, SIGNAL(SignalTransferFunctionModeChanged(int)),
          SLOT(OnMitkInternalPreset(int)));

  m_Controls->enableRenderingCB->setEnabled(false);
  m_Controls->blendMode->setEnabled(false);
  m_Controls->renderMode->setEnabled(false);
  m_Controls->transferFunctionWidget->setEnabled(false);
  m_Controls->transferFunctionGeneratorWidget->setEnabled(false);

  m_Controls->volumeSelectionWidget->SetAutoSelectNewNodes(true);
}

void QmitkVolumeVisualizationView::OnMitkInternalPreset(int mode)
{
  if (m_SelectedNode.IsExpired())
  {
    return;
  }

  auto node = m_SelectedNode.Lock();
  mitk::TransferFunctionProperty::Pointer transferFuncProp;
  if (node->GetProperty(transferFuncProp, "TransferFunction"))
  {
    // first item is only information
    if (--mode == -1)
      return;

    // -- Creat new TransferFunction
    mitk::TransferFunctionInitializer::Pointer tfInit = mitk::TransferFunctionInitializer::New(transferFuncProp->GetValue());
    tfInit->SetTransferFunctionMode(mode);
    RequestRenderWindowUpdate();
    m_Controls->transferFunctionWidget->OnUpdateCanvas();
  }
}

void QmitkVolumeVisualizationView::OnCurrentSelectionChanged(QList<mitk::DataNode::Pointer> nodes)
{
  m_SelectedNode = nullptr;

  if (nodes.empty() || nodes.front().IsNull())
  {
    UpdateInterface();
    return;
  }

  auto selectedNode = nodes.front();
  auto image = dynamic_cast<mitk::Image*>(selectedNode->GetData());
  if (nullptr != image)
  {
    m_SelectedNode = selectedNode;
  }

  UpdateInterface();
}

void QmitkVolumeVisualizationView::OnEnableRendering(bool state)
{
  if (m_SelectedNode.IsExpired())
  {
    return;
  }

  m_SelectedNode.Lock()->SetProperty("volumerendering", mitk::BoolProperty::New(state));
  UpdateInterface();
  RequestRenderWindowUpdate();
}

void QmitkVolumeVisualizationView::OnRenderMode(int mode)
{
  if (m_SelectedNode.IsExpired())
  {
    return;
  }

  auto selectedNode = m_SelectedNode.Lock();

  bool usegpu = false;
  bool useray = false;
  if (DEFAULT_RENDERMODE == mode)
  {
    useray = true;
    usegpu = true;
  }
  else if (GPU_RENDERMODE == mode)
  {
    usegpu = true;
  }
  else if (RAYCAST_RENDERMODE == mode)
  {
    useray = true;
  }

  selectedNode->SetProperty("volumerendering.usegpu", mitk::BoolProperty::New(usegpu));
  selectedNode->SetProperty("volumerendering.useray", mitk::BoolProperty::New(useray));

  RequestRenderWindowUpdate();
}

void QmitkVolumeVisualizationView::OnBlendMode(int mode)
{
  if (m_SelectedNode.IsExpired())
  {
    return;
  }

  auto selectedNode = m_SelectedNode.Lock();

  bool usemip = false;
  if (vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND == mode)
  {
    usemip = true;
  }

  selectedNode->SetProperty("volumerendering.usemip", mitk::BoolProperty::New(usemip));
  selectedNode->SetProperty("volumerendering.blendmode", mitk::IntProperty::New(mode));

  RequestRenderWindowUpdate();
}

void QmitkVolumeVisualizationView::UpdateInterface()
{
  if (m_SelectedNode.IsExpired())
  {
    // turnoff all
    m_Controls->enableRenderingCB->setChecked(false);
    m_Controls->enableRenderingCB->setEnabled(false);

    m_Controls->blendMode->setCurrentIndex(0);
    m_Controls->blendMode->setEnabled(false);

    m_Controls->renderMode->setCurrentIndex(0);
    m_Controls->renderMode->setEnabled(false);

    m_Controls->transferFunctionWidget->SetDataNode(nullptr);
    m_Controls->transferFunctionWidget->setEnabled(false);

    m_Controls->transferFunctionGeneratorWidget->SetDataNode(nullptr);
    m_Controls->transferFunctionGeneratorWidget->setEnabled(false);
    return;
  }

  bool enabled = false;
  auto selectedNode = m_SelectedNode.Lock();

  selectedNode->GetBoolProperty("volumerendering", enabled);
  m_Controls->enableRenderingCB->setEnabled(true);
  m_Controls->enableRenderingCB->setChecked(enabled);

  if (!enabled)
  {
    // turnoff all except volumerendering checkbox
    m_Controls->blendMode->setCurrentIndex(0);
    m_Controls->blendMode->setEnabled(false);

    m_Controls->renderMode->setCurrentIndex(0);
    m_Controls->renderMode->setEnabled(false);

    m_Controls->transferFunctionWidget->SetDataNode(nullptr);
    m_Controls->transferFunctionWidget->setEnabled(false);

    m_Controls->transferFunctionGeneratorWidget->SetDataNode(nullptr);
    m_Controls->transferFunctionGeneratorWidget->setEnabled(false);
    return;
  }

  // otherwise we can activate em all
  m_Controls->blendMode->setEnabled(true);
  m_Controls->renderMode->setEnabled(true);

  // Determine Combo Box mode
  {
    bool usegpu = false;
    bool useray = false;
    bool usemip = false;
    selectedNode->GetBoolProperty("volumerendering.usegpu", usegpu);
    selectedNode->GetBoolProperty("volumerendering.useray", useray);
    selectedNode->GetBoolProperty("volumerendering.usemip", usemip);

    int blendMode;
    if (selectedNode->GetIntProperty("volumerendering.blendmode", blendMode))
      m_Controls->blendMode->setCurrentIndex(blendMode);

    if (usemip)
      m_Controls->blendMode->setCurrentIndex(vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND);

    int mode = DEFAULT_RENDERMODE;

    if (useray)
      mode = RAYCAST_RENDERMODE;
    else if (usegpu)
      mode = GPU_RENDERMODE;

    m_Controls->renderMode->setCurrentIndex(mode);
  }

  m_Controls->transferFunctionWidget->SetDataNode(selectedNode);
  m_Controls->transferFunctionWidget->setEnabled(true);
  m_Controls->transferFunctionGeneratorWidget->SetDataNode(selectedNode);
  m_Controls->transferFunctionGeneratorWidget->setEnabled(true);
}
<|endoftext|>