<commit_before>#include <stdio.h>

#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h> // for MAXPATHLEN
#include <fcntl.h>

#include <zip.h>
#include <jansson.h>

#if ARCH_WIN
	#include <windows.h>
	#include <direct.h>
	#define mkdir(_dir, _perms) _mkdir(_dir)
#else
	#include <dlfcn.h>
#endif
#include <dirent.h>

#include "plugin.hpp"
#include "app.hpp"
#include "asset.hpp"
#include "util/request.hpp"

namespace rack {

std::list<Plugin*> gPlugins;
std::string gToken;

std::string gTagNames[NUM_TAGS] = {
	"Amplifier/VCA",
	"Attenuator",
	"Blank",
	"Clock",
	"Controller",
	"Delay",
	"Digital",
	"Distortion",
	"Drum",
	"Dual/Stereo",
	"Dynamics",
	"Effect",
	"Envelope Follower",
	"Envelope Generator",
	"Equalizer",
	"External",
	"Filter/VCF",
	"Function Generator",
	"Granular",
	"LFO",
	"Logic",
	"Low Pass Gate",
	"MIDI",
	"Mixer",
	"Multiple",
	"Noise",
	"Oscillator/VCO",
	"Panning",
	"Quad",
	"Quantizer",
	"Random",
	"Reverb",
	"Ring Modulator",
	"Sample and Hold",
	"Sampler",
	"Sequencer",
	"Slew Limiter",
	"Switch",
	"Synth Voice",
	"Tuner",
	"Utility",
	"Visual",
	"Waveshaper",
};

static bool isDownloading = false;
static float downloadProgress = 0.0;
static std::string downloadName;
static std::string loginStatus;

Plugin::~Plugin() {
	for (Model *model : models) {
		delete model;
	}
}

void Plugin::addModel(Model *model) {
	assert(!model->plugin);
	model->plugin = this;
	models.push_back(model);
}

static int loadPlugin(std::string path) {
	std::string libraryFilename;
#if ARCH_LIN
	libraryFilename = path + "/" + "plugin.so";
#elif ARCH_WIN
	libraryFilename = path + "/" + "plugin.dll";
#elif ARCH_MAC
	libraryFilename = path + "/" + "plugin.dylib";
#endif

	// Load dynamic/shared library
#if ARCH_WIN
	HINSTANCE handle = LoadLibrary(libraryFilename.c_str());
	if (!handle) {
		int error = GetLastError();
		warn("Failed to load library %s: %d", libraryFilename.c_str(), error);
		return -1;
	}
#elif ARCH_LIN || ARCH_MAC
	void *handle = dlopen(libraryFilename.c_str(), RTLD_NOW);
	if (!handle) {
		warn("Failed to load library %s: %s", libraryFilename.c_str(), dlerror());
		return -1;
	}
#endif

	// Call plugin's init() function
	typedef void (*InitCallback)(Plugin *);
	InitCallback initCallback;
#if ARCH_WIN
	initCallback = (InitCallback) GetProcAddress(handle, "init");
#elif ARCH_LIN || ARCH_MAC
	initCallback = (InitCallback) dlsym(handle, "init");
#endif
	if (!initCallback) {
		warn("Failed to read init() symbol in %s", libraryFilename.c_str());
		return -2;
	}

	// Construct and initialize Plugin instance
	Plugin *plugin = new Plugin();
	plugin->path = path;
	plugin->handle = handle;
	initCallback(plugin);

	// Add plugin to list
	gPlugins.push_back(plugin);
	info("Loaded plugin %s", libraryFilename.c_str());

	return 0;
}

static void loadPlugins(std::string path) {
	DIR *dir = opendir(path.c_str());
	if (dir) {
		struct dirent *d;
		while ((d = readdir(dir))) {
			if (d->d_name[0] == '.')
				continue;
			loadPlugin(path + "/" + d->d_name);
		}
		closedir(dir);
	}
}

////////////////////
// plugin helpers
////////////////////

static int extractZipHandle(zip_t *za, const char *dir) {
	int err = 0;
	for (int i = 0; i < zip_get_num_entries(za, 0); i++) {
		zip_stat_t zs;
		err = zip_stat_index(za, i, 0, &zs);
		if (err)
			return err;
		int nameLen = strlen(zs.name);

		char path[MAXPATHLEN];
		snprintf(path, sizeof(path), "%s/%s", dir, zs.name);

		if (zs.name[nameLen - 1] == '/') {
			err = mkdir(path, 0755);
			if (err && errno != EEXIST)
				return err;
		}
		else {
			zip_file_t *zf = zip_fopen_index(za, i, 0);
			if (!zf)
				return 1;

			FILE *outFile = fopen(path, "wb");
			if (!outFile)
				continue;

			while (1) {
				char buffer[4096];
				int len = zip_fread(zf, buffer, sizeof(buffer));
				if (len <= 0)
					break;
				fwrite(buffer, 1, len, outFile);
			}

			err = zip_fclose(zf);
			if (err)
				return err;
			fclose(outFile);
		}
	}
	return 0;
}

static int extractZip(const char *filename, const char *dir) {
	int err = 0;
	zip_t *za = zip_open(filename, 0, &err);
	if (!za)
		return 1;

	if (!err) {
		err = extractZipHandle(za, dir);
	}

	zip_close(za);
	return err;
}

static void refreshPurchase(json_t *pluginJ) {
	json_t *slugJ = json_object_get(pluginJ, "slug");
	if (!slugJ) return;
	const char *slug = json_string_value(slugJ);

	json_t *nameJ = json_object_get(pluginJ, "name");
	if (!nameJ) return;
	const char *name = json_string_value(nameJ);

	// Append token and version to download URL
	std::string url = gApiHost;
	url += "/download";
	url += "?product=";
	url += slug;
	url += "&version=";
	url += requestEscape(gApplicationVersion);
	url += "&token=";
	url += requestEscape(gToken);

	// If plugin is not loaded, download the zip file to /plugins
	downloadName = name;
	downloadProgress = 0.0;

	// Download zip
	std::string pluginsDir = assetLocal("plugins");
	std::string pluginPath = pluginsDir + "/" + slug;
	std::string zipPath = pluginPath + ".zip";
	bool success = requestDownload(url, zipPath, &downloadProgress);
	if (success) {
		// Unzip file
		int err = extractZip(zipPath.c_str(), pluginsDir.c_str());
		if (!err) {
			// Delete zip
			remove(zipPath.c_str());
			// Load plugin
			loadPlugin(pluginPath);
		}
	}

	downloadName = "";
}

////////////////////
// plugin API
////////////////////

void pluginInit() {
	// Load core
	// This function is defined in core.cpp
	Plugin *coreManufacturer = new Plugin();
	init(coreManufacturer);
	gPlugins.push_back(coreManufacturer);

	// Load plugins from global directory
	std::string globalPlugins = assetGlobal("plugins");
	info("Loading plugins from %s", globalPlugins.c_str());
	loadPlugins(globalPlugins);

	// Load plugins from local directory
	std::string localPlugins = assetLocal("plugins");
	if (globalPlugins != localPlugins) {
		mkdir(localPlugins.c_str(), 0755);
		info("Loading plugins from %s", localPlugins.c_str());
		loadPlugins(localPlugins);
	}
}

void pluginDestroy() {
	for (Plugin *plugin : gPlugins) {
		// Free library handle
#if ARCH_WIN
		if (plugin->handle)
			FreeLibrary((HINSTANCE)plugin->handle);
#elif ARCH_LIN || ARCH_MAC
		if (plugin->handle)
			dlclose(plugin->handle);
#endif

		// For some reason this segfaults.
		// It might be best to let them leak anyway, because "crash on exit" issues would occur with badly-written plugins.
		// delete plugin;
	}
	gPlugins.clear();
}

void pluginLogIn(std::string email, std::string password) {
	json_t *reqJ = json_object();
	json_object_set(reqJ, "email", json_string(email.c_str()));
	json_object_set(reqJ, "password", json_string(password.c_str()));
	json_t *resJ = requestJson(METHOD_POST, gApiHost + "/token", reqJ);
	json_decref(reqJ);

	if (resJ) {
		json_t *errorJ = json_object_get(resJ, "error");
		if (errorJ) {
			const char *errorStr = json_string_value(errorJ);
			loginStatus = errorStr;
		}
		else {
			json_t *tokenJ = json_object_get(resJ, "token");
			if (tokenJ) {
				const char *tokenStr = json_string_value(tokenJ);
				gToken = tokenStr;
				loginStatus = "";
			}
		}
		json_decref(resJ);
	}
}

void pluginLogOut() {
	gToken = "";
}

void pluginRefresh() {
	if (gToken.empty())
		return;

	isDownloading = true;
	downloadProgress = 0.0;
	downloadName = "";

	json_t *reqJ = json_object();
	json_object_set(reqJ, "token", json_string(gToken.c_str()));
	json_t *resJ = requestJson(METHOD_GET, gApiHost + "/purchases", reqJ);
	json_decref(reqJ);

	if (resJ) {
		json_t *errorJ = json_object_get(resJ, "error");
		if (errorJ) {
			const char *errorStr = json_string_value(errorJ);
			warn("Plugin refresh error: %s", errorStr);
		}
		else {
			json_t *purchasesJ = json_object_get(resJ, "purchases");
			size_t index;
			json_t *purchaseJ;
			json_array_foreach(purchasesJ, index, purchaseJ) {
				refreshPurchase(purchaseJ);
			}
		}
		json_decref(resJ);
	}

	isDownloading = false;
}

void pluginCancelDownload() {
	// TODO
}

bool pluginIsLoggedIn() {
	return gToken != "";
}

bool pluginIsDownloading() {
	return isDownloading;
}

float pluginGetDownloadProgress() {
	return downloadProgress;
}

std::string pluginGetDownloadName() {
	return downloadName;
}

std::string pluginGetLoginStatus() {
	return loginStatus;
}

} // namespace rack
<commit_msg>Refresh plugin only if the plugin slug and exact version is not found or does not match<commit_after>#include <stdio.h>

#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h> // for MAXPATHLEN
#include <fcntl.h>

#include <zip.h>
#include <jansson.h>

#if ARCH_WIN
	#include <windows.h>
	#include <direct.h>
	#define mkdir(_dir, _perms) _mkdir(_dir)
#else
	#include <dlfcn.h>
#endif
#include <dirent.h>

#include "plugin.hpp"
#include "app.hpp"
#include "asset.hpp"
#include "util/request.hpp"

