<commit_before>/*
 * Copyright (c) 2016 Chris Iatrou <Chris_Paul.Iatrou@tu-dresden.de>
 * Chair for Process Systems Engineering
 * Technical University of Dresden
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 */

extern "C"
{
  #include "open62541.h"
}

#include "ua_proxies.h"

#include <iostream>

using namespace std;

UA_NodeId *nodePairList_getTargetIdBySourceId(nodePairList pairList, UA_NodeId remoteId) {
  UA_NodeId *local = nullptr;
  // cppcheck-suppress postfixOperator                  REASON: List iterator cannot be prefixed
  for(nodePairList::iterator j = pairList.begin(); j != pairList.end(); j++)
    if (UA_NodeId_equal(&((*j)->sourceNodeId), &remoteId ) == UA_TRUE) {
      local = &((*j)->targetNodeId);
      break;
    }
  return local;
}

UA_NodeId *nodePairList_getSourceIdByTargetId(nodePairList pairList, UA_NodeId targetId) {
  UA_NodeId *local = nullptr;
  // cppcheck-suppress postfixOperator                  REASON: List iterator cannot be prefixed
  for(nodePairList::iterator j = pairList.begin(); j != pairList.end(); j++)
    if (UA_NodeId_equal(&((*j)->targetNodeId), &targetId ) == UA_TRUE) {
      local = &((*j)->sourceNodeId);
      break;
    }
  return local;
}

UA_StatusCode ua_mapInstantiatedNodes(UA_NodeId objectId, UA_NodeId definitionId, void *handle) {
  nodePairList *lst = static_cast<nodePairList*>(handle);

  UA_NodeId_pair *thisNode = new UA_NodeId_pair;
  UA_NodeId_copy(&definitionId, &thisNode->sourceNodeId);
  UA_NodeId_copy(&objectId, &thisNode->targetNodeId);
  lst->push_back(thisNode);

  return UA_STATUSCODE_GOOD;
}

UA_StatusCode ua_callProxy_mapFunctions(UA_Server *server, UA_NodeId objectId, UA_FunctionCall_Map *map, void *srcClass, int recursive) {
  UA_StatusCode retval = UA_STATUSCODE_GOOD;
  if (map == nullptr || server == nullptr)
    return retval;

  // Functions are not instantiated... they are just linked to the node.
  UA_BrowseDescription bDesc;
  UA_BrowseDescription_init(&bDesc);
  bDesc.nodeId = objectId;
  bDesc.browseDirection = UA_BROWSEDIRECTION_FORWARD;
  bDesc.includeSubtypes = UA_FALSE;
  bDesc.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
  bDesc.nodeClassMask = UA_NODECLASS_METHOD;
  bDesc.resultMask = UA_BROWSERESULTMASK_ALL;

  UA_BrowseResult bResp = UA_Server_browse(server, 65536, &bDesc);
  for (int i = 0; i<bResp.referencesSize; i++) {
    UA_ReferenceDescription ref =  bResp.references[i];

    // Recursive on objects
    if (ref.nodeClass == UA_NODECLASS_OBJECT && recursive != 0) {
        ua_callProxy_mapFunctions(server, ref.nodeId.nodeId, map, srcClass, recursive < 0 ? recursive : recursive-1); // Recursive==-1 is infinite, else adjust recursion count
        continue;
    }

    if (! (ref.nodeClass == UA_NODECLASS_METHOD))
      continue;

    // Check if we have this node in our map
    UA_FunctionCall_Map_Element *ele = nullptr;
    // cppcheck-suppress postfixOperator                REASON: List iterator cannot be prefixed
    for (std::list<UA_FunctionCall_Map_Element>::iterator j=map->begin(); j != map->end(); j++) {
      UA_FunctionCall_Map_Element thisEle = *(j);
      if(UA_NodeId_equal(&ref.nodeId.nodeId, (const UA_NodeId *) &thisEle.typeTemplateId) == UA_TRUE) {
        ele = &thisEle;
	break;
      }
    }
    if (ele == nullptr)
      continue;

    // Found a mapping: templateId <-> objectId /w functionpointer...
    UA_FunctionCall_InstanceLookUpTable *tbl =  ele->lookupTable;
    if (tbl == NULL)
      continue; // Only possible on wrong call, since the tables are allocated during compilation!

    // Register this objects Id with the callbacks lookup table
    UA_FunctionCall_InstanceLookupTable_Element *ntel = new UA_FunctionCall_InstanceLookupTable_Element;
    ntel->classInstance = srcClass;
    UA_NodeId_copy( &objectId, &ntel->classObjectId);
    ntel->server = server;
    ele->lookupTable->push_back(ntel);

    // I forgot what this was supposed to be doing - but it clearly was meant to ... eeee...?
    //     for (UA_FunctionCall_InstanceLookUpTable::iterator j=tbl->begin(); j != tbl->end(); j++)
    //       if(UA_NodeId_equal(&(*(j))->classObjectId, &objectId)) {
    //         void *a = &(*(j))->classInstance;
    //         break;
    //       }

    // Found a mapping, now register the callback; pass the server along (instance and object are identified in the table)
    retval |= UA_Server_setMethodNode_callback(server, (const UA_NodeId) ele->typeTemplateId, ele->callback, (void *) server);
  }

  return retval;
}

UA_StatusCode ua_callProxy_mapDataSources(UA_Server* server, nodePairList instantiatedNodesList, UA_DataSource_Map *map, void *srcClass)
{
  UA_StatusCode retval = UA_STATUSCODE_GOOD;
  if (map == nullptr || server == nullptr)
    return retval;

  // Functions are not instantiated... they are just linked to the node.
  for (nodePairList::iterator l = instantiatedNodesList.begin(); l != instantiatedNodesList.end(); ++l) {
    UA_NodeId typeTemplateId = (*l)->sourceNodeId;
    UA_NodeId instantiatedId = (*l)->targetNodeId;

    // Check if we have this node in our map
    UA_DataSource_Map_Element *ele = nullptr;
    // cppcheck-suppress postfixOperator                REASON: List iterator cannot be prefixed
    for (UA_DataSource_Map::iterator j=map->begin(); j != map->end(); j++) {
      UA_DataSource_Map_Element thisEle = *(j);
      if(UA_NodeId_equal(&typeTemplateId, (const UA_NodeId *) &thisEle.typeTemplateId) == UA_TRUE) {
        ele = new UA_DataSource_Map_Element;
        *ele = thisEle;
        break;
      }
    }
    if (ele == nullptr)
      continue;

    // Found a mapping, now register the dataSource
    UA_DataSource ds;
    ds.read = ele->read;
    ds.write = ele->write;
    ds.handle = srcClass;
    // add individual description for every variable
    UA_Server_writeDescription(server, instantiatedId, ele->description);
    delete ele; // inhibit memleak warning during static analysis

    retval |= UA_Server_setVariableNode_dataSource(server, (const UA_NodeId) instantiatedId, ds);
  }

  return retval;
}
<commit_msg>Update ua_proxies.cpp<commit_after>/*
 * Copyright (c) 2016
 * Chris Iatrou <Chris_Paul.Iatrou@tu-dresden.de>
 * Julian Rahm <Julian.Rahm@tu-dresden.de>
 * Chair for Process Systems Engineering
 * Technical University of Dresden
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 */

extern "C"
{
  #include "open62541.h"
}

#include "ua_proxies.h"

#include <iostream>

using namespace std;

UA_NodeId *nodePairList_getTargetIdBySourceId(nodePairList pairList, UA_NodeId remoteId) {
  UA_NodeId *local = nullptr;
  // cppcheck-suppress postfixOperator                  REASON: List iterator cannot be prefixed
  for(nodePairList::iterator j = pairList.begin(); j != pairList.end(); j++)
    if (UA_NodeId_equal(&((*j)->sourceNodeId), &remoteId ) == UA_TRUE) {
      local = &((*j)->targetNodeId);
      break;
    }
  return local;
}

UA_NodeId *nodePairList_getSourceIdByTargetId(nodePairList pairList, UA_NodeId targetId) {
  UA_NodeId *local = nullptr;
  // cppcheck-suppress postfixOperator                  REASON: List iterator cannot be prefixed
  for(nodePairList::iterator j = pairList.begin(); j != pairList.end(); j++)
    if (UA_NodeId_equal(&((*j)->targetNodeId), &targetId ) == UA_TRUE) {
      local = &((*j)->sourceNodeId);
      break;
    }
  return local;
}

UA_StatusCode ua_mapInstantiatedNodes(UA_NodeId objectId, UA_NodeId definitionId, void *handle) {
  nodePairList *lst = static_cast<nodePairList*>(handle);

  UA_NodeId_pair *thisNode = new UA_NodeId_pair;
  UA_NodeId_copy(&definitionId, &thisNode->sourceNodeId);
  UA_NodeId_copy(&objectId, &thisNode->targetNodeId);
  lst->push_back(thisNode);

  return UA_STATUSCODE_GOOD;
}

UA_StatusCode ua_callProxy_mapFunctions(UA_Server *server, UA_NodeId objectId, UA_FunctionCall_Map *map, void *srcClass, int recursive) {
  UA_StatusCode retval = UA_STATUSCODE_GOOD;
  if (map == nullptr || server == nullptr)
    return retval;

  // Functions are not instantiated... they are just linked to the node.
  UA_BrowseDescription bDesc;
  UA_BrowseDescription_init(&bDesc);
  bDesc.nodeId = objectId;
  bDesc.browseDirection = UA_BROWSEDIRECTION_FORWARD;
  bDesc.includeSubtypes = UA_FALSE;
  bDesc.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
  bDesc.nodeClassMask = UA_NODECLASS_METHOD;
  bDesc.resultMask = UA_BROWSERESULTMASK_ALL;

  UA_BrowseResult bResp = UA_Server_browse(server, 65536, &bDesc);
  for (int i = 0; i<bResp.referencesSize; i++) {
    UA_ReferenceDescription ref =  bResp.references[i];

    // Recursive on objects
    if (ref.nodeClass == UA_NODECLASS_OBJECT && recursive != 0) {
        ua_callProxy_mapFunctions(server, ref.nodeId.nodeId, map, srcClass, recursive < 0 ? recursive : recursive-1); // Recursive==-1 is infinite, else adjust recursion count
        continue;
    }

    if (! (ref.nodeClass == UA_NODECLASS_METHOD))
      continue;

    // Check if we have this node in our map
    UA_FunctionCall_Map_Element *ele = nullptr;
    // cppcheck-suppress postfixOperator                REASON: List iterator cannot be prefixed
    for (std::list<UA_FunctionCall_Map_Element>::iterator j=map->begin(); j != map->end(); j++) {
      UA_FunctionCall_Map_Element thisEle = *(j);
      if(UA_NodeId_equal(&ref.nodeId.nodeId, (const UA_NodeId *) &thisEle.typeTemplateId) == UA_TRUE) {
        ele = &thisEle;
	break;
      }
    }
    if (ele == nullptr)
      continue;

    // Found a mapping: templateId <-> objectId /w functionpointer...
    UA_FunctionCall_InstanceLookUpTable *tbl =  ele->lookupTable;
    if (tbl == NULL)
      continue; // Only possible on wrong call, since the tables are allocated during compilation!

    // Register this objects Id with the callbacks lookup table
    UA_FunctionCall_InstanceLookupTable_Element *ntel = new UA_FunctionCall_InstanceLookupTable_Element;
    ntel->classInstance = srcClass;
    UA_NodeId_copy( &objectId, &ntel->classObjectId);
    ntel->server = server;
    ele->lookupTable->push_back(ntel);

    // I forgot what this was supposed to be doing - but it clearly was meant to ... eeee...?
    //     for (UA_FunctionCall_InstanceLookUpTable::iterator j=tbl->begin(); j != tbl->end(); j++)
    //       if(UA_NodeId_equal(&(*(j))->classObjectId, &objectId)) {
    //         void *a = &(*(j))->classInstance;
    //         break;
    //       }

    // Found a mapping, now register the callback; pass the server along (instance and object are identified in the table)
    retval |= UA_Server_setMethodNode_callback(server, (const UA_NodeId) ele->typeTemplateId, ele->callback, (void *) server);
  }

  return retval;
}