namespace rack {

std::list<Plugin*> gPlugins;
std::string gToken;

std::string gTagNames[NUM_TAGS] = {
	"Amplifier/VCA",
	"Attenuator",
	"Blank",
	"Clock",
	"Controller",
	"Delay",
	"Digital",
	"Distortion",
	"Drum",
	"Dual/Stereo",
	"Dynamics",
	"Effect",
	"Envelope Follower",
	"Envelope Generator",
	"Equalizer",
	"External",
	"Filter/VCF",
	"Function Generator",
	"Granular",
	"LFO",
	"Logic",
	"Low Pass Gate",
	"MIDI",
	"Mixer",
	"Multiple",
	"Noise",
	"Oscillator/VCO",
	"Panning",
	"Quad",
	"Quantizer",
	"Random",
	"Reverb",
	"Ring Modulator",
	"Sample and Hold",
	"Sampler",
	"Sequencer",
	"Slew Limiter",
	"Switch",
	"Synth Voice",
	"Tuner",
	"Utility",
	"Visual",
	"Waveshaper",
};

static bool isDownloading = false;
static float downloadProgress = 0.0;
static std::string downloadName;
static std::string loginStatus;

Plugin::~Plugin() {
	for (Model *model : models) {
		delete model;
	}
}

void Plugin::addModel(Model *model) {
	assert(!model->plugin);
	model->plugin = this;
	models.push_back(model);
}

static int loadPlugin(std::string path) {
	std::string libraryFilename;
#if ARCH_LIN
	libraryFilename = path + "/" + "plugin.so";
#elif ARCH_WIN
	libraryFilename = path + "/" + "plugin.dll";
#elif ARCH_MAC
	libraryFilename = path + "/" + "plugin.dylib";
#endif

	// Load dynamic/shared library
#if ARCH_WIN
	HINSTANCE handle = LoadLibrary(libraryFilename.c_str());
	if (!handle) {
		int error = GetLastError();
		warn("Failed to load library %s: %d", libraryFilename.c_str(), error);
		return -1;
	}
#elif ARCH_LIN || ARCH_MAC
	void *handle = dlopen(libraryFilename.c_str(), RTLD_NOW);
	if (!handle) {
		warn("Failed to load library %s: %s", libraryFilename.c_str(), dlerror());
		return -1;
	}
#endif

	// Call plugin's init() function
	typedef void (*InitCallback)(Plugin *);
	InitCallback initCallback;
#if ARCH_WIN
	initCallback = (InitCallback) GetProcAddress(handle, "init");
#elif ARCH_LIN || ARCH_MAC
	initCallback = (InitCallback) dlsym(handle, "init");
#endif
	if (!initCallback) {
		warn("Failed to read init() symbol in %s", libraryFilename.c_str());
		return -2;
	}

	// Construct and initialize Plugin instance
	Plugin *plugin = new Plugin();
	plugin->path = path;
	plugin->handle = handle;
	initCallback(plugin);

	// Add plugin to list
	gPlugins.push_back(plugin);
	info("Loaded plugin %s", libraryFilename.c_str());

	return 0;
}

static void loadPlugins(std::string path) {
	DIR *dir = opendir(path.c_str());
	if (dir) {
		struct dirent *d;
		while ((d = readdir(dir))) {
			if (d->d_name[0] == '.')
				continue;
			loadPlugin(path + "/" + d->d_name);
		}
		closedir(dir);
	}
}

////////////////////
// plugin helpers
////////////////////

static int extractZipHandle(zip_t *za, const char *dir) {
	int err = 0;
	for (int i = 0; i < zip_get_num_entries(za, 0); i++) {
		zip_stat_t zs;
		err = zip_stat_index(za, i, 0, &zs);
		if (err)
			return err;
		int nameLen = strlen(zs.name);

		char path[MAXPATHLEN];
		snprintf(path, sizeof(path), "%s/%s", dir, zs.name);

		if (zs.name[nameLen - 1] == '/') {
			err = mkdir(path, 0755);
			if (err && errno != EEXIST)
				return err;
		}
		else {
			zip_file_t *zf = zip_fopen_index(za, i, 0);
			if (!zf)
				return 1;

			FILE *outFile = fopen(path, "wb");
			if (!outFile)
				continue;

			while (1) {
				char buffer[4096];
				int len = zip_fread(zf, buffer, sizeof(buffer));
				if (len <= 0)
					break;
				fwrite(buffer, 1, len, outFile);
			}

			err = zip_fclose(zf);
			if (err)
				return err;
			fclose(outFile);
		}
	}
	return 0;
}

static int extractZip(const char *filename, const char *dir) {
	int err = 0;
	zip_t *za = zip_open(filename, 0, &err);
	if (!za)
		return 1;

	if (!err) {
		err = extractZipHandle(za, dir);
	}

	zip_close(za);
	return err;
}

static void refreshPurchase(json_t *pluginJ) {
	json_t *slugJ = json_object_get(pluginJ, "slug");
	if (!slugJ) return;
	std::string slug = json_string_value(slugJ);

	json_t *nameJ = json_object_get(pluginJ, "name");
	if (!nameJ) return;
	std::string name = json_string_value(nameJ);

	json_t *versionJ = json_object_get(pluginJ, "version");
	if (!versionJ) return;
	std::string version = json_string_value(versionJ);

	// Check whether the plugin is already loaded
	for (Plugin *plugin : gPlugins) {
		if (plugin->slug == slug && plugin->version == version) {
			return;
		}
	}

	// Append token and version to download URL
	std::string url = gApiHost;
	url += "/download";
	url += "?product=";
	url += slug;
	url += "&version=";
	url += requestEscape(gApplicationVersion);
	url += "&token=";
	url += requestEscape(gToken);

	// If plugin is not loaded, download the zip file to /plugins
	downloadName = name;
	downloadProgress = 0.0;

	// Download zip
	std::string pluginsDir = assetLocal("plugins");
	std::string pluginPath = pluginsDir + "/" + slug;
	std::string zipPath = pluginPath + ".zip";
	bool success = requestDownload(url, zipPath, &downloadProgress);
	if (success) {
		// Unzip file
		int err = extractZip(zipPath.c_str(), pluginsDir.c_str());
		if (!err) {
			// Delete zip
			remove(zipPath.c_str());
			// Load plugin
			loadPlugin(pluginPath);
		}
	}

	downloadName = "";
}

////////////////////
// plugin API
////////////////////

void pluginInit() {
	// Load core
	// This function is defined in core.cpp
	Plugin *coreManufacturer = new Plugin();
	init(coreManufacturer);
	gPlugins.push_back(coreManufacturer);

	// Load plugins from global directory
	std::string globalPlugins = assetGlobal("plugins");
	info("Loading plugins from %s", globalPlugins.c_str());
	loadPlugins(globalPlugins);

	// Load plugins from local directory
	std::string localPlugins = assetLocal("plugins");
	if (globalPlugins != localPlugins) {
		mkdir(localPlugins.c_str(), 0755);
		info("Loading plugins from %s", localPlugins.c_str());
		loadPlugins(localPlugins);
	}
}

void pluginDestroy() {
	for (Plugin *plugin : gPlugins) {
		// Free library handle
#if ARCH_WIN
		if (plugin->handle)
			FreeLibrary((HINSTANCE)plugin->handle);
#elif ARCH_LIN || ARCH_MAC
		if (plugin->handle)
			dlclose(plugin->handle);
#endif

		// For some reason this segfaults.
		// It might be best to let them leak anyway, because "crash on exit" issues would occur with badly-written plugins.
		// delete plugin;
	}
	gPlugins.clear();
}

void pluginLogIn(std::string email, std::string password) {
	json_t *reqJ = json_object();
	json_object_set(reqJ, "email", json_string(email.c_str()));
	json_object_set(reqJ, "password", json_string(password.c_str()));
	json_t *resJ = requestJson(METHOD_POST, gApiHost + "/token", reqJ);
	json_decref(reqJ);

	if (resJ) {
		json_t *errorJ = json_object_get(resJ, "error");
		if (errorJ) {
			const char *errorStr = json_string_value(errorJ);
			loginStatus = errorStr;
		}
		else {
			json_t *tokenJ = json_object_get(resJ, "token");
			if (tokenJ) {
				const char *tokenStr = json_string_value(tokenJ);
				gToken = tokenStr;
				loginStatus = "";
			}
		}
		json_decref(resJ);
	}
}

void pluginLogOut() {
	gToken = "";
}

void pluginRefresh() {
	if (gToken.empty())
		return;

	isDownloading = true;
	downloadProgress = 0.0;
	downloadName = "";

	json_t *reqJ = json_object();
	json_object_set(reqJ, "token", json_string(gToken.c_str()));
	json_t *resJ = requestJson(METHOD_GET, gApiHost + "/purchases", reqJ);
	json_decref(reqJ);

	if (resJ) {
		json_t *errorJ = json_object_get(resJ, "error");
		if (errorJ) {
			const char *errorStr = json_string_value(errorJ);
			warn("Plugin refresh error: %s", errorStr);
		}
		else {
			json_t *purchasesJ = json_object_get(resJ, "purchases");
			size_t index;
			json_t *purchaseJ;
			json_array_foreach(purchasesJ, index, purchaseJ) {
				refreshPurchase(purchaseJ);
			}
		}
		json_decref(resJ);
	}

	isDownloading = false;
}

void pluginCancelDownload() {
	// TODO
}

bool pluginIsLoggedIn() {
	return gToken != "";
}

bool pluginIsDownloading() {
	return isDownloading;
}

float pluginGetDownloadProgress() {
	return downloadProgress;
}

std::string pluginGetDownloadName() {
	return downloadName;
}

std::string pluginGetLoginStatus() {
	return loginStatus;
}

} // namespace rack
<|endoftext|>


<commit_before>#pragma once
#include <set>
#include <tuple>
#include <vector>

#include "mtao/types.hpp"

namespace mtao::geometry::mesh {
// converts a collection of curves represented as pairs of indices in E into a
// collection of curves. By default it alwys generates closed curves but when
// closed_only=false it also can generate open and closed curves as long as
// vertices have valence <=2 This function returns a collection of <vector,bool>
// pairs where hte bool says whehter the returned curve is closed. if
// closed_only=true then the bool is always true
template <typename EDerived>
std::vector<std::tuple<std::vector<int>, bool>> edge_to_plcurves(
    const Eigen::MatrixBase<EDerived>& E, bool closed_only = true) {
    std::map<int, std::array<int, 2>> adj;
    for (int i = 0; i < E.cols(); ++i) {
        auto e = E.col(i);
        if (auto it = adj.find(e(0)); it == adj.end()) {
            adj[e(0)] = std::array<int, 2>{{i, -1}};
        } else {
            // it->second[1] = i;
            adj[e(0)][1] = i;
        }
        if (auto it = adj.find(e(1)); it == adj.end()) {
            adj[e(1)] = std::array<int, 2>{{i, -1}};
        } else {
            // it->second[0] = i;
            adj[e(1)][1] = i;
        }
    }

    // create an orientation by running around open edges and then random edges
    std::set<int> unseen_edges;
    std::set<int> open_edges;
    for (auto&& [k, v] : adj) {
        unseen_edges.emplace(k);
        if (v[0] < 0 || v[1] < 0) {
            open_edges.emplace(k);
        }
    }
    auto remove = [&](int idx) {
        if (auto it = open_edges.find(idx); it != open_edges.end()) {
            open_edges.erase(it);
        }
        if (auto it = unseen_edges.find(idx); it != unseen_edges.end()) {
            unseen_edges.erase(it);
        }
    };
    std::vector<std::tuple<std::vector<int>, bool>> ret;
    auto run = [&](const int start_vertex, const int start_edge) {
        auto& [vec, closedness] = ret.emplace_back();
        closedness = true;
        int vertex = start_vertex;
        int edge = start_edge;
        do {
            vec.emplace_back(vertex);
            auto&& e = E.col(edge);
            // step vertex
            if (e[0] == vertex) {
                vertex = e[1];
            } else {
                vertex = e[0];
            }
            remove(vertex);

            // step edge and swap it into the right order
            auto& de = adj[vertex];
            if (de[0] != edge) {
                std::swap(de[0], de[1]);
            }
            edge = de[1];
            // if we hit an end we can continue wrapping around or end,
            // depending on closed_only
            if (edge < 0) {
                closedness = false;
                if (closed_only) {
                    edge = de[0];
                } else {
                    break;
                }
            }

        } while (vertex != start_vertex);
    };
    while (!open_edges.empty()) {
        int start_idx = *open_edges.begin();
        remove(start_idx);
        int start_edge;
        auto&& e = adj[start_idx];
        if (e[0] == -1) {
            start_edge = e[1];
        } else {
            start_edge = e[0];
        }
        run(start_idx, start_edge);
    }
    while (!unseen_edges.empty()) {
        int start_idx = *unseen_edges.begin();
        remove(start_idx);
        auto&& e = adj[start_idx];
        int start_edge = e[0];

        run(start_idx, start_edge);
    }
    return ret;
}  // namespace mtao::geometry::mesh
}  // namespace mtao::geometry::mesh
<commit_msg>minor bump<commit_after>#pragma once
#include <set>
#include <tuple>
#include <vector>

#include "mtao/types.hpp"

namespace mtao::geometry::mesh {
// converts a collection of curves represented as pairs of indices in E into a
// collection of curves. By default it alwys generates closed curves but when
// closed_only=false it also can generate open and closed curves as long as
// vertices have valence <=2 This function returns a collection of <vector,bool>
// pairs where hte bool says whehter the returned curve is closed. if
// closed_only=true then the bool is always true
template <typename EDerived>
std::vector<std::tuple<std::vector<int>, bool>> edge_to_plcurves(
    const Eigen::MatrixBase<EDerived>& E, bool closed_only = true) {
    std::map<int, std::array<int, 2>> adj;
    for (int i = 0; i < E.cols(); ++i) {
        auto e = E.col(i);
        if (auto it = adj.find(e(0)); it == adj.end()) {
            adj[e(0)] = std::array<int, 2>{{i, -1}};
        } else {
            // it->second[1] = i;
            adj[e(0)][1] = i;
        }
        if (auto it = adj.find(e(1)); it == adj.end()) {
            adj[e(1)] = std::array<int, 2>{{i, -1}};
        } else {
            // it->second[0] = i;
            adj[e(1)][1] = i;
        }
    }

    // create an orientation by running around open edges and then random edges
    std::set<int> unseen_edges;
    std::set<int> open_edges;
    for (auto&& [k, v] : adj) {
        unseen_edges.emplace(k);
        if (v[0] < 0 || v[1] < 0) {
            open_edges.emplace(k);
        }
    }
    auto remove = [&](int idx) {
        if (auto it = open_edges.find(idx); it != open_edges.end()) {
            open_edges.erase(it);
        }
        if (auto it = unseen_edges.find(idx); it != unseen_edges.end()) {
            unseen_edges.erase(it);
        }
    };
    std::vector<std::tuple<std::vector<int>, bool>> ret;
    auto run = [&](const int start_vertex, const int start_edge) {
        auto& [vec, closedness] = ret.emplace_back();
        closedness = true;
        int vertex = start_vertex;
        int edge = start_edge;
        do {
            vec.emplace_back(vertex);
            auto&& e = E.col(edge);
            // step vertex
            if (e[0] == vertex) {
                vertex = e[1];
            } else {
                vertex = e[0];
            }
            remove(vertex);

            // step edge and swap it into the right order
            auto& de = adj[vertex];
            if (de[0] != edge) {
                std::swap(de[0], de[1]);
            }
            edge = de[1];
            // if we hit an end we can continue wrapping around or end,
            // depending on closed_only
            if (edge < 0) {
                closedness = false;
                if (closed_only) {
                    edge = de[0];
                } else {
                    break;
                }
            }

        } while (vertex != start_vertex);
        if(vec.size() == 2 && !closed_only) {
            closedness = false;
        }
    };
    while (!open_edges.empty()) {
        int start_idx = *open_edges.begin();
        remove(start_idx);
        int start_edge;
        auto&& e = adj[start_idx];
        if (e[0] == -1) {
            start_edge = e[1];
        } else {
            start_edge = e[0];
        }
        run(start_idx, start_edge);
    }
    while (!unseen_edges.empty()) {
        int start_idx = *unseen_edges.begin();
        remove(start_idx);
        auto&& e = adj[start_idx];
        int start_edge = e[0];

        run(start_idx, start_edge);
    }
    return ret;
}  // namespace mtao::geometry::mesh
}  // namespace mtao::geometry::mesh
<|endoftext|>


<commit_before>/*
 * teleop_pr2
 * Copyright (c) 2009, Willow Garage, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <ORGANIZATION> nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
 */

//\author: Blaise Gassend

#include <unistd.h>
#include <math.h>
#include <linux/joystick.h>
#include <fcntl.h>
#include <diagnostic_updater/diagnostic_updater.h>
#include "ros/ros.h"
#include "joy/Joy.h"

///\brief Opens, reads from and publishes joystick events
class Joystick
{
private:
  ros::NodeHandle nh_;
  bool open_;
  std::string joy_dev_;
  double deadzone_;
  double autorepeat_rate_;  // in Hz.  0 for no repeat.
  double coalesce_interval_; // Defaults to 100 Hz rate limit.
  int event_count_;
  int pub_count_;
  ros::Publisher pub_;
  double lastDiagTime_;

  diagnostic_updater::Updater diagnostic_;

  ///\brief Publishes diagnostics and status
  void diagnostics(diagnostic_updater::DiagnosticStatusWrapper& stat)
  {
    double now = ros::Time::now().toSec();
    double interval = now - lastDiagTime_;
    if (open_)
      stat.summary(0, "OK");
    else
      stat.summary(2, "Joystick not open.");

    stat.add("topic", pub_.getTopic());
    stat.add("device", joy_dev_);
    stat.add("dead zone", deadzone_);
    stat.add("autorepeat rate (Hz)", autorepeat_rate_);
    stat.add("coalesce interval (s)", coalesce_interval_);
    stat.add("recent joystick event rate (Hz)", event_count_ / interval);
    stat.add("recent publication rate (Hz)", pub_count_ / interval);
    stat.add("subscribers", pub_.getNumSubscribers());
    event_count_ = 0;
    pub_count_ = 0;
    lastDiagTime_ = now;
  }

public:
  Joystick() : nh_(), diagnostic_()
  {}