UA_StatusCode ua_callProxy_mapDataSources(UA_Server* server, nodePairList instantiatedNodesList, UA_DataSource_Map *map, void *srcClass)
{
  UA_StatusCode retval = UA_STATUSCODE_GOOD;
  if (map == nullptr || server == nullptr)
    return retval;

  // Functions are not instantiated... they are just linked to the node.
  for (nodePairList::iterator l = instantiatedNodesList.begin(); l != instantiatedNodesList.end(); ++l) {
    UA_NodeId typeTemplateId = (*l)->sourceNodeId;
    UA_NodeId instantiatedId = (*l)->targetNodeId;

    // Check if we have this node in our map
    UA_DataSource_Map_Element *ele = nullptr;
    // cppcheck-suppress postfixOperator                REASON: List iterator cannot be prefixed
    for (UA_DataSource_Map::iterator j=map->begin(); j != map->end(); j++) {
      UA_DataSource_Map_Element thisEle = *(j);
      if(UA_NodeId_equal(&typeTemplateId, (const UA_NodeId *) &thisEle.typeTemplateId) == UA_TRUE) {
        ele = new UA_DataSource_Map_Element;
        *ele = thisEle;
        break;
      }
    }
    if (ele == nullptr)
      continue;

    // Found a mapping, now register the dataSource
    UA_DataSource ds;
    ds.read = ele->read;
    ds.write = ele->write;
    ds.handle = srcClass;

    // Set accesslevel depending on callback functions
    UA_Byte accessLevel;
    if(ele->write == NULL && ele->read == NULL) {
        accessLevel = 0;
    }
    if(ele->write != NULL && ele->read == NULL) {
        accessLevel = UA_ACCESSLEVELMASK_WRITE;
    }
    if(ele->write != NULL && ele->read != NULL) {
        accessLevel = UA_ACCESSLEVELMASK_WRITE^UA_ACCESSLEVELMASK_READ;
    }
    if(ele->write == NULL && ele->read != NULL) {
        accessLevel = UA_ACCESSLEVELMASK_READ;
    }
    retval = UA_Server_writeAccessLevel(server, instantiatedId, accessLevel);
    // There is currently no high- level function to do this. (02.12.2016)
    retval = __UA_Server_write(server, &instantiatedId, UA_ATTRIBUTEID_USERACCESSLEVEL, &UA_TYPES[UA_TYPES_BYTE], &accessLevel);
    // add individual description for every variable
    UA_Server_writeDescription(server, instantiatedId, ele->description);
    delete ele; // inhibit memleak warning during static analysis

    retval |= UA_Server_setVariableNode_dataSource(server, (const UA_NodeId) instantiatedId, ds);

    /* Set the right Value Datatype and ValueRank
     * -> This is a quickfix for subjective data handling by open62541
     * (02.12.2016)
    */
    UA_NodeId datatTypeNodeId = UA_NODEID_NULL;
    retval = UA_Server_readDataType(server, instantiatedId, &datatTypeNodeId);
        if(retval == UA_STATUSCODE_GOOD) {
	    const UA_NodeId basedatatype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
	    UA_Variant variantVal;
	    UA_Variant_init(&variantVal);
	    retval = UA_Server_readValue(server, instantiatedId, &variantVal);
	    if(UA_NodeId_equal(&datatTypeNodeId, &basedatatype) && retval == UA_STATUSCODE_GOOD) {
	        retval = UA_Server_writeDataType(server, instantiatedId, variantVal.type->typeId);
		// See IEC 62541-3: OPC Unified Architecture - Part 3: Address space model -> Page 75
                UA_Int32 valueRank = -2;
		if(variantVal.arrayDimensionsSize == 0 && UA_Variant_isScalar(&variantVal)) {
		    // Scalar
		    valueRank = -1;
		}
		else {
		    if(variantVal.arrayDimensionsSize > 1) {
		        // OneOrMoreDimensions
		        valueRank = 0;
		    }
		    else {
		        if(variantVal.arrayDimensionsSize == 1 || variantVal.arrayLength) {
			    // OneDimension
			    valueRank = 1;
			}
		    }
		}
		UA_Server_writeValueRank(server, instantiatedId, valueRank);
	    }
        }
    }

  return retval;
}
<|endoftext|>


<commit_before>#include "uiAnalysis.h"

namespace bp=boost::python;
using namespace bp;

uiAnalysis::uiAnalysis(sqlite3 **db, uiFileDetailsTreeView* fileDetailsTree, bool compact, Settings *settings, Gtk::Window* parent)
{
	this->db = db;
	this->compact = compact;
	mp_FileDetailsTree = fileDetailsTree;
	m_parent = parent;
	this->settings = settings;

	initPlugins();

	Gtk::Toolbar *toolbar = Gtk::manage( new Gtk::Toolbar() );

	tbOpen = Gtk::manage( new Gtk::ToolButton(Gtk::Stock::OPEN) );
	tbRun = Gtk::manage( new Gtk::ToolButton(Gtk::Stock::EXECUTE) );
	tbRun->set_sensitive(false);
    toolbar->append(*tbOpen, sigc::mem_fun(*this, &uiAnalysis::on_open_clicked) );
    toolbar->append(*tbRun, sigc::mem_fun(*this, &uiAnalysis::on_run_clicked) );
	tbPlugins = Gtk::manage( new Gtk::ComboBoxText() );
	Gtk::ToolItem *tbPluginItem = Gtk::manage( new Gtk::ToolItem() );
	tbPluginItem->add(*tbPlugins);
	for (unsigned int i = 0; i < plugins.size(); ++i) {
		tbPlugins->append_text(plugins[i].second);
		if (i == 0) tbPlugins->set_active_text(plugins[i].second);
	}
	tbPlugins->signal_changed().connect(sigc::mem_fun(*this, &uiAnalysis::on_plugin_changed));

	toolbar->append(*tbPluginItem);
	tbPlugins->set_focus_on_click(false);
	this->pack_start(*toolbar, false, false);

	mp_plot = Gtk::manage( new EasyPlotmm() );
	Gtk::ScrolledWindow *swOutput = Gtk::manage( new Gtk::ScrolledWindow() );
	mrp_tbOutput = Gtk::TextBuffer::create();
	tvOutput = Gtk::manage( new Gtk::TextView(mrp_tbOutput) );
	tvOutput->set_editable(false);
	swOutput->add(*tvOutput);

	if (compact)
	{
		this->pack_start(*mp_plot);
	} else {
		Gtk::VPaned *vp = Gtk::manage( new Gtk::VPaned() );
		vp->pack1(*mp_plot);
		vp->pack2(*swOutput);
		this->pack_start(*vp);
	}

	if (compact)
	{
		toolbar->set_toolbar_style(Gtk::TOOLBAR_ICONS);
		toolbar->set_icon_size(Gtk::IconSize(Gtk::ICON_SIZE_SMALL_TOOLBAR));
	}

}

uiAnalysis::~uiAnalysis() {}

void uiAnalysis::initPlugins()
{
	/**
	 * Setup built-in plugins
	 */
	plugins.push_back(std::pair<Glib::ustring,Glib::ustring>("meanSpikeCount.py", "Mean Spike Count"));
	plugins.push_back(std::pair<Glib::ustring,Glib::ustring>("firstSpikeLatency.py", "First Spike Latency"));
	plugins.push_back(std::pair<Glib::ustring,Glib::ustring>("spikeProbability.py", "Spiking Probability"));
	plugins.push_back(std::pair<Glib::ustring,Glib::ustring>("", "Custom Analysis Script"));
}

void uiAnalysis::on_open_clicked()
{
	Gtk::FileChooserDialog dialog("Select the analysis script",
				      Gtk::FILE_CHOOSER_ACTION_OPEN);

	dialog.set_transient_for(*m_parent);

	// Set default folder
	dialog.set_current_folder(settings->get_string("lastScriptFolder", "."));

	// Add response buttons to the dialog:
	dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
	dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);

	// Show the dialog
	int result = dialog.run();

	switch (result) {
	case (Gtk::RESPONSE_OK):
		std::string filename = dialog.get_filename();
		settings->set("lastScriptFolder", Glib::path_get_dirname(filename));
		tbRun->set_sensitive(true);
		m_filename = filename;
		tbPlugins->set_active_text("Custom Analysis Script");
		break;
	}
}

void uiAnalysis::on_run_clicked()
{
	runScript();
}

void uiAnalysis::on_plugin_changed()
{
	if (compact) runPlugin();
}

void uiAnalysis::addOutput(Glib::ustring t)
{
	mrp_tbOutput->insert(mrp_tbOutput->end(), t);
	while (Gtk::Main::events_pending()) {
    	Gtk::Main::iteration();
	}
}

void uiAnalysis::runPlugin()
{
	runScript(plugins[tbPlugins->get_active_row_number()].first);
}

void uiAnalysis::runScript(const Glib::ustring &plugin)
{
	//TODO: Check if we have a valid filename
	if (m_filename == "" && plugin == "") return;

	mrp_tbOutput->set_text("*** Initializing Analysis Plugin Library ***\n");
	addOutput("Using Script: ");
	addOutput(m_filename);
	addOutput("\n");

	Py_Initialize();

	bp::object main_module((
		bp::handle<>(bp::borrowed(PyImport_AddModule("__main__")))));
	bp::object main_namespace = main_module.attr("__dict__");

	main_namespace["pySpikeDB"] = class_<pySpikeDB>("pySpikeDB")
		.def("getCells", &pySpikeDB::getCells)
		.def("getFiles", &pySpikeDB::getFiles)
		.def("write", &pySpikeDB::print)
		.def("mean", &pySpikeDB::mean)
		.def("stddev", &pySpikeDB::stddev)
		.def("plotClear", &pySpikeDB::plotClear)
		.def("plotSetRGBA", &pySpikeDB::plotSetRGBA)
		.def("plotSetPointSize", &pySpikeDB::plotSetPointSize)
		.def("plotSetLineWidth", &pySpikeDB::plotSetLineWidth)
		.def("plotLine", &pySpikeDB::plotLine);
	pySpikeDB _pySpikeDB(db, mp_FileDetailsTree, mp_plot, mrp_tbOutput);
	main_namespace["SpikeDB"] = bp::ptr(&_pySpikeDB);
	main_namespace["SpikeDB"].attr("__dict__")["VARYING"] = VARYING_STIMULUS;

	// Redirect stderr and stdout
	main_namespace["pySpikeDBWrite"] = class_<pySpikeDB>("pySpikeDBWrite")
		.def("write", &pySpikeDB::print);
	pySpikeDB _pySpikeDBWrite(db, mp_FileDetailsTree, mp_plot, mrp_tbOutput);
	main_namespace["SpikeDBWrite"] = bp::ptr(&_pySpikeDBWrite);
	PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.stderr = SpikeDBWrite");
	PyRun_SimpleString("sys.stdout = SpikeDBWrite");

	addOutput("*** Running Analysis Plugin ***\n\n");

	// Open the file and run the code
	FILE *fp;
	if (plugin == "") {
		fp = fopen(m_filename.c_str(), "r");
	} else {
		Glib::ustring plugin_path = "plugins/"+plugin;
		fp = fopen(plugin_path.c_str(), "r");
	}
	PyRun_SimpleFile(fp, m_filename.c_str());
	fclose(fp);

	addOutput("\n*** Analysis Plugin Completed ***");
}