  ///\brief Opens joystick port, reads from port and publishes while node is active
  int main(int argc, char **argv)
  {
    diagnostic_.add("Joystick Driver Status", this, &Joystick::diagnostics);

    // Parameters
    ros::NodeHandle nh_param("~");
    pub_ = nh_.advertise<joy::Joy>("joy", 1);
    nh_param.param<std::string>("dev", joy_dev_, "/dev/input/js0");
    nh_param.param<double>("deadzone", deadzone_, 0.05);
    nh_param.param<double>("autorepeat_rate", autorepeat_rate_, 0);
    nh_param.param<double>("coalesce_interval", coalesce_interval_, 0.001);

    // Checks on parameters
    if (autorepeat_rate_ > 1 / coalesce_interval_)
      ROS_WARN("joy_node: autorepeat_rate (%f Hz) > 1/coalesce_interval (%f Hz) does not make sense. Timing behavior is not well defined.", autorepeat_rate_, 1/coalesce_interval_);

    if (deadzone_ >= 1)
    {
      ROS_WARN("joy_node: deadzone greater than 1 was requested. The semantics of deadzone have changed. It is now related to the range [-1:1] instead of [-32767:32767]. For now I am dividing your deadzone by 32767, but this behavior is deprecated so you need to update your launch file.");
      deadzone_ /= 32767;
    }

    if (deadzone_ > 0.9)
    {
      ROS_WARN("joy_node: deadzone (%f) greater than 0.9, setting it to 0.9", deadzone_);
      deadzone_ = 0.9;
    }

    if (deadzone_ < 0)
    {
      ROS_WARN("joy_node: deadzone_ (%f) less than 0, setting to 0.", deadzone_);
      deadzone_ = 0;
    }

    if (autorepeat_rate_ < 0)
    {
      ROS_WARN("joy_node: autorepeat_rate (%f) less than 0, setting to 0.", autorepeat_rate_);
      autorepeat_rate_ = 0;
    }

    if (coalesce_interval_ < 0)
    {
      ROS_WARN("joy_node: coalesce_interval (%f) less than 0, setting to 0.", coalesce_interval_);
      coalesce_interval_ = 0;
    }

    // Parameter conversions
    double autorepeat_interval = 1 / autorepeat_rate_;
    double scale = -1. / (1. - deadzone_) / 32767.;
    double unscaled_deadzone = 32767. * deadzone_;

    js_event event;
    struct timeval tv;
    fd_set set;
    int joy_fd;
    event_count_ = 0;
    pub_count_ = 0;
    lastDiagTime_ = ros::Time::now().toSec();

    // Big while loop opens, publishes
    while (nh_.ok())
    {
      open_ = false;
      diagnostic_.force_update();
      bool first_fault = true;
      while (true)
      {
        ros::spinOnce();
        if (!nh_.ok())
          goto cleanup;
        joy_fd = open(joy_dev_.c_str(), O_RDONLY);
        if (joy_fd != -1)
        {
          // There seems to be a bug in the driver or something where the
          // initial events that are to define the initial state of the
          // joystick are not the values of the joystick when it was opened
          // but rather the values of the joystick when it was last closed.
          // Opening then closing and opening again is a hack to get more
          // accurate initial state data.
          close(joy_fd);
          joy_fd = open(joy_dev_.c_str(), O_RDONLY);
        }
        if (joy_fd != -1)
          break;
        if (first_fault)
        {
          ROS_ERROR("Couldn't open joystick %s. Will retry every second.", joy_dev_.c_str());
          first_fault = false;
        }
        sleep(1.0);
        diagnostic_.update();
      }

      ROS_INFO("Opened joystick: %s. deadzone_: %f.", joy_dev_.c_str(), deadzone_);
      open_ = true;
      diagnostic_.force_update();

      bool tv_set = false;
      bool publication_pending = false;
      tv.tv_sec = 1;
      tv.tv_usec = 0;
      joy::Joy joy_msg; // Here because we want to reset it on device close.
      while (nh_.ok())
      {
        ros::spinOnce();

        bool publish_now = false;
        bool publish_soon = false;
        FD_ZERO(&set);
        FD_SET(joy_fd, &set);

        //ROS_INFO("Select...");
        int select_out = select(joy_fd+1, &set, NULL, NULL, &tv);
        //ROS_INFO("Tick...");
        if (select_out == -1)
        {
          tv.tv_sec = 0;
          tv.tv_usec = 0;
          //ROS_INFO("Select returned negative. %i", ros::isShuttingDown());
          continue;
          //				break; // Joystick is probably closed. Not sure if this case is useful.
        }

        if (FD_ISSET(joy_fd, &set))
        {
          if (read(joy_fd, &event, sizeof(js_event)) == -1 && errno != EAGAIN)
            break; // Joystick is probably closed. Definitely occurs.

          //ROS_INFO("Read data...");

          event_count_++;
          switch(event.type)
          {
          case JS_EVENT_BUTTON:
          case JS_EVENT_BUTTON | JS_EVENT_INIT:
            if(event.number >= joy_msg.get_buttons_size())
            {
              int old_size = joy_msg.buttons.size();
              joy_msg.buttons.resize(event.number+1);
              for(unsigned int i=old_size;i<joy_msg.buttons.size();i++)
                joy_msg.buttons[i] = 0.0;
            }
            joy_msg.buttons[event.number] = (event.value ? 1 : 0);
            // For initial events, wait a bit before sending to try to catch
            // all the initial events.
            if (!(event.type & JS_EVENT_INIT))
              publish_now = true;
            else
              publish_soon = true;
            break;
          case JS_EVENT_AXIS:
          case JS_EVENT_AXIS | JS_EVENT_INIT:
            if(event.number >= joy_msg.get_axes_size())
            {
              int old_size = joy_msg.axes.size();
              joy_msg.axes.resize(event.number+1);
              for(unsigned int i=old_size;i<joy_msg.axes.size();i++)
                joy_msg.axes[i] = 0.0;
            }
            if (!(event.type & JS_EVENT_INIT)) // Init event.value is wrong.
            {
              double val = event.value;
              // Allows deadzone to be "smooth"
              if (val > unscaled_deadzone)
                val -= unscaled_deadzone;
              else if (val < -unscaled_deadzone)
                val += unscaled_deadzone;
              else
                val = 0;
              joy_msg.axes[event.number] = val * scale;
            }
            // Will wait a bit before sending to try to combine events.
            publish_soon = true;
            break;
          default:
            ROS_WARN("joy_node: Unknown event type. Please file a ticket. time=%u, value=%d, type=%Xh, number=%d", event.time, event.value, event.type, event.number);
            break;
          }
        }
        else if (tv_set) // Assume that the timer has expired.
          publish_now = true;

        if (publish_now)
        {
          // Assume that all the JS_EVENT_INIT messages have arrived already.
          // This should be the case as the kernel sends them along as soon as
          // the device opens.
          //ROS_INFO("Publish...");
          pub_.publish(joy_msg);
          publish_now = false;
          tv_set = false;
          publication_pending = false;
          publish_soon = false;
          pub_count_++;
        }

        // If an axis event occurred, start a timer to combine with other
        // events.
        if (!publication_pending && publish_soon)
        {
          tv.tv_sec = trunc(coalesce_interval_);
          tv.tv_usec = (coalesce_interval_ - tv.tv_sec) * 1e6;
          publication_pending = true;
          tv_set = true;
          //ROS_INFO("Pub pending...");
        }

        // If nothing is going on, start a timer to do autorepeat.
        if (!tv_set && autorepeat_rate_ > 0)
        {
          tv.tv_sec = trunc(autorepeat_interval);
          tv.tv_usec = (autorepeat_interval - tv.tv_sec) * 1e6;
          tv_set = true;
          //ROS_INFO("Autorepeat pending... %i %i", tv.tv_sec, tv.tv_usec);
        }

        if (!tv_set)
        {
          tv.tv_sec = 1;
          tv.tv_usec = 0;
        }

        diagnostic_.update();
      } // End of joystick open loop.

      close(joy_fd);
      ros::spinOnce();
      if (nh_.ok())
        ROS_ERROR("Connection to joystick device lost unexpectedly. Will reopen.");
    }

  cleanup:
    ROS_INFO("joy_node shut down.");

    return 0;
  }
};

int main(int argc, char **argv)
{
  ros::init(argc, argv, "joy_node");
  Joystick j;
  return j.main(argc, argv);
}
<commit_msg>Added call to setHardwareID to get rid of warning.<commit_after>/*
 * teleop_pr2
 * Copyright (c) 2009, Willow Garage, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <ORGANIZATION> nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
 */

//\author: Blaise Gassend

#include <unistd.h>
#include <math.h>
#include <linux/joystick.h>
#include <fcntl.h>
#include <diagnostic_updater/diagnostic_updater.h>
#include "ros/ros.h"
#include "joy/Joy.h"

///\brief Opens, reads from and publishes joystick events
class Joystick
{
private:
  ros::NodeHandle nh_;
  bool open_;
  std::string joy_dev_;
  double deadzone_;
  double autorepeat_rate_;  // in Hz.  0 for no repeat.
  double coalesce_interval_; // Defaults to 100 Hz rate limit.
  int event_count_;
  int pub_count_;
  ros::Publisher pub_;
  double lastDiagTime_;

  diagnostic_updater::Updater diagnostic_;

  ///\brief Publishes diagnostics and status
  void diagnostics(diagnostic_updater::DiagnosticStatusWrapper& stat)
  {
    double now = ros::Time::now().toSec();
    double interval = now - lastDiagTime_;
    if (open_)
      stat.summary(0, "OK");
    else
      stat.summary(2, "Joystick not open.");

    stat.add("topic", pub_.getTopic());
    stat.add("device", joy_dev_);
    stat.add("dead zone", deadzone_);
    stat.add("autorepeat rate (Hz)", autorepeat_rate_);
    stat.add("coalesce interval (s)", coalesce_interval_);
    stat.add("recent joystick event rate (Hz)", event_count_ / interval);
    stat.add("recent publication rate (Hz)", pub_count_ / interval);
    stat.add("subscribers", pub_.getNumSubscribers());
    event_count_ = 0;
    pub_count_ = 0;
    lastDiagTime_ = now;
  }

public:
  Joystick() : nh_(), diagnostic_()
  {}

  ///\brief Opens joystick port, reads from port and publishes while node is active
  int main(int argc, char **argv)
  {
    diagnostic_.add("Joystick Driver Status", this, &Joystick::diagnostics);
    diagnostic_.setHardwareID("none");

    // Parameters
    ros::NodeHandle nh_param("~");
    pub_ = nh_.advertise<joy::Joy>("joy", 1);
    nh_param.param<std::string>("dev", joy_dev_, "/dev/input/js0");
    nh_param.param<double>("deadzone", deadzone_, 0.05);
    nh_param.param<double>("autorepeat_rate", autorepeat_rate_, 0);
    nh_param.param<double>("coalesce_interval", coalesce_interval_, 0.001);

    // Checks on parameters
    if (autorepeat_rate_ > 1 / coalesce_interval_)
      ROS_WARN("joy_node: autorepeat_rate (%f Hz) > 1/coalesce_interval (%f Hz) does not make sense. Timing behavior is not well defined.", autorepeat_rate_, 1/coalesce_interval_);

    if (deadzone_ >= 1)
    {
      ROS_WARN("joy_node: deadzone greater than 1 was requested. The semantics of deadzone have changed. It is now related to the range [-1:1] instead of [-32767:32767]. For now I am dividing your deadzone by 32767, but this behavior is deprecated so you need to update your launch file.");
      deadzone_ /= 32767;
    }

    if (deadzone_ > 0.9)
    {
      ROS_WARN("joy_node: deadzone (%f) greater than 0.9, setting it to 0.9", deadzone_);
      deadzone_ = 0.9;
    }

    if (deadzone_ < 0)
    {
      ROS_WARN("joy_node: deadzone_ (%f) less than 0, setting to 0.", deadzone_);
      deadzone_ = 0;
    }

    if (autorepeat_rate_ < 0)
    {
      ROS_WARN("joy_node: autorepeat_rate (%f) less than 0, setting to 0.", autorepeat_rate_);
      autorepeat_rate_ = 0;
    }

    if (coalesce_interval_ < 0)
    {
      ROS_WARN("joy_node: coalesce_interval (%f) less than 0, setting to 0.", coalesce_interval_);
      coalesce_interval_ = 0;
    }

    // Parameter conversions
    double autorepeat_interval = 1 / autorepeat_rate_;
    double scale = -1. / (1. - deadzone_) / 32767.;
    double unscaled_deadzone = 32767. * deadzone_;

    js_event event;
    struct timeval tv;
    fd_set set;
    int joy_fd;
    event_count_ = 0;
    pub_count_ = 0;
    lastDiagTime_ = ros::Time::now().toSec();

    // Big while loop opens, publishes
    while (nh_.ok())
    {
      open_ = false;
      diagnostic_.force_update();
      bool first_fault = true;
      while (true)
      {
        ros::spinOnce();
        if (!nh_.ok())
          goto cleanup;
        joy_fd = open(joy_dev_.c_str(), O_RDONLY);
        if (joy_fd != -1)
        {
          // There seems to be a bug in the driver or something where the
          // initial events that are to define the initial state of the
          // joystick are not the values of the joystick when it was opened
          // but rather the values of the joystick when it was last closed.
          // Opening then closing and opening again is a hack to get more
          // accurate initial state data.
          close(joy_fd);
          joy_fd = open(joy_dev_.c_str(), O_RDONLY);
        }
        if (joy_fd != -1)
          break;
        if (first_fault)
        {
          ROS_ERROR("Couldn't open joystick %s. Will retry every second.", joy_dev_.c_str());
          first_fault = false;
        }
        sleep(1.0);
        diagnostic_.update();
      }

      ROS_INFO("Opened joystick: %s. deadzone_: %f.", joy_dev_.c_str(), deadzone_);
      open_ = true;
      diagnostic_.force_update();

      bool tv_set = false;
      bool publication_pending = false;
      tv.tv_sec = 1;
      tv.tv_usec = 0;
      joy::Joy joy_msg; // Here because we want to reset it on device close.
      while (nh_.ok())
      {
        ros::spinOnce();

        bool publish_now = false;
        bool publish_soon = false;
        FD_ZERO(&set);
        FD_SET(joy_fd, &set);

        //ROS_INFO("Select...");
        int select_out = select(joy_fd+1, &set, NULL, NULL, &tv);
        //ROS_INFO("Tick...");
        if (select_out == -1)
        {
          tv.tv_sec = 0;
          tv.tv_usec = 0;
          //ROS_INFO("Select returned negative. %i", ros::isShuttingDown());
          continue;
          //				break; // Joystick is probably closed. Not sure if this case is useful.
        }

        if (FD_ISSET(joy_fd, &set))
        {
          if (read(joy_fd, &event, sizeof(js_event)) == -1 && errno != EAGAIN)
            break; // Joystick is probably closed. Definitely occurs.

          //ROS_INFO("Read data...");

          event_count_++;
          switch(event.type)
          {
          case JS_EVENT_BUTTON:
          case JS_EVENT_BUTTON | JS_EVENT_INIT:
            if(event.number >= joy_msg.get_buttons_size())
            {
              int old_size = joy_msg.buttons.size();
              joy_msg.buttons.resize(event.number+1);
              for(unsigned int i=old_size;i<joy_msg.buttons.size();i++)
                joy_msg.buttons[i] = 0.0;
            }
            joy_msg.buttons[event.number] = (event.value ? 1 : 0);
            // For initial events, wait a bit before sending to try to catch
            // all the initial events.
            if (!(event.type & JS_EVENT_INIT))
              publish_now = true;
            else
              publish_soon = true;
            break;
          case JS_EVENT_AXIS:
          case JS_EVENT_AXIS | JS_EVENT_INIT:
            if(event.number >= joy_msg.get_axes_size())
            {
              int old_size = joy_msg.axes.size();
              joy_msg.axes.resize(event.number+1);
              for(unsigned int i=old_size;i<joy_msg.axes.size();i++)
                joy_msg.axes[i] = 0.0;
            }
            if (!(event.type & JS_EVENT_INIT)) // Init event.value is wrong.
            {
              double val = event.value;
              // Allows deadzone to be "smooth"
              if (val > unscaled_deadzone)
                val -= unscaled_deadzone;
              else if (val < -unscaled_deadzone)
                val += unscaled_deadzone;
              else
                val = 0;
              joy_msg.axes[event.number] = val * scale;
            }
            // Will wait a bit before sending to try to combine events.
            publish_soon = true;
            break;
          default:
            ROS_WARN("joy_node: Unknown event type. Please file a ticket. time=%u, value=%d, type=%Xh, number=%d", event.time, event.value, event.type, event.number);
            break;
          }
        }
        else if (tv_set) // Assume that the timer has expired.
          publish_now = true;

        if (publish_now)
        {
          // Assume that all the JS_EVENT_INIT messages have arrived already.
          // This should be the case as the kernel sends them along as soon as
          // the device opens.
          //ROS_INFO("Publish...");
          pub_.publish(joy_msg);
          publish_now = false;
          tv_set = false;
          publication_pending = false;
          publish_soon = false;
          pub_count_++;
        }

        // If an axis event occurred, start a timer to combine with other
        // events.
        if (!publication_pending && publish_soon)
        {
          tv.tv_sec = trunc(coalesce_interval_);
          tv.tv_usec = (coalesce_interval_ - tv.tv_sec) * 1e6;
          publication_pending = true;
          tv_set = true;
          //ROS_INFO("Pub pending...");
        }

        // If nothing is going on, start a timer to do autorepeat.
        if (!tv_set && autorepeat_rate_ > 0)
        {
          tv.tv_sec = trunc(autorepeat_interval);
          tv.tv_usec = (autorepeat_interval - tv.tv_sec) * 1e6;
          tv_set = true;
          //ROS_INFO("Autorepeat pending... %i %i", tv.tv_sec, tv.tv_usec);
        }

        if (!tv_set)
        {
          tv.tv_sec = 1;
          tv.tv_usec = 0;
        }

        diagnostic_.update();
      } // End of joystick open loop.

      close(joy_fd);
      ros::spinOnce();
      if (nh_.ok())
        ROS_ERROR("Connection to joystick device lost unexpectedly. Will reopen.");
    }

  cleanup:
    ROS_INFO("joy_node shut down.");

    return 0;
  }
};

int main(int argc, char **argv)
{
  ros::init(argc, argv, "joy_node");
  Joystick j;
  return j.main(argc, argv);
}
<|endoftext|>


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

#include <vector>

#include <Observation.hpp>

#pragma once

void generateBeamDriver(bool subbandDedispersion, AstroData::Observation & observation, std::vector< uint8_t > & beamDriver, unsigned int padding);

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

#include <vector>

#include <Observation.hpp>
#include <Platform.hpp>

#pragma once

void generateBeamDriver(bool subbandDedispersion, AstroData::Observation & observation, std::vector< uint8_t > & beamDriver, unsigned int padding);

<|endoftext|>


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

#include <string>

#include <utils.hpp>
#include <Observation.hpp>

#ifndef BEAM_FORMER_HPP
#define BEAM_FORMER_HPP

namespace RadioAstronomy {

// Sequential beam forming algorithm
template< typename T > void beamFormer(const AstroData::Observation & observation, const std::vector< T > & samples, std::vector< T > & output, const std::vector< float > & weights);
// OpenCL beam forming algorithm
std::string * getBeamFormerOpenCL(const unsigned int nrSamplesPerBlock, const unsigned int nrBeamsPerBlock, const unsigned int nrSamplesPerThread, const unsigned int nrBeamsPerThread, const std::string & dataType, const AstroData::Observation & observation);

// Implementations
template< typename T > void beamFormer(const AstroData::Observation & observation, const std::vector< T > & samples, std::vector< T > & output, const std::vector< float > & weights) {
  for ( unsigned int channel = 0; channel < observation.getNrChannels(); channel++ ) {
    for ( unsigned int sample = 0; sample < observation.getNrSamplesPerSecond(); sample++ ) {
      for ( unsigned int beam = 0; beam < observation.getNrBeams(); beam++ ) {
        T beamP0_r = 0;
        T beamP0_i = 0;
        T beamP1_r = 0;
        T beamP1_i = 0;

        for ( unsigned int station = 0; station < observation.getNrStations(); station++ ) {
          T * samplePointer = &(samples.data()[(channel * observation.getNrStations() * observation.getNrSamplesPerPaddedSecond() * 4) + (station * observation.getNrSamplesPerPaddedSecond() * 4) + (sample * 4)]);
          float * weightPointer = &(weights.data()[(channel * observation.getNrStations() * observation.getNrPaddedBeams() * 2) + (station * observation.getNrPaddedBeams() * 2) + (beam * 2)]);

          beamP0_r += (samplePointer[0] * weightPointer[0]) - (samplePointer[1] * weightPointer[1]);
          beamP0_i += (samplePointer[0] * weightPointer[1]) + (samplePointer[1] * weightPointer[0]);
          beamP1_r += (samplePointer[2] * weightPointer[0]) - (samplePointer[3] * weightPointer[1]);
          beamP1_i += (samplePointer[2] * weightPointer[1]) + (samplePointer[3] * weightPointer[0]);
        }
        beamP0_r /= observation.getNrStations();
        beamP0_i /= observation.getNrStations();
        beamP1_r /= observation.getNrStations();
        beamP1_i /= observation.getNrStations();
        output[(beam * observation.getNrChannels() * observation.getNrSamplesPerPaddedSecond() * 4) + (channel * observation.getNrSamplesPerPaddedSecond() * 4) + (sample * 4)] = beamP0_r;
        output[(beam * observation.getNrChannels() * observation.getNrSamplesPerPaddedSecond() * 4) + (channel * observation.getNrSamplesPerPaddedSecond() * 4) + (sample * 4) + 1] = beamP0_i;
        output[(beam * observation.getNrChannels() * observation.getNrSamplesPerPaddedSecond() * 4) + (channel * observation.getNrSamplesPerPaddedSecond() * 4) + (sample * 4) + 2] = beamP1_r;
        output[(beam * observation.getNrChannels() * observation.getNrSamplesPerPaddedSecond() * 4) + (channel * observation.getNrSamplesPerPaddedSecond() * 4) + (sample * 4) + 3] = beamP1_i;
      }
    }
  }
}

std::string * getBeamFormerOpenCL(const unsigned int nrSamplesPerBlock, const unsigned int nrBeamsPerBlock, const unsigned int nrSamplesPerThread, const unsigned int nrBeamsPerThread, const std::string & dataType, const AstroData::Observation & observation) {
  std::string * code = new std::string();

  // Begin kernel's template
  *code = "__kernel void beamFormer(__global const " + dataType + "4 * restrict const samples, __global " + dataType + "4 * restrict const output, __global const float2 * restrict const weights) {\n"
    "const unsigned int channel = get_group_id(2);\n"
    "const unsigned int beam = (get_group_id(1) * " + isa::utils::toString(nrBeamsPerBlock * nrBeamsPerThread) + ") + (get_local_id(1) * " + isa::utils::toString(nrBeamsPerThread) + ");\n"
    "unsigned int itemGlobal = 0;\n"
    "unsigned int itemLocal = 0;\n"
    "<%DEF_SAMPLES%>"
    "<%DEF_SUMS%>"
    + dataType + "4 sample = (" + dataType + "4)(0);\n"
    "__local float2 localWeights[" + isa::utils::toString(nrBeamsPerBlock * nrBeamsPerThread) + "];\n"
    "float2 weight = (float2)(0);\n"
    "\n"
    "for ( unsigned int station = 0; station < " + isa::utils::toString(observation.getNrStations()) + "; station++ ) {\n"
    "<%LOAD_COMPUTE%>"
    "}\n"
    "<%AVERAGE%>"
    "<%STORE%>"
    "}\n";
  std::string defSamplesTemplate = "const unsigned int sample<%SNUM%> = (get_group_id(0) * " + isa::utils::toString(nrSamplesPerBlock * nrSamplesPerThread) + ") + get_local_id(0) + <%OFFSET%>;\n";
  std::string defSumsTemplate = dataType + "4 beam<%BNUM%>s<%SNUM%> = (" + dataType + "4)(0);\n";
  std::string loadComputeTemplate = "itemGlobal = (channel * " + isa::utils::toString(observation.getNrStations() * observation.getNrPaddedBeams()) + ") + (station * " + isa::utils::toString(observation.getNrPaddedBeams()) + ") + (get_local_id(1) * " + isa::utils::toString(nrSamplesPerBlock) + ") + get_local_id(0);\n"
    "itemLocal = get_local_id(0);\n"
    "while ( itemLocal < " + isa::utils::toString(nrBeamsPerBlock * nrBeamsPerThread) + ") {\n"
    "localWeights[itemLocal] = weights[itemGlobal];\n"
    "itemLocal += " + isa::utils::toString(nrSamplesPerBlock * nrBeamsPerBlock) + ";\n"
    "itemGlobal += " + isa::utils::toString(nrSamplesPerBlock * nrBeamsPerBlock) + ";\n"
    "}\n"
    "barrier(CLK_LOCAL_MEM_FENCE);\n"
    "sample = samples[(channel * " + isa::utils::toString(observation.getNrStations() * observation.getNrSamplesPerPaddedSecond()) + ") + (station * " + isa::utils::toString(observation.getNrSamplesPerPaddedSecond()) + ") + sample<%SNUM%>];\n"
    "<%SUMS%>";
  std::string sumsTemplate = "weight = localWeights[(get_local_id(1) * " + isa::utils::toString(nrBeamsPerThread) + ") + <%BNUM%>];\n"
    "beam<%BNUM%>s<%SNUM%>.x += (sample.x * weight.x) - (sample.y * weight.y);\n"
    "beam<%BNUM%>s<%SNUM%>.y += (sample.x * weight.y) + (sample.y * weight.x);\n"
    "beam<%BNUM%>s<%SNUM%>.z += (sample.z * weight.x) - (sample.w * weight.y);\n"
    "beam<%BNUM%>s<%SNUM%>.w += (sample.z * weight.y) + (sample.w * weight.x);\n";
  std::string averageTemplate = "beam<%BNUM%>s<%SNUM%> *= " + isa::utils::toString(1.0f / observation.getNrStations()) + "f;\n";
  std::string storeTemplate = "output[((beam + <%BNUM%>) * " + isa::utils::toString(observation.getNrChannels() * observation.getNrSamplesPerPaddedSecond()) + ") + (channel * " + isa::utils::toString(observation.getNrSamplesPerPaddedSecond()) + ") + sample<%SNUM%>] = beam<%BNUM%>s<%SNUM%>;\n";
  // End kernel's template

  std::string * defSamples_s = new std::string();
  std::string * defSums_s = new std::string();
  std::string * loadCompute_s = new std::string();
  std::string * average_s = new std::string();
  std::string * store_s = new std::string();

  for ( unsigned int sample = 0; sample < nrSamplesPerThread; sample++ ) {
    std::string sample_s = isa::utils::toString(sample);
    std::string offset_s = isa::utils::toString(sample * nrSamplesPerBlock);
    std::string * sums_s = new std::string();
    std::string * temp_s = 0;

    temp_s = isa::utils::replace(&defSamplesTemplate, "<%SNUM%>", sample_s);
    temp_s = isa::utils::replace(temp_s, "<%OFFSET%>", offset_s, true);
    defSamples_s->append(*temp_s);
    delete temp_s;

    for ( unsigned int beam = 0; beam < nrBeamsPerThread; beam++ ) {
      std::string beam_s = isa::utils::toString(beam);
      std::string * temp_s = 0;

      temp_s = isa::utils::replace(&defSumsTemplate, "<%BNUM%>", beam_s);
      defSums_s->append(*temp_s);
      delete temp_s;
      temp_s = isa::utils::replace(&sumsTemplate, "<%BNUM%>", beam_s);
      sums_s->append(*temp_s);
      delete temp_s;
      temp_s = isa::utils::replace(&averageTemplate, "<%BNUM%>", beam_s);
      average_s->append(*temp_s);
      delete temp_s;
      temp_s = isa::utils::replace(&storeTemplate, "<%BNUM%>", beam_s);
      store_s->append(*temp_s);
      delete temp_s;
    }
    defSums_s = isa::utils::replace(defSums_s, "<%SNUM%>", sample_s, true);
    temp_s = isa::utils::replace(&loadComputeTemplate, "<%SNUM%>", sample_s);
    temp_s = isa::utils::replace(temp_s, "<%SUMS%>", *sums_s, true);
    temp_s = isa::utils::replace(temp_s, "<%SNUM%>", sample_s, true);
    loadCompute_s->append(*temp_s);
    delete temp_s;
    average_s = isa::utils::replace(average_s, "<%SNUM%>", sample_s, true);
    store_s = isa::utils::replace(store_s, "<%SNUM%>", sample_s, true);
  }

  code = isa::utils::replace(code, "<%DEF_SAMPLES%>", *defSamples_s, true);
  code = isa::utils::replace(code, "<%DEF_SUMS%>", *defSums_s, true);
  code = isa::utils::replace(code, "<%LOAD_COMPUTE%>", *loadCompute_s, true);
  code = isa::utils::replace(code, "<%AVERAGE%>", *average_s, true);
  code = isa::utils::replace(code, "<%STORE%>", *store_s, true);

  return code;
}

} // RadioAstronomy