void uiAnalysis::print(const std::string &s)
{
	addOutput(s);
}

EasyPlotmm* uiAnalysis::getPlot()
{
	return mp_plot;
}
<commit_msg>Minor bug fix<commit_after>#include "uiAnalysis.h"

namespace bp=boost::python;
using namespace bp;

uiAnalysis::uiAnalysis(sqlite3 **db, uiFileDetailsTreeView* fileDetailsTree, bool compact, Settings *settings, Gtk::Window* parent)
{
	this->db = db;
	this->compact = compact;
	mp_FileDetailsTree = fileDetailsTree;
	m_parent = parent;
	this->settings = settings;

	initPlugins();

	Gtk::Toolbar *toolbar = Gtk::manage( new Gtk::Toolbar() );

	tbOpen = Gtk::manage( new Gtk::ToolButton(Gtk::Stock::OPEN) );
	tbRun = Gtk::manage( new Gtk::ToolButton(Gtk::Stock::EXECUTE) );
	tbRun->set_sensitive(false);
    toolbar->append(*tbOpen, sigc::mem_fun(*this, &uiAnalysis::on_open_clicked) );
    toolbar->append(*tbRun, sigc::mem_fun(*this, &uiAnalysis::on_run_clicked) );
	tbPlugins = Gtk::manage( new Gtk::ComboBoxText() );
	Gtk::ToolItem *tbPluginItem = Gtk::manage( new Gtk::ToolItem() );
	tbPluginItem->add(*tbPlugins);
	for (unsigned int i = 0; i < plugins.size(); ++i) {
		tbPlugins->append_text(plugins[i].second);
		if (i == 1) tbPlugins->set_active_text(plugins[i].second);
	}
	tbPlugins->signal_changed().connect(sigc::mem_fun(*this, &uiAnalysis::on_plugin_changed));

	toolbar->append(*tbPluginItem);
	tbPlugins->set_focus_on_click(false);
	this->pack_start(*toolbar, false, false);

	mp_plot = Gtk::manage( new EasyPlotmm() );
	Gtk::ScrolledWindow *swOutput = Gtk::manage( new Gtk::ScrolledWindow() );
	mrp_tbOutput = Gtk::TextBuffer::create();
	tvOutput = Gtk::manage( new Gtk::TextView(mrp_tbOutput) );
	tvOutput->set_editable(false);
	swOutput->add(*tvOutput);

	if (compact)
	{
		this->pack_start(*mp_plot);
	} else {
		Gtk::VPaned *vp = Gtk::manage( new Gtk::VPaned() );
		vp->pack1(*mp_plot);
		vp->pack2(*swOutput);
		this->pack_start(*vp);
	}

	if (compact)
	{
		toolbar->set_toolbar_style(Gtk::TOOLBAR_ICONS);
		toolbar->set_icon_size(Gtk::IconSize(Gtk::ICON_SIZE_SMALL_TOOLBAR));
	}

}

uiAnalysis::~uiAnalysis() {}

void uiAnalysis::initPlugins()
{
	/**
	 * Setup built-in plugins
	 */
	plugins.push_back(std::pair<Glib::ustring,Glib::ustring>("", "Custom Analysis Script"));
	plugins.push_back(std::pair<Glib::ustring,Glib::ustring>("meanSpikeCount.py", "Mean Spike Count"));
	plugins.push_back(std::pair<Glib::ustring,Glib::ustring>("firstSpikeLatency.py", "First Spike Latency"));
	plugins.push_back(std::pair<Glib::ustring,Glib::ustring>("spikeProbability.py", "Spiking Probability"));
}

void uiAnalysis::on_open_clicked()
{
	Gtk::FileChooserDialog dialog("Select the analysis script",
				      Gtk::FILE_CHOOSER_ACTION_OPEN);

	dialog.set_transient_for(*m_parent);

	// Set default folder
	dialog.set_current_folder(settings->get_string("lastScriptFolder", "."));

	// Add response buttons to the dialog:
	dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
	dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);

	// Show the dialog
	int result = dialog.run();

	switch (result) {
	case (Gtk::RESPONSE_OK):
		std::string filename = dialog.get_filename();
		settings->set("lastScriptFolder", Glib::path_get_dirname(filename));
		tbRun->set_sensitive(true);
		m_filename = filename;
		tbPlugins->set_active_text("Custom Analysis Script");
		break;
	}
}

void uiAnalysis::on_run_clicked()
{
	runScript();
}

void uiAnalysis::on_plugin_changed()
{
	runPlugin();
}

void uiAnalysis::addOutput(Glib::ustring t)
{
	mrp_tbOutput->insert(mrp_tbOutput->end(), t);
	while (Gtk::Main::events_pending()) {
    	Gtk::Main::iteration();
	}
}

void uiAnalysis::runPlugin()
{
	runScript(plugins[tbPlugins->get_active_row_number()].first);
}

void uiAnalysis::runScript(const Glib::ustring &plugin)
{
	//TODO: Check if we have a valid filename
	if (m_filename == "" && plugin == "") return;

	mrp_tbOutput->set_text("*** Initializing Analysis Plugin Library ***\n");
	addOutput("Using Script: ");
	addOutput(m_filename);
	addOutput("\n");

	Py_Initialize();

	bp::object main_module((
		bp::handle<>(bp::borrowed(PyImport_AddModule("__main__")))));
	bp::object main_namespace = main_module.attr("__dict__");

	main_namespace["pySpikeDB"] = class_<pySpikeDB>("pySpikeDB")
		.def("getCells", &pySpikeDB::getCells)
		.def("getFiles", &pySpikeDB::getFiles)
		.def("write", &pySpikeDB::print)
		.def("mean", &pySpikeDB::mean)
		.def("stddev", &pySpikeDB::stddev)
		.def("plotClear", &pySpikeDB::plotClear)
		.def("plotSetRGBA", &pySpikeDB::plotSetRGBA)
		.def("plotSetPointSize", &pySpikeDB::plotSetPointSize)
		.def("plotSetLineWidth", &pySpikeDB::plotSetLineWidth)
		.def("plotLine", &pySpikeDB::plotLine);
	pySpikeDB _pySpikeDB(db, mp_FileDetailsTree, mp_plot, mrp_tbOutput);
	main_namespace["SpikeDB"] = bp::ptr(&_pySpikeDB);
	main_namespace["SpikeDB"].attr("__dict__")["VARYING"] = VARYING_STIMULUS;

	// Redirect stderr and stdout
	main_namespace["pySpikeDBWrite"] = class_<pySpikeDB>("pySpikeDBWrite")
		.def("write", &pySpikeDB::print);
	pySpikeDB _pySpikeDBWrite(db, mp_FileDetailsTree, mp_plot, mrp_tbOutput);
	main_namespace["SpikeDBWrite"] = bp::ptr(&_pySpikeDBWrite);
	PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.stderr = SpikeDBWrite");
	PyRun_SimpleString("sys.stdout = SpikeDBWrite");

	addOutput("*** Running Analysis Plugin ***\n\n");

	// Open the file and run the code
	FILE *fp;
	if (plugin == "") {
		fp = fopen(m_filename.c_str(), "r");
	} else {
		Glib::ustring plugin_path = "plugins/"+plugin;
		fp = fopen(plugin_path.c_str(), "r");
	}
	PyRun_SimpleFile(fp, m_filename.c_str());
	fclose(fp);

	addOutput("\n*** Analysis Plugin Completed ***");
}

void uiAnalysis::print(const std::string &s)
{
	addOutput(s);
}

EasyPlotmm* uiAnalysis::getPlot()
{
	return mp_plot;
}
<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org)             */
/*                                                                            */
/* 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.                                             */
/* -------------------------------------------------------------------------- */

/* ************************************************************************** */
/* User Pool                                            	 	      */
/* ************************************************************************** */

#include "UserPool.h"
#include "NebulaLog.h"
#include "Nebula.h"
#include "AuthManager.h"
#include "SSLTools.h"

#include <fstream>
#include <sys/types.h>
#include <pwd.h>
#include <stdlib.h>

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

UserPool::UserPool(SqlDB * db):PoolSQL(db,User::table)
{
    int           one_uid = -1;
    ostringstream oss;
    string        one_token;
    string        one_name;
    string        one_pass;
    string        one_auth_file;

    const char *  one_auth;
    ifstream      file;

    if (get(0,false) != 0)
    {
        return;
    }

    // User oneadmin needs to be added in the bootstrap
    one_auth = getenv("ONE_AUTH");

    if (!one_auth)
    {
        struct passwd * pw_ent;

        pw_ent = getpwuid(getuid());

        if ((pw_ent != NULL) && (pw_ent->pw_dir != NULL))
        {
            one_auth_file = pw_ent->pw_dir;
            one_auth_file += "/.one/one_auth";

            one_auth = one_auth_file.c_str();
        }
        else
        {
            oss << "Could not get one_auth file location";
        }
    }

    file.open(one_auth);

    if (file.good())
    {
        getline(file,one_token);

        if (file.fail())
        {
            oss << "Error reading file: " << one_auth;
        }
        else
        {
            if (User::split_secret(one_token,one_name,one_pass) == 0)
            {
                string error_str;
                string sha1_pass = SSLTools::sha1_digest(one_pass);

                allocate(&one_uid,
                         GroupPool::ONEADMIN_ID,
                         one_name,
                         GroupPool::ONEADMIN_NAME,
                         sha1_pass,
                         true,
                         error_str);
            }
            else
            {
                oss << "Wrong format must be <username>:<password>";
            }
        }
    }
    else
    {
        oss << "Cloud not open file: " << one_auth;
    }

    file.close();

    if (one_uid != 0)
    {
        NebulaLog::log("ONE",Log::ERROR,oss);
        throw;
    }
}

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

int UserPool::allocate (
    int *   oid,
    int     gid,
    const   string&  uname,
    const   string&  gname,
    const   string&  password,
    bool    enabled,
    string& error_str)
{
    Nebula&     nd    = Nebula::instance();

    User *      user;
    GroupPool * gpool;
    Group *     group;

    ostringstream   oss;

    if ( uname.empty() )
    {
        goto error_name;
    }

    user = get(uname,false);

    if ( user !=0 )
    {
        goto error_duplicated;
    }

    // Build a new User object
    user = new User(-1, gid, uname, gname, password, enabled);

    // Insert the Object in the pool
    *oid = PoolSQL::allocate(user, error_str);

    if ( *oid < 0 )
    {
        return *oid;
    }

    // Adds User to group
    gpool = nd.get_gpool();
    group = gpool->get(gid, true);

    if( group == 0 )
    {
        return -1;
    }

    group->add_user(*oid);

    gpool->update(group);

    group->unlock();

    return *oid;

error_name:
    oss << "NAME cannot be empty.";
    goto error_common;

error_duplicated:
    oss << "NAME is already taken by USER " << user->get_oid() << ".";
    goto error_common;

error_common:
    *oid = -1;
    error_str = oss.str();

    return *oid;
}

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

bool UserPool::authenticate(const string& session,
                            int&          user_id,
                            int&          group_id,
                            string&       uname,
                            string&       gname)
{
    map<string, int>::iterator index;

    User * user = 0;
    string username;
    string secret, u_pass;

    string tuname;
    string tgname;

    int  uid, gid;
    int  rc;
    bool result;

    Nebula&     nd      = Nebula::instance();
    AuthManager * authm = nd.get_authm();

    user_id  = -1;
    group_id = -1;
    uname    = "";
    gname    = "";
    result   = false;

    rc = User::split_secret(session,username,secret);

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

    user = get(username,true);

    if (user != 0) //User known to OpenNebula
    {
        u_pass = user->password;
        uid    = user->oid;
        gid    = user->gid;

        tuname  = user->name;
        tgname  = user->gname;

        user->unlock();
    }
    else //External User
    {
        u_pass = "-";
        uid    = -1;
        gid    = -1;
    }

    AuthRequest ar(uid, gid);

    ar.add_authenticate(username,u_pass,secret);

    if ( uid == 0 ) //oneadmin
    {
        if (ar.plain_authenticate())
        {
            user_id  = uid;
            group_id = gid;

            uname = tuname;
            gname = tgname;

            result   = true;
        }
    }
    else if (authm == 0) //plain auth
    {
        if ( user != 0 && ar.plain_authenticate()) //no plain for external users
        {
            user_id  = uid;
            group_id = gid;

            uname = tuname;
            gname = tgname;

            result   = true;
        }
    }
    else //use the driver
    {
        authm->trigger(AuthManager::AUTHENTICATE,&ar);
        ar.wait();

        if (ar.result==true)
        {
            if ( user != 0 ) //knwon user_id
            {
                user_id  = uid;
                group_id = gid;

                uname = tuname;
                gname = tgname;

                result   = true;
            }
            else //External user, username & pass in driver message
            {
                string mad_name;
                string mad_pass;
                string error_str;

                istringstream is(ar.message);

                if ( is.good() )
                {
                    is >> mad_name >> ws >> mad_pass;
                }

                if ( !is.fail() )
                {
                    allocate(&user_id,
                             GroupPool::USERS_ID,
                             mad_name,
                             GroupPool::USERS_NAME,
                             mad_pass,
                             true,
                             error_str);
                }

                if ( user_id == -1 )
                {
                    ostringstream oss;

                    oss << "Can't create user: " << error_str <<
                           ". Driver response: " << ar.message;

                    ar.message = oss.str();
                }
                else
                {
                    group_id = GroupPool::USERS_ID;

                    uname = mad_name;
                    gname = GroupPool::USERS_NAME;

                    result   = true;
                }
            }
        }
        else
        {
            ostringstream oss;
            oss << "Auth Error: " << ar.message;

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

    return result;
}

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

int UserPool::authorize(AuthRequest& ar)
{
    Nebula&       nd    = Nebula::instance();
    AuthManager * authm = nd.get_authm();
    int           rc    = -1;

    if (authm == 0)
    {
        if (ar.plain_authorize())
        {
            rc = 0;
        }
    }
    else
    {
        authm->trigger(AuthManager::AUTHORIZE,&ar);
        ar.wait();

        if (ar.result==true)
        {
            rc = 0;
        }
        else
        {
            ostringstream oss;
            oss << "Auth Error: " << ar.message;

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

    return rc;
}

<commit_msg>Remove restriction for plain auth for oneadmin. (cherry picked from commit ff05d07af0231ae4a2e8a0915973c05f9b533409)<commit_after>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org)             */
/*                                                                            */
/* 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.                                             */
/* -------------------------------------------------------------------------- */

/* ************************************************************************** */
/* User Pool                                            	 	      */
/* ************************************************************************** */

#include "UserPool.h"
#include "NebulaLog.h"
#include "Nebula.h"
#include "AuthManager.h"
#include "SSLTools.h"

#include <fstream>
#include <sys/types.h>
#include <pwd.h>
#include <stdlib.h>

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

UserPool::UserPool(SqlDB * db):PoolSQL(db,User::table)
{
    int           one_uid = -1;
    ostringstream oss;
    string        one_token;
    string        one_name;
    string        one_pass;
    string        one_auth_file;

    const char *  one_auth;
    ifstream      file;

    if (get(0,false) != 0)
    {
        return;
    }

    // User oneadmin needs to be added in the bootstrap
    one_auth = getenv("ONE_AUTH");

    if (!one_auth)
    {
        struct passwd * pw_ent;

        pw_ent = getpwuid(getuid());

        if ((pw_ent != NULL) && (pw_ent->pw_dir != NULL))
        {
            one_auth_file = pw_ent->pw_dir;
            one_auth_file += "/.one/one_auth";

            one_auth = one_auth_file.c_str();
        }
        else
        {
            oss << "Could not get one_auth file location";
        }
    }

    file.open(one_auth);

    if (file.good())
    {
        getline(file,one_token);

        if (file.fail())
        {
            oss << "Error reading file: " << one_auth;
        }
        else
        {
            if (User::split_secret(one_token,one_name,one_pass) == 0)
            {
                string error_str;
                string sha1_pass = SSLTools::sha1_digest(one_pass);

                allocate(&one_uid,
                         GroupPool::ONEADMIN_ID,
                         one_name,
                         GroupPool::ONEADMIN_NAME,
                         sha1_pass,
                         true,
                         error_str);
            }
            else
            {
                oss << "Wrong format must be <username>:<password>";
            }
        }
    }
    else
    {
        oss << "Cloud not open file: " << one_auth;
    }

    file.close();

    if (one_uid != 0)
    {
        NebulaLog::log("ONE",Log::ERROR,oss);
        throw;
    }
}

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

int UserPool::allocate (
    int *   oid,
    int     gid,
    const   string&  uname,
    const   string&  gname,
    const   string&  password,
    bool    enabled,
    string& error_str)
{
    Nebula&     nd    = Nebula::instance();

    User *      user;
    GroupPool * gpool;
    Group *     group;

    ostringstream   oss;

    if ( uname.empty() )
    {
        goto error_name;
    }

    user = get(uname,false);

    if ( user !=0 )
    {
        goto error_duplicated;
    }

    // Build a new User object
    user = new User(-1, gid, uname, gname, password, enabled);

    // Insert the Object in the pool
    *oid = PoolSQL::allocate(user, error_str);

    if ( *oid < 0 )
    {
        return *oid;
    }

    // Adds User to group
    gpool = nd.get_gpool();
    group = gpool->get(gid, true);

    if( group == 0 )
    {
        return -1;
    }

    group->add_user(*oid);

    gpool->update(group);

    group->unlock();

    return *oid;

error_name:
    oss << "NAME cannot be empty.";
    goto error_common;

error_duplicated:
    oss << "NAME is already taken by USER " << user->get_oid() << ".";
    goto error_common;

error_common:
    *oid = -1;
    error_str = oss.str();

    return *oid;
}

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

bool UserPool::authenticate(const string& session,
                            int&          user_id,
                            int&          group_id,
                            string&       uname,
                            string&       gname)
{
    map<string, int>::iterator index;

    User * user = 0;
    string username;
    string secret, u_pass;

    string tuname;
    string tgname;

    int  uid, gid;
    int  rc;
    bool result;

    Nebula&     nd      = Nebula::instance();
    AuthManager * authm = nd.get_authm();

    user_id  = -1;
    group_id = -1;
    uname    = "";
    gname    = "";
    result   = false;

    rc = User::split_secret(session,username,secret);

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

    user = get(username,true);

    if (user != 0) //User known to OpenNebula
    {
        u_pass = user->password;
        uid    = user->oid;
        gid    = user->gid;

        tuname  = user->name;
        tgname  = user->gname;

        user->unlock();
    }
    else //External User
    {
        u_pass = "-";
        uid    = -1;
        gid    = -1;
    }

    AuthRequest ar(uid, gid);

    NebulaLog::log("UserPool",Log::ERROR, "Authenticating " + username);

    ar.add_authenticate(username,u_pass,secret);

    if (authm == 0) //plain auth
    {
        if ( user != 0 && ar.plain_authenticate()) //no plain for external users
        {
            user_id  = uid;
            group_id = gid;

            uname = tuname;
            gname = tgname;

            result   = true;
        }
    }
    else //use the driver
    {
        authm->trigger(AuthManager::AUTHENTICATE,&ar);
        ar.wait();

        if (ar.result==true)
        {
            if ( user != 0 ) //knwon user_id
            {
                user_id  = uid;
                group_id = gid;

                uname = tuname;
                gname = tgname;

                result   = true;
            }
            else //External user, username & pass in driver message
            {
                string mad_name;
                string mad_pass;
                string error_str;

                istringstream is(ar.message);

                if ( is.good() )
                {
                    is >> mad_name >> ws >> mad_pass;
                }

                if ( !is.fail() )
                {
                    allocate(&user_id,
                             GroupPool::USERS_ID,
                             mad_name,
                             GroupPool::USERS_NAME,
                             mad_pass,
                             true,
                             error_str);
                }

                if ( user_id == -1 )
                {
                    ostringstream oss;

                    oss << "Can't create user: " << error_str <<
                           ". Driver response: " << ar.message;

                    ar.message = oss.str();
                }
                else
                {
                    group_id = GroupPool::USERS_ID;

                    uname = mad_name;
                    gname = GroupPool::USERS_NAME;

                    result   = true;
                }
            }
        }
        else
        {
            ostringstream oss;
            oss << "Auth Error: " << ar.message;

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

    return result;
}

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

int UserPool::authorize(AuthRequest& ar)
{
    Nebula&       nd    = Nebula::instance();
    AuthManager * authm = nd.get_authm();
    int           rc    = -1;

    if (authm == 0)
    {
        if (ar.plain_authorize())
        {
            rc = 0;
        }
    }
    else
    {
        authm->trigger(AuthManager::AUTHORIZE,&ar);
        ar.wait();

        if (ar.result==true)
        {
            rc = 0;
        }
        else
        {
            ostringstream oss;
            oss << "Auth Error: " << ar.message;

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

    return rc;
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2010, Joshua Lackey
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     *  Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *
 *     *  Redistributions in binary form must reproduce the above copyright
 *        notice, this list of conditions and the following disclaimer in the
 *        documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <math.h>
#include <complex>

#include "usrp_source.h"

extern int g_verbosity;

usrp_source::usrp_source(float sample_rate, long int fpga_master_clock_freq) {

	m_fpga_master_clock_freq = fpga_master_clock_freq;
	m_desired_sample_rate = sample_rate;
	m_sample_rate = 0.0;
	m_decimation = 0;
	m_cb = new circular_buffer(CB_LEN, sizeof(complex), 0);

	pthread_mutex_init(&m_u_mutex, 0);
}

usrp_source::usrp_source(unsigned int decimation, long int fpga_master_clock_freq) {

	m_fpga_master_clock_freq = fpga_master_clock_freq;
	m_sample_rate = 0.0;
	m_cb = new circular_buffer(CB_LEN, sizeof(complex), 0);

	pthread_mutex_init(&m_u_mutex, 0);

	m_decimation = decimation & ~1;
	if(m_decimation < 4)
		m_decimation = 4;
	if(m_decimation > 256)
		m_decimation = 256;
}

usrp_source::~usrp_source() {

	stop();
	delete m_cb;
	rtlsdr_close(dev);
	pthread_mutex_destroy(&m_u_mutex);
}

void usrp_source::stop() {

	pthread_mutex_lock(&m_u_mutex);
	pthread_mutex_unlock(&m_u_mutex);
}

void usrp_source::start() {

	pthread_mutex_lock(&m_u_mutex);
	pthread_mutex_unlock(&m_u_mutex);
}

void usrp_source::calculate_decimation() {

	float decimation_f;

//	decimation_f = (float)m_u_rx->fpga_master_clock_freq() / m_desired_sample_rate;
	m_decimation = (unsigned int)round(decimation_f) & ~1;

	if(m_decimation < 4)
		m_decimation = 4;
	if(m_decimation > 256)
		m_decimation = 256;
}

float usrp_source::sample_rate() {

	return m_sample_rate;

}

int usrp_source::tune(double freq) {

	int r = 0;

	pthread_mutex_lock(&m_u_mutex);
	if (freq != m_center_freq) {
		r = rtlsdr_set_center_freq(dev, (uint32_t)freq);
//		fprintf(stderr, "Tuned to %i Hz.\n", (uint32_t)freq);
		m_center_freq = freq;
	}

	pthread_mutex_unlock(&m_u_mutex);

	return (r < 0) ? 0 : 1;
}

int usrp_source::set_freq_correction(int ppm) {
	m_freq_corr = ppm;
	return rtlsdr_set_freq_correction(dev, ppm);
}

bool usrp_source::set_antenna(int antenna) {

	return 0;
}

bool usrp_source::set_gain(float gain) {
	int r, g = gain * 10;

	/* Enable manual gain */
	r = rtlsdr_set_tuner_gain_mode(dev, 1);
	if (r < 0)
		fprintf(stderr, "WARNING: Failed to enable manual gain.\n");

	fprintf(stderr, "Setting gain: %.1f dB\n", gain/10);
	r = rtlsdr_set_tuner_gain(dev, g);

	return (r < 0) ? 0 : 1;
}

/*
 * open() should be called before multiple threads access usrp_source.
 */
int usrp_source::open(unsigned int subdev) {
	int i, r, device_count, count;
	uint32_t dev_index = subdev;
	uint32_t samp_rate = 270833;

	m_sample_rate = 270833.002142;

	device_count = rtlsdr_get_device_count();
	if (!device_count) {
		fprintf(stderr, "No supported devices found.\n");
		exit(1);
	}

	fprintf(stderr, "Found %d device(s):\n", device_count);
	for (i = 0; i < device_count; i++)
		fprintf(stderr, "  %d:  %s\n", i, rtlsdr_get_device_name(i));
	fprintf(stderr, "\n");

	fprintf(stderr, "Using device %d: %s\n",
		dev_index,
		rtlsdr_get_device_name(dev_index));

	r = rtlsdr_open(&dev, dev_index);
	if (r < 0) {
		fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index);
		exit(1);
	}

	/* Set the sample rate */
	r = rtlsdr_set_sample_rate(dev, samp_rate);
	if (r < 0)
		fprintf(stderr, "WARNING: Failed to set sample rate.\n");

	/* Reset endpoint before we start reading from it (mandatory) */
	r = rtlsdr_reset_buffer(dev);
	if (r < 0)
		fprintf(stderr, "WARNING: Failed to reset buffers.\n");

//	r = rtlsdr_set_offset_tuning(dev, 1);
//	if (r < 0)
//		fprintf(stderr, "WARNING: Failed to enable offset tuning\n");

	return 0;
}

#define USB_PACKET_SIZE		(2 * 16384)
#define FLUSH_SIZE		512

int usrp_source::fill(unsigned int num_samples, unsigned int *overrun_i) {

	bool overrun;
	unsigned char ubuf[USB_PACKET_SIZE];
	unsigned int i, j, space, overruns = 0;
	complex *c;
	int n_read;

	while((m_cb->data_available() < num_samples) && (m_cb->space_available() > 0)) {

		// read one usb packet from the usrp
		pthread_mutex_lock(&m_u_mutex);

		if (rtlsdr_read_sync(dev, ubuf, sizeof(ubuf), &n_read) < 0) {
			pthread_mutex_unlock(&m_u_mutex);
			fprintf(stderr, "error: usrp_standard_rx::read\n");
			return -1;
		}

		pthread_mutex_unlock(&m_u_mutex);
		if(overrun)
			overruns++;

		// write complex<short> input to complex<float> output
		c = (complex *)m_cb->poke(&space);

		// set space to number of complex items to copy
		space = n_read / 2;

		// write data
		for(i = 0, j = 0; i < space; i += 1, j += 2)
			c[i] = complex((ubuf[j] - 127) * 256, (ubuf[j + 1] - 127) * 256);

		// update cb
		m_cb->wrote(i);
	}

	// if the cb is full, we left behind data from the usb packet
	if(m_cb->space_available() == 0) {
		fprintf(stderr, "warning: local overrun\n");
		overruns++;
	}

	if(overrun_i)
		*overrun_i = overruns;

	return 0;
}

int usrp_source::read(complex *buf, unsigned int num_samples,
   unsigned int *samples_read) {

	unsigned int n;

	if(fill(num_samples, 0))
		return -1;

	n = m_cb->read(buf, num_samples);

	if(samples_read)
		*samples_read = n;

	return 0;
}

/*
 * Don't hold a lock on this and use the usrp at the same time.
 */
circular_buffer *usrp_source::get_buffer() {

	return m_cb;
}

int usrp_source::flush(unsigned int flush_count) {

	m_cb->flush();
	fill(flush_count * FLUSH_SIZE, 0);
	m_cb->flush();

	return 0;
}
<commit_msg>remove unused, uninitialized overrun-flag<commit_after>/*
 * Copyright (c) 2010, Joshua Lackey
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     *  Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *
 *     *  Redistributions in binary form must reproduce the above copyright
 *        notice, this list of conditions and the following disclaimer in the
 *        documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <math.h>
#include <complex>

#include "usrp_source.h"

extern int g_verbosity;

usrp_source::usrp_source(float sample_rate, long int fpga_master_clock_freq) {

	m_fpga_master_clock_freq = fpga_master_clock_freq;
	m_desired_sample_rate = sample_rate;
	m_sample_rate = 0.0;
	m_decimation = 0;
	m_cb = new circular_buffer(CB_LEN, sizeof(complex), 0);

	pthread_mutex_init(&m_u_mutex, 0);
}

usrp_source::usrp_source(unsigned int decimation, long int fpga_master_clock_freq) {

	m_fpga_master_clock_freq = fpga_master_clock_freq;
	m_sample_rate = 0.0;
	m_cb = new circular_buffer(CB_LEN, sizeof(complex), 0);

	pthread_mutex_init(&m_u_mutex, 0);

	m_decimation = decimation & ~1;
	if(m_decimation < 4)
		m_decimation = 4;
	if(m_decimation > 256)
		m_decimation = 256;
}

usrp_source::~usrp_source() {

	stop();
	delete m_cb;
	rtlsdr_close(dev);
	pthread_mutex_destroy(&m_u_mutex);
}

void usrp_source::stop() {

	pthread_mutex_lock(&m_u_mutex);
	pthread_mutex_unlock(&m_u_mutex);
}

void usrp_source::start() {

	pthread_mutex_lock(&m_u_mutex);
	pthread_mutex_unlock(&m_u_mutex);
}

void usrp_source::calculate_decimation() {

	float decimation_f;

//	decimation_f = (float)m_u_rx->fpga_master_clock_freq() / m_desired_sample_rate;
	m_decimation = (unsigned int)round(decimation_f) & ~1;

	if(m_decimation < 4)
		m_decimation = 4;
	if(m_decimation > 256)
		m_decimation = 256;
}

float usrp_source::sample_rate() {

	return m_sample_rate;

}

int usrp_source::tune(double freq) {

	int r = 0;

	pthread_mutex_lock(&m_u_mutex);
	if (freq != m_center_freq) {
		r = rtlsdr_set_center_freq(dev, (uint32_t)freq);
//		fprintf(stderr, "Tuned to %i Hz.\n", (uint32_t)freq);
		m_center_freq = freq;
	}

	pthread_mutex_unlock(&m_u_mutex);

	return (r < 0) ? 0 : 1;
}

int usrp_source::set_freq_correction(int ppm) {
	m_freq_corr = ppm;
	return rtlsdr_set_freq_correction(dev, ppm);
}

bool usrp_source::set_antenna(int antenna) {

	return 0;
}

bool usrp_source::set_gain(float gain) {
	int r, g = gain * 10;

	/* Enable manual gain */
	r = rtlsdr_set_tuner_gain_mode(dev, 1);
	if (r < 0)
		fprintf(stderr, "WARNING: Failed to enable manual gain.\n");

	fprintf(stderr, "Setting gain: %.1f dB\n", gain/10);
	r = rtlsdr_set_tuner_gain(dev, g);

	return (r < 0) ? 0 : 1;
}

/*
 * open() should be called before multiple threads access usrp_source.
 */
int usrp_source::open(unsigned int subdev) {
	int i, r, device_count, count;
	uint32_t dev_index = subdev;
	uint32_t samp_rate = 270833;

	m_sample_rate = 270833.002142;

	device_count = rtlsdr_get_device_count();
	if (!device_count) {
		fprintf(stderr, "No supported devices found.\n");
		exit(1);
	}

	fprintf(stderr, "Found %d device(s):\n", device_count);
	for (i = 0; i < device_count; i++)
		fprintf(stderr, "  %d:  %s\n", i, rtlsdr_get_device_name(i));
	fprintf(stderr, "\n");

	fprintf(stderr, "Using device %d: %s\n",
		dev_index,
		rtlsdr_get_device_name(dev_index));

	r = rtlsdr_open(&dev, dev_index);
	if (r < 0) {
		fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index);
		exit(1);
	}

	/* Set the sample rate */
	r = rtlsdr_set_sample_rate(dev, samp_rate);
	if (r < 0)
		fprintf(stderr, "WARNING: Failed to set sample rate.\n");

	/* Reset endpoint before we start reading from it (mandatory) */
	r = rtlsdr_reset_buffer(dev);
	if (r < 0)
		fprintf(stderr, "WARNING: Failed to reset buffers.\n");

//	r = rtlsdr_set_offset_tuning(dev, 1);
//	if (r < 0)
//		fprintf(stderr, "WARNING: Failed to enable offset tuning\n");

	return 0;
}

#define USB_PACKET_SIZE		(2 * 16384)
#define FLUSH_SIZE		512

int usrp_source::fill(unsigned int num_samples, unsigned int *overrun_i) {

	unsigned char ubuf[USB_PACKET_SIZE];
	unsigned int i, j, space, overruns = 0;
	complex *c;
	int n_read;

	while((m_cb->data_available() < num_samples) && (m_cb->space_available() > 0)) {

		// read one usb packet from the usrp
		pthread_mutex_lock(&m_u_mutex);

		if (rtlsdr_read_sync(dev, ubuf, sizeof(ubuf), &n_read) < 0) {
			pthread_mutex_unlock(&m_u_mutex);
			fprintf(stderr, "error: usrp_standard_rx::read\n");
			return -1;
		}

		pthread_mutex_unlock(&m_u_mutex);

		// write complex<short> input to complex<float> output
		c = (complex *)m_cb->poke(&space);

		// set space to number of complex items to copy
		space = n_read / 2;

		// write data
		for(i = 0, j = 0; i < space; i += 1, j += 2)
			c[i] = complex((ubuf[j] - 127) * 256, (ubuf[j + 1] - 127) * 256);

		// update cb
		m_cb->wrote(i);
	}

	// if the cb is full, we left behind data from the usb packet
	if(m_cb->space_available() == 0) {
		fprintf(stderr, "warning: local overrun\n");
		overruns++;
	}

	if(overrun_i)
		*overrun_i = overruns;

	return 0;
}

int usrp_source::read(complex *buf, unsigned int num_samples,
   unsigned int *samples_read) {

	unsigned int n;

	if(fill(num_samples, 0))
		return -1;

	n = m_cb->read(buf, num_samples);

	if(samples_read)
		*samples_read = n;

	return 0;
}

/*
 * Don't hold a lock on this and use the usrp at the same time.
 */
circular_buffer *usrp_source::get_buffer() {

	return m_cb;
}

int usrp_source::flush(unsigned int flush_count) {

	m_cb->flush();
	fill(flush_count * FLUSH_SIZE, 0);
	m_cb->flush();

	return 0;
}
<|endoftext|>


<commit_before>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "validation.h"

#include "main.h"
#include "checkpoints.h"
#include "timedata.h"
#include "tinyformat.h"

#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>

using namespace std;
using namespace boost;

static filesystem::path BlockFilePath(unsigned int nFile)
{
    string strBlockFn = strprintf("blk%04u.dat", nFile);
    return GetDataDir() / strBlockFn;
}

FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode)
{
    if ((nFile < 1) || (nFile == (unsigned int) -1))
        return NULL;
    FILE* file = fopen(BlockFilePath(nFile).string().c_str(), pszMode);
    if (!file)
        return NULL;
    if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w'))
    {
        if (fseek(file, nBlockPos, SEEK_SET) != 0)
        {
            fclose(file);
            return NULL;
        }
    }
    return file;
}

static unsigned int nCurrentBlockFile = 1;

FILE* AppendBlockFile(unsigned int& nFileRet)
{
    nFileRet = 0;
    while (true)
    {
        FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab");
        if (!file)
            return NULL;
        if (fseek(file, 0, SEEK_END) != 0)
            return NULL;
        // FAT32 file size max 4GB, fseek and ftell max 2GB, so we must stay under 2GB
        if (ftell(file) < (long)(0x7F000000 - MAX_SIZE))
        {
            nFileRet = nCurrentBlockFile;
            return file;
        }
        fclose(file);
        nCurrentBlockFile++;
    }
}

bool IsInitialBlockDownload()
{
    if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate())
        return true;
    static int64_t nLastUpdate;
    static CBlockIndex* pindexLastBest;
    if (pindexBest != pindexLastBest)
    {
        pindexLastBest = pindexBest;
        nLastUpdate = GetTime();
    }
    return (GetTime() - nLastUpdate < 15 &&
            pindexBest->GetBlockTime() < GetTime() - 8 * 60 * 60);
}

<commit_msg>Update logic of IsInitialBlockDownload<commit_after>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "validation.h"

#include "main.h"
#include "checkpoints.h"
#include "timedata.h"
#include "tinyformat.h"

#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>

using namespace std;
using namespace boost;

static filesystem::path BlockFilePath(unsigned int nFile)
{
    string strBlockFn = strprintf("blk%04u.dat", nFile);
    return GetDataDir() / strBlockFn;
}

FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode)
{
    if ((nFile < 1) || (nFile == (unsigned int) -1))
        return NULL;
    FILE* file = fopen(BlockFilePath(nFile).string().c_str(), pszMode);
    if (!file)
        return NULL;
    if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w'))
    {
        if (fseek(file, nBlockPos, SEEK_SET) != 0)
        {
            fclose(file);
            return NULL;
        }
    }
    return file;
}

static unsigned int nCurrentBlockFile = 1;

FILE* AppendBlockFile(unsigned int& nFileRet)
{
    nFileRet = 0;
    while (true)
    {
        FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab");
        if (!file)
            return NULL;
        if (fseek(file, 0, SEEK_END) != 0)
            return NULL;
        // FAT32 file size max 4GB, fseek and ftell max 2GB, so we must stay under 2GB
        if (ftell(file) < (long)(0x7F000000 - MAX_SIZE))
        {
            nFileRet = nCurrentBlockFile;
            return file;
        }
        fclose(file);
        nCurrentBlockFile++;
    }
}

bool IsInitialBlockDownload()
{
    // Once this function has returned false, it must remain false.
    static std::atomic<bool> latchToFalse{false};
    // Optimization: pre-test latch before taking the lock.
    if (latchToFalse.load(std::memory_order_relaxed))
        return false;

    LOCK(cs_main);
    if (latchToFalse.load(std::memory_order_relaxed))
        return false;

    if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate())
        return true;

    static int64_t nLastUpdate;
    static CBlockIndex* pindexLastBest;
    if (pindexBest != pindexLastBest) {
        pindexLastBest = pindexBest;
        nLastUpdate = GetTime();
    }
    if (GetTime() - nLastUpdate < 15 && pindexBest->GetBlockTime() < GetTime() - 8 * 60 * 60)
        return true;

    LogPrintf("Leaving InitialBlockDownload (latching to false)\n");
    latchToFalse.store(true, std::memory_order_relaxed);
    return false;
}

<|endoftext|>


<commit_before>// This file is part of DelftProto.
//
// DelftProto is free software: you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// DelftProto is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with DelftProto.  If not, see <http://www.gnu.org/licenses/>.

/// \file
/// Provides the Machine class.

#ifndef __MACHINE_HPP
#define __MACHINE_HPP

#include <types.hpp>
#include <data.hpp>
#include <stack.hpp>
#include <state.hpp>
#include <script.hpp>
#include <thread.hpp>
#include <neighbour.hpp>
#include <neighbourhood.hpp>
#include <instructions.hpp>
#include <machineextension.hpp>
#include <machineid.hpp>

/// A virtual machine.
/**
 * \note This class is \ref extending "extensible".
 */
class Machine : public MachineExtension {

	public:

		/// The ID of this Machine.
		MachineId id;

		/// The standard stack.
		Stack<Data> stack;

		/// The environment stack.
		Stack<Data> environment;

		/// The globals.
		Stack<Data> globals;

		/// The threads.
		/**
		 * \see Thread
		 */
		Array<Thread> threads;

		/// The (index of the) current thread when executing a run.
		/**
		 * When not running, this is the index of the thread that will be checked first for the next run.
		 */
		Thread::Id current_thread;

		/// The state variables.
		Array<State> state;

		/// The list of all the neighbours.
		NeighbourHood hood;

	protected:

		/// A pointer to the next instruction.
		Address instruction_pointer;

		/// Boolean indicating whether execution (installation or a single run) has finished.
		bool is_finished;

		/// The callback stack.
		/**
		 * \see call
		 * \see retn
		 */
		Stack<Instruction> callbacks;

		/// The current neighbour, when iterating through them.
		/**
		 * Used by the hood instructions when iterating through the hood to let the neighbour-instructions know which neighbour is currently being processed.
		 */
		NeighbourHood::iterator current_neighbour;

		friend void Instructions::DEF_VM(Machine &);
#if MIT_COMPATIBILITY != MIT_ONLY
		friend void Instructions::DEF_VM_EX(Machine &);
#endif
		friend void Instructions::EXIT(Machine &);
		friend class HoodInstructions;

	public:

		/// The constructor.
		Machine() : instruction_pointer(0), callbacks(1) {}

		/// \name Execution control
		/// \{

			/// Start an installation script.
			/**
			 * \note This does not execute an the installation script, it only prepares it. Call step() while not finished() to execute it.
			 *
			 * \param script A pointer to the installation script.
			 */
			inline void install(Int8 const * script) {
				is_finished = false;
				jump(Address(script));
				callbacks.push(0);
			}

			/// Start the next scheduled task.
			/**
			 * \note This does not execute Proto code, it only prepares the next run. Call step() while not finished() to execute it.
			 */
			inline void run() {
				is_finished = true;
				for(Size i = 0; i < threads.size(); i++){
					if (threads[current_thread].pending()){
						threads[current_thread].untrigger();
						jump(globals.peek(current_thread).asAddress());
						callbacks.push(run_callback);
						is_finished = false;
						return;
					}
					current_thread++;
					if (current_thread >= threads.size()) current_thread = 0;
				}
			}

		protected:
			static void run_callback(Machine & machine){
				machine.threads[machine.current_thread].result = machine.stack.pop();
				for(Size i = 0; i < machine.state.size(); i++){
					if (machine.state[i].thread == machine.current_thread){
						if (!machine.state[i].is_executed) machine.state[i].data.reset();
						machine.state[i].is_executed = false;
					}
				}
				machine.current_thread++;
				if (machine.current_thread >= machine.threads.size()) machine.current_thread = 0;
			}

		public:
			/// Execute the next instruction.
			/**
			 * \note Do not use this function when already finished().
			 */
			inline void step() {
				execute(instructions[nextInt8()]);
			}

			/// Check whether the running script (installation or a single run) has finished (true) or not (false).
			inline bool finished() {
				return callbacks.empty();
			}

		/// \}

		/// \name Control flow
		/// \{

			/// Jump to another address.
			/**
			 * \param address The address to jump to.
			 */
			inline void jump(Address address) {
				instruction_pointer = address;
			}

			/// Advance the instruction pointer.
			/**
			 * \param distance The number of bytes to skip.
			 */
			inline void skip(Size distance) {
				instruction_pointer += distance;
			}

			/// Call a function.
			/**
			 * \param address The address of the first instruction of the function.
			 * \param callback Called when the function returns.
			 */
			inline void call(Address address, Instruction callback = 0) {
				callbacks.push(callback);
				stack.push(instruction_pointer);
				jump(address);
			}

			/// Return from a function.
			/**
			 * If the corresponding call (if any) registered a callback, it will be called.
			 *
			 * \see Instructions::RET
			 */
			inline void retn() {
				Instruction callback = callbacks.pop();
				Data result = stack.pop();
				if (!callbacks.empty()) jump(stack.popAddress());
				if (!(callbacks.empty() && !callback)) stack.push(result);
				if (callback) callback(*this);
			}

			/// Get the current address (instruction pointer).
			inline Address currentAddress() const {
				return instruction_pointer;
			}

			/// Get the callback depth.
			/**
			 * Usually 1, but higher inside instructions such as \ref Instructions::INIT_FEEDBACK "INIT_FEEDBACK" or \ref Instructions::TUP_MAP "TUP_MAP".
			 */
			inline Counter depth() const {
				return callbacks.size();
			}

		/// \}

		/// \name Low level
		/// \{

			/// Read the next byte(s) as an unsigned integer.
			/**
			 * Using the Variable Length Quantity (VLQ)
			 * format as used in the MIDI file format.
			 *
			 * \see http://en.wikipedia.org/wiki/Variable-length_quantity
			 */
			inline Int nextInt() {
				Int value = 0;
				while(true){
					Int8 next = *instruction_pointer++;
					value |= next & 0x7F;
					if (next & 0x80) value <<= 7;
					else break;
				}
				return value;
			}

			/// Read the next byte as an unsigned 8-bit integer.
			/**
			 * \deprecated Use nextInt() instead.
			 */
			inline Int8 nextInt8() {
				return *instruction_pointer++;
			}

			/// Read the next two bytes as an unsigned 16-bit integer (big endian).
			/**
			 * \deprecated Use nextInt() instead.
			 */
			inline Int16 nextInt16() {
				Int16 data  = static_cast<Int16>(nextInt8()) << 8;
				      data += nextInt8();
				return data;
			}

			/// Execute an instruction.
			/**
			 * \param instruction The Instruction to execute.
			 *
			 * \see Instructions
			 */
			inline void execute(Instruction instruction) {
				instruction(*this);
			}

		/// \}

		/// \name Neighbour methods
		/// \{

			/// Get the current neighbour, when iterating through them.
			/**
			 * Used by the hood instructions when iterating through the hood to let the neighbour-instructions know which neighbour is currently being processed.
			 */
			inline Neighbour & currentNeighbour() {
				return *current_neighbour;
			}

			/// Get the Neighbour representing this machine.
			inline Neighbour & thisMachine() {
				return *hood.begin();
			}

		/// \}
};

#endif
<commit_msg>Fix typo in documentation.<commit_after>// This file is part of DelftProto.
//
// DelftProto is free software: you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// DelftProto is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with DelftProto.  If not, see <http://www.gnu.org/licenses/>.

/// \file
/// Provides the Machine class.

#ifndef __MACHINE_HPP
#define __MACHINE_HPP

#include <types.hpp>
#include <data.hpp>
#include <stack.hpp>
#include <state.hpp>
#include <script.hpp>
#include <thread.hpp>
#include <neighbour.hpp>
#include <neighbourhood.hpp>
#include <instructions.hpp>
#include <machineextension.hpp>
#include <machineid.hpp>

/// A virtual machine.
/**
 * \note This class is \ref extending "extensible".
 */
class Machine : public MachineExtension {

	public:

		/// The ID of this Machine.
		MachineId id;

		/// The standard stack.
		Stack<Data> stack;

		/// The environment stack.
		Stack<Data> environment;

		/// The globals.
		Stack<Data> globals;

		/// The threads.
		/**
		 * \see Thread
		 */
		Array<Thread> threads;

		/// The (index of the) current thread when executing a run.
		/**
		 * When not running, this is the index of the thread that will be checked first for the next run.
		 */
		Thread::Id current_thread;

		/// The state variables.
		Array<State> state;

		/// The list of all the neighbours.
		NeighbourHood hood;

	protected:

		/// A pointer to the next instruction.
		Address instruction_pointer;

		/// Boolean indicating whether execution (installation or a single run) has finished.
		bool is_finished;

		/// The callback stack.
		/**
		 * \see call
		 * \see retn
		 */
		Stack<Instruction> callbacks;

		/// The current neighbour, when iterating through them.
		/**
		 * Used by the hood instructions when iterating through the hood to let the neighbour-instructions know which neighbour is currently being processed.
		 */
		NeighbourHood::iterator current_neighbour;

		friend void Instructions::DEF_VM(Machine &);
#if MIT_COMPATIBILITY != MIT_ONLY
		friend void Instructions::DEF_VM_EX(Machine &);
#endif
		friend void Instructions::EXIT(Machine &);
		friend class HoodInstructions;

	public:

		/// The constructor.
		Machine() : instruction_pointer(0), callbacks(1) {}

		/// \name Execution control
		/// \{

			/// Start an installation script.
			/**
			 * \note This does not execute the installation script, it only prepares it. Call step() while not finished() to execute it.
			 *
			 * \param script A pointer to the installation script.
			 */
			inline void install(Int8 const * script) {
				is_finished = false;
				jump(Address(script));
				callbacks.push(0);
			}

			/// Start the next scheduled task.
			/**
			 * \note This does not execute Proto code, it only prepares the next run. Call step() while not finished() to execute it.
			 */
			inline void run() {
				is_finished = true;
				for(Size i = 0; i < threads.size(); i++){
					if (threads[current_thread].pending()){
						threads[current_thread].untrigger();
						jump(globals.peek(current_thread).asAddress());
						callbacks.push(run_callback);
						is_finished = false;
						return;
					}
					current_thread++;
					if (current_thread >= threads.size()) current_thread = 0;
				}
			}

		protected:
			static void run_callback(Machine & machine){
				machine.threads[machine.current_thread].result = machine.stack.pop();
				for(Size i = 0; i < machine.state.size(); i++){
					if (machine.state[i].thread == machine.current_thread){
						if (!machine.state[i].is_executed) machine.state[i].data.reset();
						machine.state[i].is_executed = false;
					}
				}
				machine.current_thread++;
				if (machine.current_thread >= machine.threads.size()) machine.current_thread = 0;
			}

		public:
			/// Execute the next instruction.
			/**
			 * \note Do not use this function when already finished().
			 */
			inline void step() {
				execute(instructions[nextInt8()]);
			}

			/// Check whether the running script (installation or a single run) has finished (true) or not (false).
			inline bool finished() {
				return callbacks.empty();
			}

		/// \}

		/// \name Control flow
		/// \{

			/// Jump to another address.
			/**
			 * \param address The address to jump to.
			 */
			inline void jump(Address address) {
				instruction_pointer = address;
			}

			/// Advance the instruction pointer.
			/**
			 * \param distance The number of bytes to skip.
			 */
			inline void skip(Size distance) {
				instruction_pointer += distance;
			}

			/// Call a function.
			/**
			 * \param address The address of the first instruction of the function.
			 * \param callback Called when the function returns.
			 */
			inline void call(Address address, Instruction callback = 0) {
				callbacks.push(callback);
				stack.push(instruction_pointer);
				jump(address);
			}

			/// Return from a function.
			/**
			 * If the corresponding call (if any) registered a callback, it will be called.
			 *
			 * \see Instructions::RET
			 */
			inline void retn() {
				Instruction callback = callbacks.pop();
				Data result = stack.pop();
				if (!callbacks.empty()) jump(stack.popAddress());
				if (!(callbacks.empty() && !callback)) stack.push(result);
				if (callback) callback(*this);
			}

			/// Get the current address (instruction pointer).
			inline Address currentAddress() const {
				return instruction_pointer;
			}

			/// Get the callback depth.
			/**
			 * Usually 1, but higher inside instructions such as \ref Instructions::INIT_FEEDBACK "INIT_FEEDBACK" or \ref Instructions::TUP_MAP "TUP_MAP".
			 */
			inline Counter depth() const {
				return callbacks.size();
			}

		/// \}

		/// \name Low level
		/// \{

			/// Read the next byte(s) as an unsigned integer.
			/**
			 * Using the Variable Length Quantity (VLQ)
			 * format as used in the MIDI file format.
			 *
			 * \see http://en.wikipedia.org/wiki/Variable-length_quantity
			 */
			inline Int nextInt() {
				Int value = 0;
				while(true){
					Int8 next = *instruction_pointer++;
					value |= next & 0x7F;
					if (next & 0x80) value <<= 7;
					else break;
				}
				return value;
			}

			/// Read the next byte as an unsigned 8-bit integer.
			/**
			 * \deprecated Use nextInt() instead.
			 */
			inline Int8 nextInt8() {
				return *instruction_pointer++;
			}

			/// Read the next two bytes as an unsigned 16-bit integer (big endian).
			/**
			 * \deprecated Use nextInt() instead.
			 */
			inline Int16 nextInt16() {
				Int16 data  = static_cast<Int16>(nextInt8()) << 8;
				      data += nextInt8();
				return data;
			}

			/// Execute an instruction.
			/**
			 * \param instruction The Instruction to execute.
			 *
			 * \see Instructions
			 */
			inline void execute(Instruction instruction) {
				instruction(*this);
			}

		/// \}

		/// \name Neighbour methods
		/// \{

			/// Get the current neighbour, when iterating through them.
			/**
			 * Used by the hood instructions when iterating through the hood to let the neighbour-instructions know which neighbour is currently being processed.
			 */
			inline Neighbour & currentNeighbour() {
				return *current_neighbour;
			}

			/// Get the Neighbour representing this machine.
			inline Neighbour & thisMachine() {
				return *hood.begin();
			}

		/// \}
};

#endif
<|endoftext|>


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

  Program:   Visualization Toolkit
  Module:    vtkAssembly.cc
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 1993-1995 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/
#include "vtkAssembly.hh"

// Description:
// Construct object with no children.
vtkAssembly::vtkAssembly()
{
  this->Paths = NULL;
}

vtkAssembly::~vtkAssembly()
{
  this->DeletePaths();
}

// Description:
// Add a part to the list of parts.
void vtkAssembly::AddPart(vtkActor *actor)
{
  this->Parts.AddItem(actor);
}

// Description:
// Remove a part from the list of parts,
void vtkAssembly::RemovePart(vtkActor *actor)
{
  this->Parts.RemoveItem(actor);
}

// Description:
// Copy another assembly.
vtkAssembly& vtkAssembly::operator=(const vtkAssembly& assembly)
{
  *((vtkActor *)this) = assembly;
  this->DeletePaths();

  return *this;
}

// Description:
// Render this assembly and all its parts. The rendering process is recursive.
// Note that a mapper need not be defined. If not defined, then no geometry
// will be drawn for this assembly. This allows you to create "logical"
// assemblies; that is, assemblies that only serve to group and transform
// its parts.
void vtkAssembly::Render(vtkRenderer *ren)
{
  vtkActor *actor;
  vtkActorCollection *path;

  this->UpdatePaths();

  // render the Paths
  for ( this->Paths->InitTraversal(); (path = this->Paths->GetNextItem()); )
    {
    actor = path->GetLastItem();
    if ( actor->GetVisibility() ) actor->Render(ren);
    }
}

void vtkAssembly::InitPartTraversal()
{
  this->UpdatePaths();
  this->Paths->InitTraversal();
}

// Description:
// Return the next part in the hierarchy of assembly parts.  This method
// returns a properly transformed and updated actor.
vtkActor *vtkAssembly::GetNextPart()
{
  vtkActorCollection *path;

  if ( (path = Paths->GetNextItem()) == NULL )
    {
    return NULL;
    }
  else
    {
    return path->GetLastItem();
    }
}

int vtkAssembly::GetNumberOfParts()
{
  this->UpdatePaths();
  return this->Paths->GetNumberOfItems();
}

// Build the assembly paths if necessary.
void vtkAssembly::UpdatePaths()
{
  if ( this->GetMTime() > this->PathTime )
    {
    this->DeletePaths();
    }

  if ( ! this->Paths )
    {
    this->Paths = new vtkAssemblyPaths;
    vtkActorCollection *path = new vtkActorCollection;
    this->Paths->AddItem(path);

    this->BuildPaths(this->Paths,path);
    this->PathTime.Modified();
    }
}

// Build assembly paths from this current assembly. Paths consist of
// an ordered sequence of actors, with transformations properly concatenated.
void vtkAssembly::BuildPaths(vtkAssemblyPaths *paths, vtkActorCollection *path)
{
  vtkActor *part, *actor, *copy=new vtkActor, *previous;
  vtkMatrix4x4 *matrix;
  vtkActorCollection *childPath;

  *copy = *this;

  if ( path->GetNumberOfItems() < 1 ) //we're starting at the top of the hierarchy
    {
    if ( this->GetUserMatrix() )
      {
      matrix = new vtkMatrix4x4;
      *matrix = *(this->GetUserMatrix());
      copy->SetUserMatrix(matrix);
      }
    }
  else //somewhere in the middle of the assembly hierarchy
    {
    previous = path->GetLastItem();
    matrix = new vtkMatrix4x4;
    *matrix = previous->GetMatrix();
    copy->SetUserMatrix(matrix);
    }

  path->AddItem(copy);

  // add our children to the path (if we're visible).
  if ( this->Visibility )
    {
    for ( this->Parts.InitTraversal(); (part = this->Parts.GetNextItem()); )
      {
      //a new path is created for each child
      childPath = new vtkActorCollection;
      paths->AddItem(childPath);

      // copy incoming path
      for ( path->InitTraversal(); (actor = path->GetNextItem()); )
        {
        copy = new vtkActor;
        *copy = *actor;

        if ( actor->GetUserMatrix() )
          {
          matrix = new vtkMatrix4x4;
          *matrix = *(actor->GetUserMatrix());
          copy->SetUserMatrix(matrix);
          }

        childPath->AddItem(copy);
        }

      // create new paths
      part->BuildPaths(paths,childPath);
      }
    }//if visible
}

// Delete the paths asscoiated with this assembly hierarchy.
void vtkAssembly::DeletePaths()
{
  if ( this->Paths )
    {
    vtkActor *actor;
    vtkActorCollection *path;

    for ( this->Paths->InitTraversal(); (path = this->Paths->GetNextItem()); )
      {
      for ( path->InitTraversal(); (actor = path->GetNextItem()); )
        {
        if ( actor->GetUserMatrix() ) actor->GetUserMatrix()->Delete();
        actor->Delete();
        }
      path->Delete();
      }
    this->Paths->Delete();
    this->Paths = NULL;
    }
}

// Description:
// Get the bounds for the assembly as (Xmin,Xmax,Ymin,Ymax,Zmin,Zmax).
float *vtkAssembly::GetBounds()
{
  vtkActor *actor;
  vtkMapper *mapper;
  vtkActorCollection *path;
  int i,n;
  float *bounds, bbox[24], *fptr;
  float *result;
  vtkMatrix4x4 matrix;

  this->UpdatePaths();

  // now calculate the new bounds
  if ( this->Mapper )
    {
    vtkActor::GetBounds();
    }
  else
    {
    this->Bounds[0] = this->Bounds[2] = this->Bounds[4] = VTK_LARGE_FLOAT;
    this->Bounds[1] = this->Bounds[3] = this->Bounds[5] = -VTK_LARGE_FLOAT;
    }

  this->Transform.PostMultiply();
  for ( this->Paths->InitTraversal(); (path = this->Paths->GetNextItem()); )
    {
    actor = path->GetLastItem();

    if ( (mapper = actor->GetMapper()) )
      {
      bounds = mapper->GetBounds();

      // fill out vertices of a bounding box
      bbox[ 0] = bounds[1]; bbox[ 1] = bounds[3]; bbox[ 2] = bounds[5];
      bbox[ 3] = bounds[1]; bbox[ 4] = bounds[2]; bbox[ 5] = bounds[5];
      bbox[ 6] = bounds[0]; bbox[ 7] = bounds[2]; bbox[ 8] = bounds[5];
      bbox[ 9] = bounds[0]; bbox[10] = bounds[3]; bbox[11] = bounds[5];
      bbox[12] = bounds[1]; bbox[13] = bounds[3]; bbox[14] = bounds[4];
      bbox[15] = bounds[1]; bbox[16] = bounds[2]; bbox[17] = bounds[4];
      bbox[18] = bounds[0]; bbox[19] = bounds[2]; bbox[20] = bounds[4];
      bbox[21] = bounds[0]; bbox[22] = bounds[3]; bbox[23] = bounds[4];

      // save the old transform
      actor->GetMatrix(matrix);
      this->Transform.Push();
      this->Transform.Identity();
      this->Transform.Concatenate(matrix);

      // and transform into actors coordinates
      fptr = bbox;
      for (n = 0; n < 8; n++)
        {
        this->Transform.SetPoint(fptr[0],fptr[1],fptr[2],1.0);

        // now store the result
        result = this->Transform.GetPoint();
        fptr[0] = result[0] / result[3];
        fptr[1] = result[1] / result[3];
        fptr[2] = result[2] / result[3];
        fptr += 3;
        }

      this->Transform.Pop();

      for (i = 0; i < 8; i++)
        {
        for (n = 0; n < 3; n++)
          {
          if (bbox[i*3+n] < this->Bounds[n*2]) this->Bounds[n*2] = bbox[i*3+n];
          if (bbox[i*3+n] > this->Bounds[n*2+1]) this->Bounds[n*2+1] = bbox[i*3+n];
          }
        }//for each point of box
      }//if mapper
    }//for each path

  return this->Bounds;
}

unsigned long int vtkAssembly::GetMTime()
{
  unsigned long mTime=this->vtkActor::GetMTime();
  unsigned long time;
  vtkActor *part;

  for (this->Parts.InitTraversal(); (part = this->Parts.GetNextItem()); )
    {
    time = part->GetMTime();
    mTime = ( time > mTime ? time : mTime );
    }

  return mTime;
}

void vtkAssembly::PrintSelf(ostream& os, vtkIndent indent)
{
  vtkActor::PrintSelf(os,indent);

  os << indent << "There are: " << this->Parts.GetNumberOfItems()
     << " parts in this assembly\n";
}

<commit_msg>ERR: Was leaving transform in PostMultiply state. CVSR: ---------------------------------------------------------------------<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkAssembly.cc
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 1993-1995 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/
#include "vtkAssembly.hh"

// Description:
// Construct object with no children.
vtkAssembly::vtkAssembly()
{
  this->Paths = NULL;
}

vtkAssembly::~vtkAssembly()
{
  this->DeletePaths();
}

// Description:
// Add a part to the list of parts.
void vtkAssembly::AddPart(vtkActor *actor)
{
  this->Parts.AddItem(actor);
}

// Description:
// Remove a part from the list of parts,
void vtkAssembly::RemovePart(vtkActor *actor)
{
  this->Parts.RemoveItem(actor);
}

// Description:
// Copy another assembly.
vtkAssembly& vtkAssembly::operator=(const vtkAssembly& assembly)
{
  *((vtkActor *)this) = assembly;
  this->DeletePaths();

  return *this;
}

// Description:
// Render this assembly and all its parts. The rendering process is recursive.
// Note that a mapper need not be defined. If not defined, then no geometry
// will be drawn for this assembly. This allows you to create "logical"
// assemblies; that is, assemblies that only serve to group and transform
// its parts.
void vtkAssembly::Render(vtkRenderer *ren)
{
  vtkActor *actor;
  vtkActorCollection *path;

  this->UpdatePaths();

  // render the Paths
  for ( this->Paths->InitTraversal(); (path = this->Paths->GetNextItem()); )
    {
    actor = path->GetLastItem();
    if ( actor->GetVisibility() ) actor->Render(ren);
    }
}

void vtkAssembly::InitPartTraversal()
{
  this->UpdatePaths();
  this->Paths->InitTraversal();
}

// Description:
// Return the next part in the hierarchy of assembly parts.  This method
// returns a properly transformed and updated actor.
vtkActor *vtkAssembly::GetNextPart()
{
  vtkActorCollection *path;

  if ( (path = Paths->GetNextItem()) == NULL )
    {
    return NULL;
    }
  else
    {
    return path->GetLastItem();
    }
}

int vtkAssembly::GetNumberOfParts()
{
  this->UpdatePaths();
  return this->Paths->GetNumberOfItems();
}

// Build the assembly paths if necessary.
void vtkAssembly::UpdatePaths()
{
  if ( this->GetMTime() > this->PathTime )
    {
    this->DeletePaths();
    }

  if ( ! this->Paths )
    {
    this->Paths = new vtkAssemblyPaths;
    vtkActorCollection *path = new vtkActorCollection;
    this->Paths->AddItem(path);

    this->BuildPaths(this->Paths,path);
    this->PathTime.Modified();
    }
}

// Build assembly paths from this current assembly. Paths consist of
// an ordered sequence of actors, with transformations properly concatenated.
void vtkAssembly::BuildPaths(vtkAssemblyPaths *paths, vtkActorCollection *path)
{
  vtkActor *part, *actor, *copy=new vtkActor, *previous;
  vtkMatrix4x4 *matrix;
  vtkActorCollection *childPath;

  *copy = *this;

  if ( path->GetNumberOfItems() < 1 ) //we're starting at the top of the hierarchy
    {
    if ( this->GetUserMatrix() )
      {
      matrix = new vtkMatrix4x4;
      *matrix = *(this->GetUserMatrix());
      copy->SetUserMatrix(matrix);
      }
    }
  else //somewhere in the middle of the assembly hierarchy
    {
    previous = path->GetLastItem();
    matrix = new vtkMatrix4x4;
    *matrix = previous->GetMatrix();
    copy->SetUserMatrix(matrix);
    }

  path->AddItem(copy);

  // add our children to the path (if we're visible).
  if ( this->Visibility )
    {
    for ( this->Parts.InitTraversal(); (part = this->Parts.GetNextItem()); )
      {
      //a new path is created for each child
      childPath = new vtkActorCollection;
      paths->AddItem(childPath);

      // copy incoming path
      for ( path->InitTraversal(); (actor = path->GetNextItem()); )
        {
        copy = new vtkActor;
        *copy = *actor;

        if ( actor->GetUserMatrix() )
          {
          matrix = new vtkMatrix4x4;
          *matrix = *(actor->GetUserMatrix());
          copy->SetUserMatrix(matrix);
          }

        childPath->AddItem(copy);
        }

      // create new paths
      part->BuildPaths(paths,childPath);
      }
    }//if visible
}

// Delete the paths asscoiated with this assembly hierarchy.
void vtkAssembly::DeletePaths()
{
  if ( this->Paths )
    {
    vtkActor *actor;
    vtkActorCollection *path;

    for ( this->Paths->InitTraversal(); (path = this->Paths->GetNextItem()); )
      {
      for ( path->InitTraversal(); (actor = path->GetNextItem()); )
        {
        if ( actor->GetUserMatrix() ) actor->GetUserMatrix()->Delete();
        actor->Delete();
        }
      path->Delete();
      }
    this->Paths->Delete();
    this->Paths = NULL;
    }
}

// Description:
// Get the bounds for the assembly as (Xmin,Xmax,Ymin,Ymax,Zmin,Zmax).
float *vtkAssembly::GetBounds()
{
  vtkActor *actor;
  vtkMapper *mapper;
  vtkActorCollection *path;
  int i,n;
  float *bounds, bbox[24], *fptr;
  float *result;
  vtkMatrix4x4 matrix;

  this->UpdatePaths();

  // now calculate the new bounds
  if ( this->Mapper )
    {
    vtkActor::GetBounds();
    }
  else
    {
    this->Bounds[0] = this->Bounds[2] = this->Bounds[4] = VTK_LARGE_FLOAT;
    this->Bounds[1] = this->Bounds[3] = this->Bounds[5] = -VTK_LARGE_FLOAT;
    }

  this->Transform.PostMultiply();
  for ( this->Paths->InitTraversal(); (path = this->Paths->GetNextItem()); )
    {
    actor = path->GetLastItem();

    if ( (mapper = actor->GetMapper()) )
      {
      bounds = mapper->GetBounds();

      // fill out vertices of a bounding box
      bbox[ 0] = bounds[1]; bbox[ 1] = bounds[3]; bbox[ 2] = bounds[5];
      bbox[ 3] = bounds[1]; bbox[ 4] = bounds[2]; bbox[ 5] = bounds[5];
      bbox[ 6] = bounds[0]; bbox[ 7] = bounds[2]; bbox[ 8] = bounds[5];
      bbox[ 9] = bounds[0]; bbox[10] = bounds[3]; bbox[11] = bounds[5];
      bbox[12] = bounds[1]; bbox[13] = bounds[3]; bbox[14] = bounds[4];
      bbox[15] = bounds[1]; bbox[16] = bounds[2]; bbox[17] = bounds[4];
      bbox[18] = bounds[0]; bbox[19] = bounds[2]; bbox[20] = bounds[4];
      bbox[21] = bounds[0]; bbox[22] = bounds[3]; bbox[23] = bounds[4];

      // save the old transform
      actor->GetMatrix(matrix);
      this->Transform.Push();
      this->Transform.Identity();
      this->Transform.Concatenate(matrix);

      // and transform into actors coordinates
      fptr = bbox;
      for (n = 0; n < 8; n++)
        {
        this->Transform.SetPoint(fptr[0],fptr[1],fptr[2],1.0);

        // now store the result
        result = this->Transform.GetPoint();
        fptr[0] = result[0] / result[3];
        fptr[1] = result[1] / result[3];
        fptr[2] = result[2] / result[3];
        fptr += 3;
        }

      this->Transform.Pop();

      for (i = 0; i < 8; i++)
        {
        for (n = 0; n < 3; n++)
          {
          if (bbox[i*3+n] < this->Bounds[n*2]) this->Bounds[n*2] = bbox[i*3+n];
          if (bbox[i*3+n] > this->Bounds[n*2+1]) this->Bounds[n*2+1] = bbox[i*3+n];
          }
        }//for each point of box
      }//if mapper
    }//for each path

  this->Transform.PreMultiply();

  return this->Bounds;
}

unsigned long int vtkAssembly::GetMTime()
{
  unsigned long mTime=this->vtkActor::GetMTime();
  unsigned long time;
  vtkActor *part;

  for (this->Parts.InitTraversal(); (part = this->Parts.GetNextItem()); )
    {
    time = part->GetMTime();
    mTime = ( time > mTime ? time : mTime );
    }

  return mTime;
}

void vtkAssembly::PrintSelf(ostream& os, vtkIndent indent)
{
  vtkActor::PrintSelf(os,indent);

  os << indent << "There are: " << this->Parts.GetNumberOfItems()
     << " parts in this assembly\n";
}

<|endoftext|>