#endif // BEAM_FORMER_HPP

<commit_msg>Removing the const constraint because I need to play with pointers.<commit_after>// Copyright 2014 Alessio Sclocco <a.sclocco@vu.nl>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <string>

#include <utils.hpp>
#include <Observation.hpp>

#ifndef BEAM_FORMER_HPP
#define BEAM_FORMER_HPP

namespace RadioAstronomy {

// Sequential beam forming algorithm
template< typename T > void beamFormer(const AstroData::Observation & observation, std::vector< T > & samples, std::vector< T > & output, std::vector< float > & weights);
// OpenCL beam forming algorithm
std::string * getBeamFormerOpenCL(const unsigned int nrSamplesPerBlock, const unsigned int nrBeamsPerBlock, const unsigned int nrSamplesPerThread, const unsigned int nrBeamsPerThread, const std::string & dataType, const AstroData::Observation & observation);

// Implementations
template< typename T > void beamFormer(const AstroData::Observation & observation, std::vector< T > & samples, std::vector< T > & output, std::vector< float > & weights) {
  for ( unsigned int channel = 0; channel < observation.getNrChannels(); channel++ ) {
    for ( unsigned int sample = 0; sample < observation.getNrSamplesPerSecond(); sample++ ) {
      for ( unsigned int beam = 0; beam < observation.getNrBeams(); beam++ ) {
        T beamP0_r = 0;
        T beamP0_i = 0;
        T beamP1_r = 0;
        T beamP1_i = 0;

        for ( unsigned int station = 0; station < observation.getNrStations(); station++ ) {
          T * samplePointer = &(samples.data()[(channel * observation.getNrStations() * observation.getNrSamplesPerPaddedSecond() * 4) + (station * observation.getNrSamplesPerPaddedSecond() * 4) + (sample * 4)]);
          float * weightPointer = &(weights.data()[(channel * observation.getNrStations() * observation.getNrPaddedBeams() * 2) + (station * observation.getNrPaddedBeams() * 2) + (beam * 2)]);

          beamP0_r += (samplePointer[0] * weightPointer[0]) - (samplePointer[1] * weightPointer[1]);
          beamP0_i += (samplePointer[0] * weightPointer[1]) + (samplePointer[1] * weightPointer[0]);
          beamP1_r += (samplePointer[2] * weightPointer[0]) - (samplePointer[3] * weightPointer[1]);
          beamP1_i += (samplePointer[2] * weightPointer[1]) + (samplePointer[3] * weightPointer[0]);
        }
        beamP0_r /= observation.getNrStations();
        beamP0_i /= observation.getNrStations();
        beamP1_r /= observation.getNrStations();
        beamP1_i /= observation.getNrStations();
        output[(beam * observation.getNrChannels() * observation.getNrSamplesPerPaddedSecond() * 4) + (channel * observation.getNrSamplesPerPaddedSecond() * 4) + (sample * 4)] = beamP0_r;
        output[(beam * observation.getNrChannels() * observation.getNrSamplesPerPaddedSecond() * 4) + (channel * observation.getNrSamplesPerPaddedSecond() * 4) + (sample * 4) + 1] = beamP0_i;
        output[(beam * observation.getNrChannels() * observation.getNrSamplesPerPaddedSecond() * 4) + (channel * observation.getNrSamplesPerPaddedSecond() * 4) + (sample * 4) + 2] = beamP1_r;
        output[(beam * observation.getNrChannels() * observation.getNrSamplesPerPaddedSecond() * 4) + (channel * observation.getNrSamplesPerPaddedSecond() * 4) + (sample * 4) + 3] = beamP1_i;
      }
    }
  }
}

std::string * getBeamFormerOpenCL(const unsigned int nrSamplesPerBlock, const unsigned int nrBeamsPerBlock, const unsigned int nrSamplesPerThread, const unsigned int nrBeamsPerThread, const std::string & dataType, const AstroData::Observation & observation) {
  std::string * code = new std::string();

  // Begin kernel's template
  *code = "__kernel void beamFormer(__global const " + dataType + "4 * restrict const samples, __global " + dataType + "4 * restrict const output, __global const float2 * restrict const weights) {\n"
    "const unsigned int channel = get_group_id(2);\n"
    "const unsigned int beam = (get_group_id(1) * " + isa::utils::toString(nrBeamsPerBlock * nrBeamsPerThread) + ") + (get_local_id(1) * " + isa::utils::toString(nrBeamsPerThread) + ");\n"
    "unsigned int itemGlobal = 0;\n"
    "unsigned int itemLocal = 0;\n"
    "<%DEF_SAMPLES%>"
    "<%DEF_SUMS%>"
    + dataType + "4 sample = (" + dataType + "4)(0);\n"
    "__local float2 localWeights[" + isa::utils::toString(nrBeamsPerBlock * nrBeamsPerThread) + "];\n"
    "float2 weight = (float2)(0);\n"
    "\n"
    "for ( unsigned int station = 0; station < " + isa::utils::toString(observation.getNrStations()) + "; station++ ) {\n"
    "<%LOAD_COMPUTE%>"
    "}\n"
    "<%AVERAGE%>"
    "<%STORE%>"
    "}\n";
  std::string defSamplesTemplate = "const unsigned int sample<%SNUM%> = (get_group_id(0) * " + isa::utils::toString(nrSamplesPerBlock * nrSamplesPerThread) + ") + get_local_id(0) + <%OFFSET%>;\n";
  std::string defSumsTemplate = dataType + "4 beam<%BNUM%>s<%SNUM%> = (" + dataType + "4)(0);\n";
  std::string loadComputeTemplate = "itemGlobal = (channel * " + isa::utils::toString(observation.getNrStations() * observation.getNrPaddedBeams()) + ") + (station * " + isa::utils::toString(observation.getNrPaddedBeams()) + ") + (get_local_id(1) * " + isa::utils::toString(nrSamplesPerBlock) + ") + get_local_id(0);\n"
    "itemLocal = get_local_id(0);\n"
    "while ( itemLocal < " + isa::utils::toString(nrBeamsPerBlock * nrBeamsPerThread) + ") {\n"
    "localWeights[itemLocal] = weights[itemGlobal];\n"
    "itemLocal += " + isa::utils::toString(nrSamplesPerBlock * nrBeamsPerBlock) + ";\n"
    "itemGlobal += " + isa::utils::toString(nrSamplesPerBlock * nrBeamsPerBlock) + ";\n"
    "}\n"
    "barrier(CLK_LOCAL_MEM_FENCE);\n"
    "sample = samples[(channel * " + isa::utils::toString(observation.getNrStations() * observation.getNrSamplesPerPaddedSecond()) + ") + (station * " + isa::utils::toString(observation.getNrSamplesPerPaddedSecond()) + ") + sample<%SNUM%>];\n"
    "<%SUMS%>";
  std::string sumsTemplate = "weight = localWeights[(get_local_id(1) * " + isa::utils::toString(nrBeamsPerThread) + ") + <%BNUM%>];\n"
    "beam<%BNUM%>s<%SNUM%>.x += (sample.x * weight.x) - (sample.y * weight.y);\n"
    "beam<%BNUM%>s<%SNUM%>.y += (sample.x * weight.y) + (sample.y * weight.x);\n"
    "beam<%BNUM%>s<%SNUM%>.z += (sample.z * weight.x) - (sample.w * weight.y);\n"
    "beam<%BNUM%>s<%SNUM%>.w += (sample.z * weight.y) + (sample.w * weight.x);\n";
  std::string averageTemplate = "beam<%BNUM%>s<%SNUM%> *= " + isa::utils::toString(1.0f / observation.getNrStations()) + "f;\n";
  std::string storeTemplate = "output[((beam + <%BNUM%>) * " + isa::utils::toString(observation.getNrChannels() * observation.getNrSamplesPerPaddedSecond()) + ") + (channel * " + isa::utils::toString(observation.getNrSamplesPerPaddedSecond()) + ") + sample<%SNUM%>] = beam<%BNUM%>s<%SNUM%>;\n";
  // End kernel's template

  std::string * defSamples_s = new std::string();
  std::string * defSums_s = new std::string();
  std::string * loadCompute_s = new std::string();
  std::string * average_s = new std::string();
  std::string * store_s = new std::string();

  for ( unsigned int sample = 0; sample < nrSamplesPerThread; sample++ ) {
    std::string sample_s = isa::utils::toString(sample);
    std::string offset_s = isa::utils::toString(sample * nrSamplesPerBlock);
    std::string * sums_s = new std::string();
    std::string * temp_s = 0;

    temp_s = isa::utils::replace(&defSamplesTemplate, "<%SNUM%>", sample_s);
    temp_s = isa::utils::replace(temp_s, "<%OFFSET%>", offset_s, true);
    defSamples_s->append(*temp_s);
    delete temp_s;

    for ( unsigned int beam = 0; beam < nrBeamsPerThread; beam++ ) {
      std::string beam_s = isa::utils::toString(beam);
      std::string * temp_s = 0;

      temp_s = isa::utils::replace(&defSumsTemplate, "<%BNUM%>", beam_s);
      defSums_s->append(*temp_s);
      delete temp_s;
      temp_s = isa::utils::replace(&sumsTemplate, "<%BNUM%>", beam_s);
      sums_s->append(*temp_s);
      delete temp_s;
      temp_s = isa::utils::replace(&averageTemplate, "<%BNUM%>", beam_s);
      average_s->append(*temp_s);
      delete temp_s;
      temp_s = isa::utils::replace(&storeTemplate, "<%BNUM%>", beam_s);
      store_s->append(*temp_s);
      delete temp_s;
    }
    defSums_s = isa::utils::replace(defSums_s, "<%SNUM%>", sample_s, true);
    temp_s = isa::utils::replace(&loadComputeTemplate, "<%SNUM%>", sample_s);
    temp_s = isa::utils::replace(temp_s, "<%SUMS%>", *sums_s, true);
    temp_s = isa::utils::replace(temp_s, "<%SNUM%>", sample_s, true);
    loadCompute_s->append(*temp_s);
    delete temp_s;
    average_s = isa::utils::replace(average_s, "<%SNUM%>", sample_s, true);
    store_s = isa::utils::replace(store_s, "<%SNUM%>", sample_s, true);
  }

  code = isa::utils::replace(code, "<%DEF_SAMPLES%>", *defSamples_s, true);
  code = isa::utils::replace(code, "<%DEF_SUMS%>", *defSums_s, true);
  code = isa::utils::replace(code, "<%LOAD_COMPUTE%>", *loadCompute_s, true);
  code = isa::utils::replace(code, "<%AVERAGE%>", *average_s, true);
  code = isa::utils::replace(code, "<%STORE%>", *store_s, true);

  return code;
}

} // RadioAstronomy

#endif // BEAM_FORMER_HPP

<|endoftext|>


<commit_before>/***********************************************************************************************************************
*                                                                                *
* SPLASH build system v0.2                                                                                *
*                                                                                *
* Copyright (c) 2016 Andrew D. Zonenberg                                                                               *
* All rights reserved.                                                                                *
*                                                                                *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                *
*                                                                                *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                *
*                                                                                *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "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 AUTHORS BE HELD 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 "splashcore.h"

using namespace std;

Cache* g_cache = NULL;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Construction / destruction

Cache::Cache(string cachename)
{
	LogVerbose("Initializing cache subsystem...\n");
	LogIndenter li;

	//TODO: get cache path from command line arg or something?
	//Hard-coding the path prevents more than one splashctl instance from running on a given server.
	//This may or may not be what we want. If we DO want to limit to one instance, we need a way for multiple distinct
	//projects to share the splashctl/splashbuild back end.
	string home = getenv("HOME");
	if(!DoesDirectoryExist(home))
		LogFatal("home dir does not exist\n");
	m_cachePath = home + "/.splash/cache-" + cachename;

	//If the cache directory does not exist, create it
	if(!DoesDirectoryExist(m_cachePath))
	{
		LogDebug("Cache directory does not exist, creating it\n");
		MakeDirectoryRecursive(m_cachePath, 0600);
	}

	//It exists, load existing entries
	else
	{
		LogDebug("Cache directory exists, loading items\n");
		for(unsigned int i=0; i<256; i++)
		{
			//If the cache isn't very full we might not have entries in every bucket
			char hex[3];
			snprintf(hex, sizeof(hex), "%02x", i);
			string dirname = m_cachePath + "/" + hex;
			if(!DoesDirectoryExist(dirname))
				continue;

			//It exists, load whatever is in it
			vector<string> subdirs;
			FindSubdirs(dirname, subdirs);
			for(auto dir : subdirs)
			{
				//The object ID is what goes in our table, not the file hash
				string oid = GetBasenameOfFile(dir);
				if(!ValidateCacheEntry(oid))
				{
					LogWarning("Cache entry %s is not valid\n", oid.c_str());
					continue;
				}

				//TODO: load fail records too

				//Cache entry is valid, add to the cache table
				m_cacheIndex.emplace(oid);
			}
		}

		LogVerbose("%d cache entries loaded\n", (int)m_cacheIndex.size());
	}
}

Cache::~Cache()
{
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Status queries

NodeInfo::NodeState Cache::GetState(string id)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	if(IsCached(id))
		return NodeInfo::READY;

	else if(IsFailed(id))
		return NodeInfo::FAILED;

	//TODO: check if it's building

	else
		return NodeInfo::MISSING;
}

/**
	@brief Determine if a particular object is in the cache.

	@param id		Object ID hash
 */
bool Cache::IsCached(string id)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	if(m_cacheIndex.find(id) != m_cacheIndex.end())
		return true;

	return false;
}

/**
	@brief Determine if a particular object is in the cache with a "fail" record.

	@param id		Object ID hash
 */
bool Cache::IsFailed(string id)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	if(m_cacheFails.find(id) != m_cacheFails.end())
		return true;

	return false;
}

/**
	@brief Stronger form of IsCached(). Checks if the internal hash is consistent

	@param id		Object ID hash
 */
bool Cache::ValidateCacheEntry(string id)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	//If the directory doesn't exist, obviously we have nothing useful there
	string dir = GetStoragePath(id);
	if(!DoesDirectoryExist(dir))
		return false;

	//If we're missing the file or hash, can't possibly be valid
	string fpath = dir + "/data";
	string hpath = dir + "/hash";
	if( !DoesFileExist(fpath) || !DoesFileExist(hpath) )
		return false;

	//Get the expected hash and compare to the real one
	//If it's invalid, just get rid of the junk
	string expected = GetFileContents(hpath);
	string found = sha256_file(fpath);
	if(expected != found)
	{
		LogWarning("Cache directory %s is corrupted (hash match failed)\n", id.c_str());
		LogDebug("Expected hash: %s\n", expected.c_str());
		LogDebug("Found hash:    %s\n", found.c_str());
		ShellCommand(string("rm -I ") + dir + "/*");
		ShellCommand(string("rm -r ") + dir);
		return false;
	}

	return true;
}

/**
	@brief Gets the cache path used for storing a particular object
 */
string Cache::GetStoragePath(string id)
{
	return m_cachePath + "/" + id.substr(0, 2) + "/" + id;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Cache manipulation

/**
	@brief Reads a file from the cache
 */
string Cache::ReadCachedFile(string id)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	//Sanity check
	if(!IsCached(id))
	{
		LogError("Requested file %s is not in cache\n", id.c_str());
		return "";
	}

	//Read the file
	string ret = GetFileContents(GetStoragePath(id) + "/data");

	return ret;
}

/**
	@brief Reads the log from a file from the cache
 */
string Cache::ReadCachedLog(string id)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	//Sanity check
	if(!IsCached(id) && !IsFailed(id))
	{
		LogError("Requested log %s is not in cache\n", id.c_str());
		return "";
	}

	//Read the file
	string ret = GetFileContents(GetStoragePath(id) + "/log");

	return ret;
}

/**
	@brief Adds a new file's metadata to the cache, reporting that we couldn't actually make it
 */
void Cache::AddFailedFile(string /*basename*/, string id, string log)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	//If the content is already in the cache, skip it
	if(IsCached(id) || IsFailed(id))
		return;

	//Create the directory. If it already exists, delete whatever junk was in there
	string dirname = GetStoragePath(id);
	if(DoesDirectoryExist(dirname))
	{
		LogWarning("Cache directory %s already exists but was not loaded, removing dead files\n", id.c_str());
		ShellCommand(string("rm -I ") + dirname + "/*");
	}
	else
		MakeDirectoryRecursive(dirname, 0600);

	//Create the "failed" record
	if(!PutFileContents(dirname + "/failed", ""))
		return;

	//Write the command stdout (not integrity checked)
	if(!PutFileContents(dirname + "/log", log))
		return;

	//Remember that we have this file cached
	m_cacheFails.emplace(id);
}

/**
	@brief Adds a file to the cache

	@param basename			Name of the file without directory information
	@param id				Object ID hash
	@param hash				SHA-256 sum of the file
	@param data				The data to write
	@param log				Standard output of the command that built this file
 */
void Cache::AddFile(string basename, string id, string hash, string data, string log)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	//If the content is already in the cache, skip it
	if(IsCached(id) || IsFailed(id))
		return;

	//Create the directory. If it already exists, delete whatever junk was in there
	string dirname = GetStoragePath(id);
	if(DoesDirectoryExist(dirname))
	{
		LogWarning("Cache directory %s already exists but was not loaded, removing dead files\n", id.c_str());
		ShellCommand(string("rm -I ") + dirname + "/*");
	}
	else
		MakeDirectoryRecursive(dirname, 0600);

	//Write the file data to it
	if(!PutFileContents(dirname + "/data", data))
		return;

	//Write the hash
	if(!PutFileContents(dirname + "/hash", hash))
		return;

	//Write the command stdout (not integrity checked)
	if(!PutFileContents(dirname + "/log", log))
		return;

	//Sanity check
	string chash = sha256(data);
	if(chash != hash)
	{
		LogWarning("Adding new cache entry for id %s (%s):\n", id.c_str(), basename.c_str());
		LogIndenter li;
		LogWarning("passed:     %s:\n", hash.c_str());
		LogWarning("calculated: %s:\n", chash.c_str());
	}

	//TODO: write the atime file?

	//Remember that we have this file cached
	m_cacheIndex.emplace(id);

	//TODO: add this file's size to the cache, if we went over the cap delete the LRU file
}
<commit_msg>splashcore: Cache now complains if we try to add a file with an empty ID<commit_after>/***********************************************************************************************************************
*                                                                                *
* SPLASH build system v0.2                                                                                *
*                                                                                *
* Copyright (c) 2016 Andrew D. Zonenberg                                                                               *
* All rights reserved.                                                                                *
*                                                                                *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                *
*                                                                                *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                *
*                                                                                *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "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 AUTHORS BE HELD 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 "splashcore.h"

using namespace std;

Cache* g_cache = NULL;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Construction / destruction

Cache::Cache(string cachename)
{
	LogVerbose("Initializing cache subsystem...\n");
	LogIndenter li;

	//TODO: get cache path from command line arg or something?
	//Hard-coding the path prevents more than one splashctl instance from running on a given server.
	//This may or may not be what we want. If we DO want to limit to one instance, we need a way for multiple distinct
	//projects to share the splashctl/splashbuild back end.
	string home = getenv("HOME");
	if(!DoesDirectoryExist(home))
		LogFatal("home dir does not exist\n");
	m_cachePath = home + "/.splash/cache-" + cachename;

	//If the cache directory does not exist, create it
	if(!DoesDirectoryExist(m_cachePath))
	{
		LogDebug("Cache directory does not exist, creating it\n");
		MakeDirectoryRecursive(m_cachePath, 0600);
	}

	//It exists, load existing entries
	else
	{
		LogDebug("Cache directory exists, loading items\n");
		for(unsigned int i=0; i<256; i++)
		{
			//If the cache isn't very full we might not have entries in every bucket
			char hex[3];
			snprintf(hex, sizeof(hex), "%02x", i);
			string dirname = m_cachePath + "/" + hex;
			if(!DoesDirectoryExist(dirname))
				continue;

			//It exists, load whatever is in it
			vector<string> subdirs;
			FindSubdirs(dirname, subdirs);
			for(auto dir : subdirs)
			{
				//The object ID is what goes in our table, not the file hash
				string oid = GetBasenameOfFile(dir);
				if(!ValidateCacheEntry(oid))
				{
					LogWarning("Cache entry %s is not valid\n", oid.c_str());
					continue;
				}

				//TODO: load fail records too

				//Cache entry is valid, add to the cache table
				m_cacheIndex.emplace(oid);
			}
		}

		LogVerbose("%d cache entries loaded\n", (int)m_cacheIndex.size());
	}
}

Cache::~Cache()
{
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Status queries

NodeInfo::NodeState Cache::GetState(string id)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	if(IsCached(id))
		return NodeInfo::READY;

	else if(IsFailed(id))
		return NodeInfo::FAILED;

	//TODO: check if it's building

	else
		return NodeInfo::MISSING;
}

/**
	@brief Determine if a particular object is in the cache.

	@param id		Object ID hash
 */
bool Cache::IsCached(string id)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	if(m_cacheIndex.find(id) != m_cacheIndex.end())
		return true;

	return false;
}

/**
	@brief Determine if a particular object is in the cache with a "fail" record.

	@param id		Object ID hash
 */
bool Cache::IsFailed(string id)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	if(m_cacheFails.find(id) != m_cacheFails.end())
		return true;

	return false;
}

/**
	@brief Stronger form of IsCached(). Checks if the internal hash is consistent

	@param id		Object ID hash
 */
bool Cache::ValidateCacheEntry(string id)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	//If the directory doesn't exist, obviously we have nothing useful there
	string dir = GetStoragePath(id);
	if(!DoesDirectoryExist(dir))
		return false;

	//If we're missing the file or hash, can't possibly be valid
	string fpath = dir + "/data";
	string hpath = dir + "/hash";
	if( !DoesFileExist(fpath) || !DoesFileExist(hpath) )
		return false;

	//Get the expected hash and compare to the real one
	//If it's invalid, just get rid of the junk
	string expected = GetFileContents(hpath);
	string found = sha256_file(fpath);
	if(expected != found)
	{
		LogWarning("Cache directory %s is corrupted (hash match failed)\n", id.c_str());
		LogDebug("Expected hash: %s\n", expected.c_str());
		LogDebug("Found hash:    %s\n", found.c_str());
		ShellCommand(string("rm -I ") + dir + "/*");
		ShellCommand(string("rm -r ") + dir);
		return false;
	}

	return true;
}

/**
	@brief Gets the cache path used for storing a particular object
 */
string Cache::GetStoragePath(string id)
{
	return m_cachePath + "/" + id.substr(0, 2) + "/" + id;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Cache manipulation

/**
	@brief Reads a file from the cache
 */
string Cache::ReadCachedFile(string id)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	//Sanity check
	if(!IsCached(id))
	{
		LogError("Requested file %s is not in cache\n", id.c_str());
		return "";
	}

	//Read the file
	string ret = GetFileContents(GetStoragePath(id) + "/data");

	return ret;
}

/**
	@brief Reads the log from a file from the cache
 */
string Cache::ReadCachedLog(string id)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	//Sanity check
	if(!IsCached(id) && !IsFailed(id))
	{
		LogError("Requested log %s is not in cache\n", id.c_str());
		return "";
	}

	//Read the file
	string ret = GetFileContents(GetStoragePath(id) + "/log");

	return ret;
}

/**
	@brief Adds a new file's metadata to the cache, reporting that we couldn't actually make it
 */
void Cache::AddFailedFile(string basename, string id, string log)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	if(id == "")
	{
		LogError("Tried to add file \"%s\" to cache with empty ID hash\n", basename.c_str());
		return;
	}

	//If the content is already in the cache, skip it
	if(IsCached(id) || IsFailed(id))
		return;

	//Create the directory. If it already exists, delete whatever junk was in there
	string dirname = GetStoragePath(id);
	if(DoesDirectoryExist(dirname))
	{
		LogWarning("Cache directory %s already exists but was not loaded, removing dead files\n", id.c_str());
		ShellCommand(string("rm -I ") + dirname + "/*");
	}
	else
		MakeDirectoryRecursive(dirname, 0600);

	//Create the "failed" record
	if(!PutFileContents(dirname + "/failed", ""))
		return;

	//Write the command stdout (not integrity checked)
	if(!PutFileContents(dirname + "/log", log))
		return;

	//Remember that we have this file cached
	m_cacheFails.emplace(id);
}

/**
	@brief Adds a file to the cache

	@param basename			Name of the file without directory information
	@param id				Object ID hash
	@param hash				SHA-256 sum of the file
	@param data				The data to write
	@param log				Standard output of the command that built this file
 */
void Cache::AddFile(string basename, string id, string hash, string data, string log)
{
	lock_guard<recursive_mutex> lock(m_mutex);

	if(id == "")
	{
		LogError("Tried to add file \"%s\" to cache with empty ID hash\n", basename.c_str());
		return;
	}

	//If the content is already in the cache, skip it
	if(IsCached(id) || IsFailed(id))
		return;

	//Create the directory. If it already exists, delete whatever junk was in there
	string dirname = GetStoragePath(id);
	if(DoesDirectoryExist(dirname))
	{
		LogWarning("Cache directory %s already exists but was not loaded, removing dead files\n", id.c_str());
		ShellCommand(string("rm -I ") + dirname + "/*");
	}
	else
		MakeDirectoryRecursive(dirname, 0600);

	//Write the file data to it
	if(!PutFileContents(dirname + "/data", data))
		return;

	//Write the hash
	if(!PutFileContents(dirname + "/hash", hash))
		return;

	//Write the command stdout (not integrity checked)
	if(!PutFileContents(dirname + "/log", log))
		return;

	//Sanity check
	string chash = sha256(data);
	if(chash != hash)
	{
		LogWarning("Adding new cache entry for id %s (%s):\n", id.c_str(), basename.c_str());
		LogIndenter li;
		LogWarning("passed:     %s:\n", hash.c_str());
		LogWarning("calculated: %s:\n", chash.c_str());
	}

	//TODO: write the atime file?

	//Remember that we have this file cached
	m_cacheIndex.emplace(id);

	//TODO: add this file's size to the cache, if we went over the cap delete the LRU file
}
<|endoftext|>


<commit_before>#ifndef STAN_MCMC_SAMPLE_HPP
#define STAN_MCMC_SAMPLE_HPP

#include <Eigen/Dense>
#include <vector>
#include <string>

namespace stan {

  namespace mcmc {

    class sample {
    public:
      sample(const Eigen::VectorXd& q, double log_prob, double stat)
        : cont_params_(q), log_prob_(log_prob), accept_stat_(stat) {
      }

      virtual ~sample() {}  // No-op

      int size_cont() const {
        return cont_params_.size();
      }

      double cont_params(int k) const {
        return cont_params_(k);
      }

      void cont_params(Eigen::VectorXd& x) const {
        x = cont_params_;
      }

      const Eigen::VectorXd& cont_params() const {
        return cont_params_;
      }

      inline double log_prob() const {
        return log_prob_;
      }

      inline double accept_stat() const {
        return accept_stat_;
      }

      void get_sample_param_names(std::vector<std::string>& names) {
        names.push_back("lp__");
        names.push_back("accept_stat__");
      }

      void get_sample_params(std::vector<double>& values) {
        values.push_back(log_prob_);
        values.push_back(accept_stat_);
      }

    private:
      Eigen::VectorXd cont_params_;  // Continuous coordinates of sample
      double log_prob_;              // Log probability of sample
      double accept_stat_;           // Acceptance statistic of transition
    };

  }  // mcmc

}  // stan

#endif

<commit_msg>Changing a few methods to static methods<commit_after>#ifndef STAN_MCMC_SAMPLE_HPP
#define STAN_MCMC_SAMPLE_HPP

#include <Eigen/Dense>
#include <vector>
#include <string>

namespace stan {

  namespace mcmc {

    class sample {
    public:
      sample(const Eigen::VectorXd& q, double log_prob, double stat)
        : cont_params_(q), log_prob_(log_prob), accept_stat_(stat) {
      }

      virtual ~sample() {}  // No-op

      int size_cont() const {
        return cont_params_.size();
      }

      double cont_params(int k) const {
        return cont_params_(k);
      }

      void cont_params(Eigen::VectorXd& x) const {
        x = cont_params_;
      }

      const Eigen::VectorXd& cont_params() const {
        return cont_params_;
      }

      inline double log_prob() const {
        return log_prob_;
      }

      inline double accept_stat() const {
        return accept_stat_;
      }

      static void get_sample_param_names(std::vector<std::string>& names) {
        names.push_back("lp__");
        names.push_back("accept_stat__");
      }

      static void get_sample_params(std::vector<double>& values) {
        values.push_back(log_prob_);
        values.push_back(accept_stat_);
      }

    private:
      Eigen::VectorXd cont_params_;  // Continuous coordinates of sample
      double log_prob_;              // Log probability of sample
      double accept_stat_;           // Acceptance statistic of transition
    };

  }  // mcmc

}  // stan

#endif

<|endoftext|>