<commit_before>#include "LoadSupportController.h"

#include <string>
#include <iostream>

LoadSupportController::LoadSupportController(ros::NodeHandle &n,
        double frequency,
        double M_object,
        double Z_ceiling,
        double Z_level,
        double Max_Weitht_compensation,
        std::vector<double> ee_rest_position,
        std::string topic_wrench_external,
        std::string topic_wrench_control,
        std::string topic_desired_equilibrium)
	: nh_(n),
	  loop_rate_(frequency),
	  M_object_(M_object),
	  MAX_weight_(Max_Weitht_compensation),
	  ee_rest_position_(ee_rest_position.data()),
	  Z_ceiling_(Z_ceiling),
	  Z_level_(Z_level),
	  dt_(1 / frequency) {

	ROS_INFO_STREAM("Load-Support-Controoler is created at: " << nh_.getNamespace() << " with freq: " << frequency << "Hz");

	// a subscriber to get read the external forces
	sub_external_wrench_ = nh_.subscribe(topic_wrench_external, 5,
	                                     &LoadSupportController::UpdateExternalForce, this,
	                                     ros::TransportHints().reliable().tcpNoDelay());

	// a publisher to send the control wrench:
	pub_control_wrench_ = nh_.advertise<geometry_msgs::WrenchStamped>(topic_wrench_control, 5);

	// a publisher to send the desired equilibrium point:
	pub_desired_equilibrium_ = nh_.advertise<geometry_msgs::Point>(topic_desired_equilibrium, 5);

	// initializing the sensed weight as zero
	Weight_ = 0;

	// initializing the load-share as zero
	loadShare_ = 0;

	// initializing the control force as zero
	Force_control_.setZero();

	// initializing the attractor at the rest position
	attractor_ = ee_rest_position_;

	// initializing the position of the object at the rest position
	object_position_ = ee_rest_position_;

	//  assuming the object is far away
	dist_object_ee_ground_ = 1e2;

	// a refractory period (e.g 2seconds) between each talking commands
	talking_rate_ = 2;
	// a time-duration that robot should not stay silent more than
	talking_forced_rate_ = 10;

	// intializing some sentence for the robot
	speech_statement_ = "I have nothing to say!";
	speech_statement_last_ = speech_statement_;
}

void LoadSupportController::Run() {

	// wait for soundplay to start running
	ros::Duration(1).sleep();
	// Say something on the startup
	soundclinet_.say("I am alive! How can I serve you?");
	// wait for the sound to be played
	ros::Duration(2).sleep();

	// reseting ROS-Time (not really necessary I guess).
	ros::Time::now();

	// next time that the robot is allowed to speak
	time_to_be_silient_ = ros::Time::now() + ros::Duration(talking_rate_);
	// next maximum time that the robot say something
	time_to_say_something_ = ros::Time::now() + ros::Duration(talking_forced_rate_);

	while (nh_.ok() ) {

		FindObject();

		ComputeLoadShare();

		WeightCompensation();

		ComputeEquilibriumPoint();

		// the robot only talks if a refractory period has passed (i.e., talking_rate_)
		if ( (ros::Time::now() - time_to_be_silient_).toSec() > 0  ) {
			if (speech_statement_.compare(speech_statement_last_) || (ros::Time::now() - time_to_say_something_).toSec() > 0) {
				SayWhatIsHappining();
				time_to_be_silient_ = ros::Time::now() + ros::Duration(talking_rate_);
				time_to_say_something_ = ros::Time::now() + ros::Duration(talking_forced_rate_);
			}
		}

		ros::spinOnce();
		loop_rate_.sleep();
	}

}

// The robot says what is stored in "speech_statement_"
void LoadSupportController::SayWhatIsHappining() {

	// only talk if the robot has something new to say
	// if (speech_statement_.compare(speech_statement_last_) ) {
	speech_statement_last_ = speech_statement_;
	soundclinet_.say(speech_statement_);
	ROS_INFO_STREAM_THROTTLE(1, "Talking: " << speech_statement_);
	// }
}

void LoadSupportController::FindObject() {

	tf::StampedTransform transform;
	Vector3d object_to_ee;

	object_to_ee.setZero();

	// first we read and check the distance of the object with respect to the end-effector of the robot
	try {
		listener_object_.lookupTransform("robotiq_force_torque_frame_id", "object",
		                                 // listener_object_.lookupTransform("mocap_palm", "mocap_object",
		                                 ros::Time(0), transform);

		object_to_ee << transform.getOrigin().x(),
		             transform.getOrigin().y(),
		             transform.getOrigin().z();

		dist_object_ee_ground_ = object_to_ee.segment(0, 2).norm();

		if (dist_object_ee_ground_ > 1) {
			speech_statement_ = "The object is too far.";
			object_position_ = ee_rest_position_;

			if (loadShare_ > 0.8) {
				speech_statement_ = "The marker is too far. I keep the object here.";
			}

		}
		else {
			speech_statement_ = "I am coming to help you.";

			try {
				listener_object_.lookupTransform("ur5_arm_base_link", "object",
				                                 ros::Time(0), transform);

				object_position_ << transform.getOrigin().x(),
				                 transform.getOrigin().y(),
				                 transform.getOrigin().z();

			}
			catch (tf::TransformException ex) {
				ROS_WARN_THROTTLE(1, "Couldn't find transform between arm and the object");
			}

			if (loadShare_ > 0.8) {
				speech_statement_ = "I am bringing the object for you";
			}

			if (dist_object_ee_ground_ < .1) {
				speech_statement_ = "I am under the marker.";

				if (loadShare_ > 0.6) {
					speech_statement_ = "I am holding the object.";
				}

			}

		}
	}
	catch (tf::TransformException ex) {
		ROS_WARN_THROTTLE(1, "Couldn't find transform between ee and the object");
		speech_statement_ = "I can not find the object!";
		dist_object_ee_ground_ = 1e2;
	}

	ROS_INFO_STREAM_THROTTLE(1, "distance to ee: " <<  dist_object_ee_ground_);

	// double dt = loop_rate_.expectedCycleTime().toSec();

	// attractor_ += pow(0.95,1/dt)  * (object_position_ - attractor_);
	// ROS_INFO_STREAM_THROTTLE(1, "dt :" <<  loop_rate_.expectedCycleTime().toSec());

	// update only the x and y of the attractor based on the location of the object
	attractor_.segment(0, 2)  += 0.05  * (object_position_.segment(0, 2) - attractor_.segment(0, 2) );

	// ROS_INFO_STREAM_THROTTLE(1, "X_att:" <<  attractor_ );

}

void LoadSupportController::UpdateExternalForce(const geometry_msgs::WrenchStamped::ConstPtr& msg) {

	wrench_external_ << msg->wrench.force.x, msg->wrench.force.y, msg->wrench.force.z,
	                 msg->wrench.torque.x, msg->wrench.torque.y, msg->wrench.torque.z;

}

void LoadSupportController::ComputeLoadShare() {

	// filtering the z-axis forces to meausre the weight supported by the robot
	Weight_ += 0.1 * ( -wrench_external_(2) - Weight_);

	ROS_INFO_STREAM_THROTTLE(1, "weight:  " <<  Weight_);

	// constraining the sensed weight between 0 and allowed-maximum of what is expected
	Weight_ = (Weight_ < 0) ? 0 : Weight_;
	Weight_ = (Weight_ > MAX_weight_) ? MAX_weight_ : Weight_;

	// computing the load-share
	loadShare_ = Weight_ / M_object_ / gravity_acc;

	// display load-share on the screen every second
	ROS_INFO_STREAM_THROTTLE(1, "load-share " <<  loadShare_);

	// saturating the load-share at 1
	loadShare_ = (loadShare_ > 1) ? 1 : loadShare_;
}

void LoadSupportController::WeightCompensation() {

	// computing the remainder of the weight for the expected object
	double Weight_missing = (M_object_ * gravity_acc) - Weight_;
	Weight_missing = (Weight_missing < 0) ? 0 : Weight_missing;

	// forces applied side-way to the end-effector
	double Weight_sidway = wrench_external_.segment(0, 2).norm();

	// by default we cancel all external forces
	Force_control_ = -wrench_external_.segment(0, 3);

	// if the side-way forces are more than missing weight, we just add a deadzone
	if ( Weight_sidway > Weight_missing) {
		Force_control_ = (Weight_missing / Weight_sidway) * Force_control_;
	}

	// simply compensating the weight by applying the exact opposite force
	Force_control_(2) = Weight_;

	// sending the control Force over the corresponding ROS-topic
	geometry_msgs::WrenchStamped msg_wrench;

	msg_wrench.header.stamp    = ros::Time::now();
	msg_wrench.header.frame_id = "ur5_arm_base_link";
	msg_wrench.wrench.force.x  = Force_control_(0);
	msg_wrench.wrench.force.y  = Force_control_(1);
	msg_wrench.wrench.force.z  = Force_control_(2);
	msg_wrench.wrench.torque.x = 0;
	msg_wrench.wrench.torque.y = 0;
	msg_wrench.wrench.torque.z = 0;
	pub_control_wrench_.publish(msg_wrench);

}

void LoadSupportController::ComputeEquilibriumPoint() {

	// starting by the rest hieght of the arm
	double att_target = (dist_object_ee_ground_ > 0.2) ? ee_rest_position_(2) : Z_ceiling_;

	if (loadShare_ > 0.8) {
		double beta = (loadShare_ - 0.8) / 0.1;
		beta = (beta > 1) ? 1 : beta;
		att_target = (1 - beta) * Z_ceiling_ + beta * Z_level_;
		ROS_INFO_STREAM_THROTTLE(1, "Lowering the weight " <<  att_target);
	}

	// filtering the Location of the attractor
	double att_err = att_target - attractor_(2);

	// attractor_(2) += ((att_err > 0) ? 0.001 : 0.0005 ) * att_err;
	attractor_(2) += 0.005  * att_err;

	// saturating the Z_attractor
	attractor_(2) = (attractor_(2) < Z_level_  ) ? Z_level_   : attractor_(2);
	attractor_(2) = (attractor_(2) > Z_ceiling_) ? Z_ceiling_ : attractor_(2);

	// sending the desired attractor over the corresponding ROS-topic
	geometry_msgs::Point msg_point;
	msg_point.x = attractor_(0);
	msg_point.y = attractor_(1);
	msg_point.z = attractor_(2);
	pub_desired_equilibrium_.publish(msg_point);

}
<commit_msg>add comments for each function<commit_after>#include "LoadSupportController.h"

#include <string>
#include <iostream>

LoadSupportController::LoadSupportController(ros::NodeHandle &n,
        double frequency,
        double M_object,
        double Z_ceiling,
        double Z_level,
        double Max_Weitht_compensation,
        std::vector<double> ee_rest_position,
        std::string topic_wrench_external,
        std::string topic_wrench_control,
        std::string topic_desired_equilibrium)
	: nh_(n),
	  loop_rate_(frequency),
	  M_object_(M_object),
	  MAX_weight_(Max_Weitht_compensation),
	  ee_rest_position_(ee_rest_position.data()),
	  Z_ceiling_(Z_ceiling),
	  Z_level_(Z_level),
	  dt_(1 / frequency) {

	ROS_INFO_STREAM("Load-Support-Controoler is created at: " << nh_.getNamespace() << " with freq: " << frequency << "Hz");

	// a subscriber to get read the external forces
	sub_external_wrench_ = nh_.subscribe(topic_wrench_external, 5,
	                                     &LoadSupportController::UpdateExternalForce, this,
	                                     ros::TransportHints().reliable().tcpNoDelay());

	// a publisher to send the control wrench:
	pub_control_wrench_ = nh_.advertise<geometry_msgs::WrenchStamped>(topic_wrench_control, 5);

	// a publisher to send the desired equilibrium point:
	pub_desired_equilibrium_ = nh_.advertise<geometry_msgs::Point>(topic_desired_equilibrium, 5);

	// initializing the sensed weight as zero
	Weight_ = 0;

	// initializing the load-share as zero
	loadShare_ = 0;

	// initializing the control force as zero
	Force_control_.setZero();

	// initializing the attractor at the rest position
	attractor_ = ee_rest_position_;

	// initializing the position of the object at the rest position
	object_position_ = ee_rest_position_;

	//  assuming the object is far away
	dist_object_ee_ground_ = 1e2;

	// a refractory period (e.g 2seconds) between each talking commands
	talking_rate_ = 3;
	// a time-duration that robot should not stay silent more than
	talking_forced_rate_ = 9;

	// intializing the sentence for the robot
	speech_statement_ = "I have nothing to say!";
	speech_statement_last_ = speech_statement_;
}

void LoadSupportController::Run() {

	// wait for soundplay to start running
	ros::Duration(1).sleep();
	// Say something on the startup
	soundclinet_.say("I am alive! How can I serve you?");
	// wait for the sound to be played
	ros::Duration(2).sleep();

	// reseting ROS-Time (not really necessary I guess).
	ros::Time::now();

	// next time that the robot is allowed to speak
	time_to_be_silient_ = ros::Time::now() + ros::Duration(talking_rate_);
	// next maximum time that the robot say something
	time_to_say_something_ = ros::Time::now() + ros::Duration(talking_forced_rate_);

	while (nh_.ok() ) {

		FindObject();

		ComputeLoadShare();

		WeightCompensation();

		ComputeEquilibriumPoint();

		// the robot only talks if a refractory period has passed (i.e., talking_rate_)
		if ( (ros::Time::now() - time_to_be_silient_).toSec() > 0  ) {
			if (speech_statement_.compare(speech_statement_last_) || (ros::Time::now() - time_to_say_something_).toSec() > 0) {
				SayWhatIsHappining();
				time_to_be_silient_ = ros::Time::now() + ros::Duration(talking_rate_);
				time_to_say_something_ = ros::Time::now() + ros::Duration(talking_forced_rate_);
			}
		}

		ros::spinOnce();
		loop_rate_.sleep();
	}

}

///////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Here we comput how of the object is held by the robot ////////////////
///////////////////////////////////////////////////////////////////////////////////////////
void LoadSupportController::ComputeLoadShare() {

	// filtering the z-axis forces to meausre the weight supported by the robot
	Weight_ += 0.1 * ( -wrench_external_(2) - Weight_);

	ROS_INFO_STREAM_THROTTLE(1, "weight:  " <<  Weight_);

	// constraining the sensed weight between 0 and allowed-maximum of what is expected
	Weight_ = (Weight_ < 0) ? 0 : Weight_;
	Weight_ = (Weight_ > MAX_weight_) ? MAX_weight_ : Weight_;

	// computing the load-share
	loadShare_ = Weight_ / M_object_ / gravity_acc;

	// display load-share on the screen every second
	ROS_INFO_STREAM_THROTTLE(1, "load-share " <<  loadShare_);

	// saturating the load-share at 1
	loadShare_ = (loadShare_ > 1) ? 1 : loadShare_;
}

///////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Here, we compensate for the weight of the object and  ////////////////
//////////////////// add a deadzone for the sideway forces in order to not ////////////////
//////////////////// to react to forces created by a tilted object         ////////////////
///////////////////////////////////////////////////////////////////////////////////////////
void LoadSupportController::WeightCompensation() {

	// computing the remainder of the weight for the expected object
	double Weight_missing = (M_object_ * gravity_acc) - Weight_;
	Weight_missing = (Weight_missing < 0) ? 0 : Weight_missing;

	// forces applied side-way to the end-effector
	double Weight_sidway = wrench_external_.segment(0, 2).norm();

	// by default we cancel all external forces
	Force_control_ = -wrench_external_.segment(0, 3);

	// if the side-way forces are more than missing weight, we just add a deadzone
	if ( Weight_sidway > Weight_missing) {
		Force_control_ = (Weight_missing / Weight_sidway) * Force_control_;
	}

	// simply compensating the weight by applying the exact opposite force
	Force_control_(2) = Weight_;

	// sending the control Force over the corresponding ROS-topic
	geometry_msgs::WrenchStamped msg_wrench;

	msg_wrench.header.stamp    = ros::Time::now();
	msg_wrench.header.frame_id = "ur5_arm_base_link";
	msg_wrench.wrench.force.x  = Force_control_(0);
	msg_wrench.wrench.force.y  = Force_control_(1);
	msg_wrench.wrench.force.z  = Force_control_(2);
	msg_wrench.wrench.torque.x = 0;
	msg_wrench.wrench.torque.y = 0;
	msg_wrench.wrench.torque.z = 0;
	pub_control_wrench_.publish(msg_wrench);

}

///////////////////////////////////////////////////////////////////////////////////////////
//////////////////// based on the load that the robot is holding, it       ////////////////
//////////////////// computes a new equilibrium point for the admittance   ////////////////
//////////////////// control in order to bring dwon the object.             ////////////////
///////////////////////////////////////////////////////////////////////////////////////////
void LoadSupportController::ComputeEquilibriumPoint() {

	// starting by the rest hieght of the arm
	double att_target = (dist_object_ee_ground_ > 0.2) ? ee_rest_position_(2) : Z_ceiling_;

	if (loadShare_ > 0.8) {
		double beta = (loadShare_ - 0.8) / 0.1;
		beta = (beta > 1) ? 1 : beta;
		att_target = (1 - beta) * Z_ceiling_ + beta * Z_level_;
		ROS_INFO_STREAM_THROTTLE(1, "Lowering the weight " <<  att_target);
	}

	// filtering the Location of the attractor
	double att_err = att_target - attractor_(2);

	// attractor_(2) += ((att_err > 0) ? 0.001 : 0.0005 ) * att_err;
	attractor_(2) += 0.005  * att_err;

	// saturating the Z_attractor
	attractor_(2) = (attractor_(2) < Z_level_  ) ? Z_level_   : attractor_(2);
	attractor_(2) = (attractor_(2) > Z_ceiling_) ? Z_ceiling_ : attractor_(2);

	// sending the desired attractor over the corresponding ROS-topic
	geometry_msgs::Point msg_point;
	msg_point.x = attractor_(0);
	msg_point.y = attractor_(1);
	msg_point.z = attractor_(2);
	pub_desired_equilibrium_.publish(msg_point);

}

///////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Here, the robot say the 'speach_statement_' //////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
void LoadSupportController::SayWhatIsHappining() {

	// only talk if the robot has something new to say
	// if (speech_statement_.compare(speech_statement_last_) ) {
	speech_statement_last_ = speech_statement_;
	soundclinet_.say(speech_statement_);
	ROS_INFO_STREAM_THROTTLE(1, "Talking: " << speech_statement_);
	// }
}

///////////////////////////////////////////////////////////////////////////////////////////
//////////////////// In this function, the robot look for the marker //////////////////////
//////////////////// and based on the distance of the object to its  //////////////////////
//////////////////// end-effector and the value of the load-share    //////////////////////
//////////////////// it decides to either follow the marker or stay  //////////////////////
//////////////////// put.                                            //////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
void LoadSupportController::FindObject() {

	tf::StampedTransform transform;
	Vector3d object_to_ee;

	object_to_ee.setZero();

	// first we read and check the distance of the object with respect to the end-effector of the robot
	try {
		listener_object_.lookupTransform("robotiq_force_torque_frame_id", "object",
		                                 // listener_object_.lookupTransform("mocap_palm", "mocap_object",
		                                 ros::Time(0), transform);

		object_to_ee << transform.getOrigin().x(),
		             transform.getOrigin().y(),
		             transform.getOrigin().z();

		dist_object_ee_ground_ = object_to_ee.segment(0, 2).norm();

		if (dist_object_ee_ground_ > 1) {
			speech_statement_ = "The object is too far.";
			object_position_ = ee_rest_position_;

			if (loadShare_ > 0.8) {
				speech_statement_ = "The marker is too far.";
			}
		}
		else {
			speech_statement_ = "Coming to help.";

			try {
				listener_object_.lookupTransform("ur5_arm_base_link", "object",
				                                 ros::Time(0), transform);

				object_position_ << transform.getOrigin().x(),
				                 transform.getOrigin().y(),
				                 transform.getOrigin().z();

			}
			catch (tf::TransformException ex) {
				ROS_WARN_THROTTLE(1, "Couldn't find transform between arm and the object");
			}

			if (loadShare_ > 0.8) {
				speech_statement_ = "Bringing the object";
			}
			if (dist_object_ee_ground_ < .1) {
				speech_statement_ = "Under the marker.";

				if (loadShare_ > 0.6) {
					speech_statement_ = "Holding the object.";
				}

			}
		}
	}
	catch (tf::TransformException ex) {
		ROS_WARN_THROTTLE(1, "Couldn't find transform between ee and the object");
		speech_statement_ = "I can not find the marker!";
		dist_object_ee_ground_ = 1e2;
	}

	// ROS_INFO_STREAM_THROTTLE(1, "distance to ee: " <<  dist_object_ee_ground_);
	// double dt = loop_rate_.expectedCycleTime().toSec();

	// attractor_ += pow(0.95,1/dt)  * (object_position_ - attractor_);
	// ROS_INFO_STREAM_THROTTLE(1, "dt :" <<  loop_rate_.expectedCycleTime().toSec());

	// update only the x and y of the attractor based on the location of the object
	attractor_.segment(0, 2)  += 0.05  * (object_position_.segment(0, 2) - attractor_.segment(0, 2) );
}

///////////////////////////////////////////////////////////////////////////////////////////
//////////////////// A callback to receive the external forces   //////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
void LoadSupportController::UpdateExternalForce(const geometry_msgs::WrenchStamped::ConstPtr& msg) {

	wrench_external_ << msg->wrench.force.x, msg->wrench.force.y, msg->wrench.force.z,
	                 msg->wrench.torque.x, msg->wrench.torque.y, msg->wrench.torque.z;

}

<|endoftext|>


<commit_before>/* Begin CVS Header
   $Source: /Volumes/Home/Users/shoops/cvs/copasi_dev/copasi/CopasiDataModel/CCopasiDataModel.cpp,v $
   $Revision: 1.15 $
   $Name:  $
   $Author: ssahle $
   $Date: 2005/04/12 15:09:13 $
   End CVS Header */

#include "copasi.h"
#include "copasiversion.h"
#include "CCopasiDataModel.h"

#include "commandline/COptions.h"
#include "function/CFunctionDB.h"
#include "model/CModel.h"
#include "utilities/CCopasiVector.h"
#include "utilities/CCopasiTask.h"
#include "utilities/CCopasiProblem.h"
#include "steadystate/CSteadyStateTask.h"
#include "trajectory/CTrajectoryTask.h"
#include "scan/CScanTask.h"
#include "optimization/COptTask.h"
#include "steadystate/CMCATask.h"
#include "steadystate/CMCAMethod.h"
#include "report/CReportDefinitionVector.h"
#include "plot/CPlotSpecification.h"
#include "xml/CCopasiXML.h"
#include "sbml/SBMLImporter.h"
#include "sbml/SBMLExporter.h"

CDataModelRenameHandler::CDataModelRenameHandler(CCopasiDataModel* dm)
    : mpDataModel(dm)
{}

bool CDataModelRenameHandler::handle(const std::string & oldCN, const std::string & newCN) const
  {
    std::cout << "CDataModelRenameHandler::handle()" << std::endl;
    std::cout << " old: " << oldCN << std::endl;
    std::cout << " new: " << newCN << std::endl << std::endl;

    return true;
  }

//********************************************************************

CCopasiDataModel * CCopasiDataModel::Global = NULL;

CCopasiDataModel::CCopasiDataModel(const bool withGUI):
    mpVersion(new CVersion),
    mpFunctionList(new CFunctionDB),
    mpModel(NULL),
    mpTaskList(NULL),
    mpReportDefinitionList(NULL),
    mpPlotDefinitionList(NULL),
    mWithGUI(withGUI),
    mpGUI(NULL),
    mChanged(false),
    mAutoSaveNeeded(false),
    mRenameHandler(this),
    pOldMetabolites(new CCopasiVectorS < CMetabOld >)
{
  mpVersion->setVersion(COPASI_VERSION_MAJOR,
                        COPASI_VERSION_MINOR,
                        COPASI_VERSION_BUILD);

  mpFunctionList->load();
  newModel();
  CCopasiObject::setRenameHandler(&mRenameHandler); //TODO where in the contructor should this be called?
}

CCopasiDataModel::~CCopasiDataModel()
{
  pdelete(mpVersion);
  pdelete(mpFunctionList);
  pdelete(mpModel);
  pdelete(mpTaskList);
  pdelete(mpReportDefinitionList);
  pdelete(mpPlotDefinitionList);
  pdelete(mpGUI);
  pdelete(pOldMetabolites);
}

bool CCopasiDataModel::loadModel(const std::string & fileName)
{
  std::ifstream File(fileName.c_str());

  if (File.fail())
    {
      CCopasiMessage Message(CCopasiMessage::RAW,
                             "File error when opening '%s'.",
                             fileName.c_str());
      return false;
    }

  std::string Line;
  File >> Line;

  if (!Line.compare(0, 8, "Version="))
    {
      if (fileName.rfind(".gps") == fileName.length() - 4 ||
          fileName.rfind(".GPS") == fileName.length() - 4)
        mSaveFileName = fileName.substr(0, fileName.length() - 4) + ".cps";
      else
        mSaveFileName = fileName + ".cps";

      File.close();
      CReadConfig inbuf(fileName.c_str());

      newModel();
      mpModel->load(inbuf);

      dynamic_cast<CSteadyStateTask *>((*mpTaskList)["Steady-State"])->load(inbuf);
      dynamic_cast<CTrajectoryTask *>((*mpTaskList)["Time-Course"])->load(inbuf);
    }
  else if (!Line.compare(0, 5, "<?xml"))
    {
      mSaveFileName = fileName;

      std::cout << "XML Format" << std::endl;
      File.seekg(0, std::ios_base::beg);

      CCopasiXML XML;

      XML.setFunctionList(mpFunctionList->loadedFunctions());

      SCopasiXMLGUI *pGUI = NULL;

      if (mWithGUI)
        {
          pGUI = new SCopasiXMLGUI;
          XML.setGUI(*pGUI);
        }

      if (!XML.load(File))
        {
          XML.freeModel();
          XML.freeTaskList();
          XML.freeReportList();
          XML.freePlotList();
          XML.freeGUI();

          return false;
        }

      newModel();

      if (XML.getModel())
        {
          pdelete(mpModel);
          mpModel = XML.getModel();
        }

      if (XML.getTaskList())
        {
          pdelete(mpTaskList);
          mpTaskList = XML.getTaskList();
          mpTaskList->setObjectName("TaskList");
          CCopasiContainer::Root->add(mpTaskList, true);
          addDefaultTasks();
        }

      if (XML.getReportList())
        {
          pdelete(mpReportDefinitionList);
          mpReportDefinitionList = new CReportDefinitionVector;
          *static_cast< CCopasiVector< CReportDefinition > * >(mpReportDefinitionList) = *XML.getReportList();
        }

      if (XML.getPlotList())
        {
          pdelete(mpPlotDefinitionList);
          mpPlotDefinitionList = XML.getPlotList();
          mpPlotDefinitionList->setObjectName("PlotList");
          CCopasiContainer::Root->add(mpPlotDefinitionList, true);
        }

      if (mWithGUI)
        {
          pdelete(mpGUI);
          mpGUI = pGUI;
        }
    }

  if (mpModel) mpModel->setCompileFlag();

  changed(false);
  return true;
}

bool CCopasiDataModel::saveModel(const std::string & fileName, bool overwriteFile,
                                 const bool & autoSave)
{
  std::string FileName = (fileName != "") ? fileName : mSaveFileName;

  // test first if a file would accidentaly overwritten.
  std::ifstream testInfile(FileName.c_str(), std::ios::in);

  if (testInfile && !overwriteFile)
    {
      CCopasiMessage(CCopasiMessage::ERROR, MCSBML + 1, FileName.c_str());
      return false;
    }

  mpModel->compileIfNecessary();

  CCopasiXML XML;

  XML.setModel(*mpModel);
  XML.setTaskList(*mpTaskList);
  XML.setReportList(*mpReportDefinitionList);
  XML.setPlotList(*mpPlotDefinitionList);
  XML.setGUI(*mpGUI);

  // We are first writing to a temporary stream to prevent accidental
  // destruction of an existing file in case the save command fails.
  std::ostringstream tmp;
  if (!XML.save(tmp)) return false;

  std::ofstream os(FileName.c_str());
  if (os.fail()) return false;

  os << tmp.str();
  if (os.fail()) return false;

  if (!autoSave) changed(false);

  return true;
}

bool CCopasiDataModel::autoSave()
{
  if (!mAutoSaveNeeded) return true;

  std::string AutoSave;
  int index;
  COptions::getValue("Tmp", AutoSave);
  if (AutoSave == "") return false;
#ifdef WIN32
  AutoSave += "\\";
#else
  AutoSave += "/";
#endif

  if ((index = mSaveFileName.find_last_of('\\')) == -1)
    index = mSaveFileName.find_last_of('/');
  //index = mSaveFileName.find_last_of('/' || '\\');
  AutoSave += "tmp_" + mSaveFileName.substr(index + 1, mSaveFileName.length() - index - 5) + ".cps";

  if (!saveModel(AutoSave, true)) return false;

  mAutoSaveNeeded = false;
  return true;
}

bool CCopasiDataModel::newModel(CModel * pModel)
{
  pdelete(mpModel);
  mpModel = (pModel) ? pModel : new CModel();

  pdelete(mpTaskList);
  mpTaskList = new CCopasiVectorN< CCopasiTask >("TaskList", CCopasiContainer::Root);

  // We have at least one task of every type
  addDefaultTasks();

  pdelete(mpReportDefinitionList);
  mpReportDefinitionList = new CReportDefinitionVector;

  pdelete(mpPlotDefinitionList);
  mpPlotDefinitionList = new CCopasiVectorN< CPlotSpecification >("PlotList", CCopasiContainer::Root);

  if (mWithGUI)
    {
      pdelete(mpGUI);
      mpGUI = new SCopasiXMLGUI;
    }

  changed(false);

  return true;
}

bool CCopasiDataModel::importSBML(const std::string & fileName)
{
  if (fileName.rfind(".xml") == fileName.length() - 4 ||
      fileName.rfind(".XML") == fileName.length() - 4)
    mSaveFileName = fileName.substr(0, fileName.length() - 4) + ".cps";
  else
    mSaveFileName = fileName + ".cps";

  SBMLImporter importer;
  CModel * pModel = importer.readSBML(fileName, mpFunctionList);

  if (pModel == NULL) return false;

  return newModel(pModel);
}

bool CCopasiDataModel::exportSBML(const std::string & fileName, bool overwriteFile)
{
  if (fileName == "") return false;

  SBMLExporter exporter;

  return exporter.exportSBML(mpModel, fileName.c_str(), overwriteFile);
}

CModel * CCopasiDataModel::getModel()
{return mpModel;}

CCopasiVectorN< CCopasiTask > * CCopasiDataModel::getTaskList()
{return mpTaskList;}

// static
CCopasiTask * CCopasiDataModel::addTask(const CCopasiTask::Type & taskType)
{
  CCopasiTask * pTask = NULL;

  switch (taskType)
    {
    case CCopasiTask::steadyState:
      pTask = new CSteadyStateTask(mpTaskList);
      break;

    case CCopasiTask::timeCourse:
      pTask = new CTrajectoryTask(mpTaskList);
      break;

    case CCopasiTask::scan:
      pTask = new CScanTask(mpTaskList);
      break;

    case CCopasiTask::fluxMode:
      // :TODO: implement task for elementary flux mode analysis
      return pTask;
      break;

    case CCopasiTask::optimization:
      // :TODO: implement task for optimization
      return pTask;
      break;

    case CCopasiTask::parameterFitting:
      // :TODO: implement task for parameter fitting
      return pTask;
      break;

    case CCopasiTask::mca:
      pTask = new CMCATask(mpTaskList);
      // :TODO: This must be avoided.
      static_cast<CMCAMethod *>(pTask->getMethod())->setModel(mpModel);
      break;

    default:
      return pTask;
    }

  pTask->getProblem()->setModel(mpModel);
  mpTaskList->add(pTask);

  return pTask;
}

bool CCopasiDataModel::addDefaultTasks()
{
  if (mpTaskList->getIndex("Steady-State") == C_INVALID_INDEX)
    addTask(CCopasiTask::steadyState);

  if (mpTaskList->getIndex("Time-Course") == C_INVALID_INDEX)
    addTask(CCopasiTask::timeCourse);

  if (mpTaskList->getIndex("Scan") == C_INVALID_INDEX)
    addTask(CCopasiTask::scan);

  if (mpTaskList->getIndex("Elementary Flux Modes") == C_INVALID_INDEX)
    addTask(CCopasiTask::fluxMode);

  if (mpTaskList->getIndex("Optimization") == C_INVALID_INDEX)
    addTask(CCopasiTask::optimization);

  if (mpTaskList->getIndex("Parameter Fitting") == C_INVALID_INDEX)
    addTask(CCopasiTask::parameterFitting);

  if (mpTaskList->getIndex("Metabolic Control Analysis") == C_INVALID_INDEX)
    addTask(CCopasiTask::mca);

  return true;
}

CReportDefinitionVector * CCopasiDataModel::getReportDefinitionList()
{return mpReportDefinitionList;}

CCopasiVectorN<CPlotSpecification> * CCopasiDataModel::getPlotDefinitionList()
{return mpPlotDefinitionList;}

CFunctionDB * CCopasiDataModel::getFunctionList()
{return mpFunctionList;}

SCopasiXMLGUI * CCopasiDataModel::getGUI()
{return mpGUI;}

CVersion * CCopasiDataModel::getVersion()
{return mpVersion;}

bool CCopasiDataModel::isChanged() const
  {return mChanged;}

void CCopasiDataModel::changed(const bool & changed)
{
  mChanged = changed;
  mAutoSaveNeeded = changed;
}
<commit_msg>the rename handler gets the list of CNs from CRegisteredObjectNames. It does not yet do anything with it.<commit_after>/* Begin CVS Header
   $Source: /Volumes/Home/Users/shoops/cvs/copasi_dev/copasi/CopasiDataModel/CCopasiDataModel.cpp,v $
   $Revision: 1.16 $
   $Name:  $
   $Author: ssahle $
   $Date: 2005/04/12 22:13:15 $
   End CVS Header */

#include "copasi.h"
#include "copasiversion.h"
#include "CCopasiDataModel.h"

#include "commandline/COptions.h"
#include "function/CFunctionDB.h"
#include "model/CModel.h"
#include "utilities/CCopasiVector.h"
#include "utilities/CCopasiTask.h"
#include "utilities/CCopasiProblem.h"
#include "steadystate/CSteadyStateTask.h"
#include "trajectory/CTrajectoryTask.h"
#include "scan/CScanTask.h"
#include "optimization/COptTask.h"
#include "steadystate/CMCATask.h"
#include "steadystate/CMCAMethod.h"
#include "report/CReportDefinitionVector.h"
#include "plot/CPlotSpecification.h"
#include "xml/CCopasiXML.h"
#include "sbml/SBMLImporter.h"
#include "sbml/SBMLExporter.h"

CDataModelRenameHandler::CDataModelRenameHandler(CCopasiDataModel* dm)
    : mpDataModel(dm)
{}

bool CDataModelRenameHandler::handle(const std::string & oldCN, const std::string & newCN) const
  {
    std::cout << "CDataModelRenameHandler::handle()" << std::endl;
    std::cout << " old: " << oldCN << std::endl;
    std::cout << " new: " << newCN << std::endl;

    const std::set<CRegisteredObjectName*> nameSet = CRegisteredObjectName::getSet();

    std::cout << " ************ " << nameSet.size() << std::endl;
    std::set<CRegisteredObjectName*>::const_iterator it, itEnd = nameSet.end();
    for (it = nameSet.begin(); it != itEnd; ++it)
      {
        std::cout << "      " << **it << std::endl;
      }

    std::cout << " " << std::endl;
    return true;
  }

//********************************************************************

CCopasiDataModel * CCopasiDataModel::Global = NULL;

CCopasiDataModel::CCopasiDataModel(const bool withGUI):
    mpVersion(new CVersion),
    mpFunctionList(new CFunctionDB),
    mpModel(NULL),
    mpTaskList(NULL),
    mpReportDefinitionList(NULL),
    mpPlotDefinitionList(NULL),
    mWithGUI(withGUI),
    mpGUI(NULL),
    mChanged(false),
    mAutoSaveNeeded(false),
    mRenameHandler(this),
    pOldMetabolites(new CCopasiVectorS < CMetabOld >)
{
  mpVersion->setVersion(COPASI_VERSION_MAJOR,
                        COPASI_VERSION_MINOR,
                        COPASI_VERSION_BUILD);

  mpFunctionList->load();
  newModel();
  CCopasiObject::setRenameHandler(&mRenameHandler); //TODO where in the contructor should this be called?
}

CCopasiDataModel::~CCopasiDataModel()
{
  pdelete(mpVersion);
  pdelete(mpFunctionList);
  pdelete(mpModel);
  pdelete(mpTaskList);
  pdelete(mpReportDefinitionList);
  pdelete(mpPlotDefinitionList);
  pdelete(mpGUI);
  pdelete(pOldMetabolites);
}

bool CCopasiDataModel::loadModel(const std::string & fileName)
{
  std::ifstream File(fileName.c_str());

  if (File.fail())
    {
      CCopasiMessage Message(CCopasiMessage::RAW,
                             "File error when opening '%s'.",
                             fileName.c_str());
      return false;
    }

  std::string Line;
  File >> Line;

  if (!Line.compare(0, 8, "Version="))
    {
      if (fileName.rfind(".gps") == fileName.length() - 4 ||
          fileName.rfind(".GPS") == fileName.length() - 4)
        mSaveFileName = fileName.substr(0, fileName.length() - 4) + ".cps";
      else
        mSaveFileName = fileName + ".cps";

      File.close();
      CReadConfig inbuf(fileName.c_str());

      newModel();
      mpModel->load(inbuf);

      dynamic_cast<CSteadyStateTask *>((*mpTaskList)["Steady-State"])->load(inbuf);
      dynamic_cast<CTrajectoryTask *>((*mpTaskList)["Time-Course"])->load(inbuf);
    }
  else if (!Line.compare(0, 5, "<?xml"))
    {
      mSaveFileName = fileName;

      std::cout << "XML Format" << std::endl;
      File.seekg(0, std::ios_base::beg);

      CCopasiXML XML;

      XML.setFunctionList(mpFunctionList->loadedFunctions());

      SCopasiXMLGUI *pGUI = NULL;

      if (mWithGUI)
        {
          pGUI = new SCopasiXMLGUI;
          XML.setGUI(*pGUI);
        }

      if (!XML.load(File))
        {
          XML.freeModel();
          XML.freeTaskList();
          XML.freeReportList();
          XML.freePlotList();
          XML.freeGUI();

          return false;
        }

      newModel();

      if (XML.getModel())
        {
          pdelete(mpModel);
          mpModel = XML.getModel();
        }

      if (XML.getTaskList())
        {
          pdelete(mpTaskList);
          mpTaskList = XML.getTaskList();
          mpTaskList->setObjectName("TaskList");
          CCopasiContainer::Root->add(mpTaskList, true);
          addDefaultTasks();
        }

      if (XML.getReportList())
        {
          pdelete(mpReportDefinitionList);
          mpReportDefinitionList = new CReportDefinitionVector;
          *static_cast< CCopasiVector< CReportDefinition > * >(mpReportDefinitionList) = *XML.getReportList();
        }

      if (XML.getPlotList())
        {
          pdelete(mpPlotDefinitionList);
          mpPlotDefinitionList = XML.getPlotList();
          mpPlotDefinitionList->setObjectName("PlotList");
          CCopasiContainer::Root->add(mpPlotDefinitionList, true);
        }

      if (mWithGUI)
        {
          pdelete(mpGUI);
          mpGUI = pGUI;
        }
    }

  if (mpModel) mpModel->setCompileFlag();

  changed(false);
  return true;
}

bool CCopasiDataModel::saveModel(const std::string & fileName, bool overwriteFile,
                                 const bool & autoSave)
{
  std::string FileName = (fileName != "") ? fileName : mSaveFileName;

  // test first if a file would accidentaly overwritten.
  std::ifstream testInfile(FileName.c_str(), std::ios::in);

  if (testInfile && !overwriteFile)
    {
      CCopasiMessage(CCopasiMessage::ERROR, MCSBML + 1, FileName.c_str());
      return false;
    }

  mpModel->compileIfNecessary();

  CCopasiXML XML;

  XML.setModel(*mpModel);
  XML.setTaskList(*mpTaskList);
  XML.setReportList(*mpReportDefinitionList);
  XML.setPlotList(*mpPlotDefinitionList);
  XML.setGUI(*mpGUI);

  // We are first writing to a temporary stream to prevent accidental
  // destruction of an existing file in case the save command fails.
  std::ostringstream tmp;
  if (!XML.save(tmp)) return false;

  std::ofstream os(FileName.c_str());
  if (os.fail()) return false;

  os << tmp.str();
  if (os.fail()) return false;

  if (!autoSave) changed(false);

  return true;
}

bool CCopasiDataModel::autoSave()
{
  if (!mAutoSaveNeeded) return true;

  std::string AutoSave;
  int index;
  COptions::getValue("Tmp", AutoSave);
  if (AutoSave == "") return false;
#ifdef WIN32
  AutoSave += "\\";
#else
  AutoSave += "/";
#endif

  if ((index = mSaveFileName.find_last_of('\\')) == -1)
    index = mSaveFileName.find_last_of('/');
  //index = mSaveFileName.find_last_of('/' || '\\');
  AutoSave += "tmp_" + mSaveFileName.substr(index + 1, mSaveFileName.length() - index - 5) + ".cps";

  if (!saveModel(AutoSave, true)) return false;

  mAutoSaveNeeded = false;
  return true;
}

bool CCopasiDataModel::newModel(CModel * pModel)
{
  pdelete(mpModel);
  mpModel = (pModel) ? pModel : new CModel();

  pdelete(mpTaskList);
  mpTaskList = new CCopasiVectorN< CCopasiTask >("TaskList", CCopasiContainer::Root);

  // We have at least one task of every type
  addDefaultTasks();

  pdelete(mpReportDefinitionList);
  mpReportDefinitionList = new CReportDefinitionVector;

  pdelete(mpPlotDefinitionList);
  mpPlotDefinitionList = new CCopasiVectorN< CPlotSpecification >("PlotList", CCopasiContainer::Root);

  if (mWithGUI)
    {
      pdelete(mpGUI);
      mpGUI = new SCopasiXMLGUI;
    }

  changed(false);

  return true;
}

bool CCopasiDataModel::importSBML(const std::string & fileName)
{
  if (fileName.rfind(".xml") == fileName.length() - 4 ||
      fileName.rfind(".XML") == fileName.length() - 4)
    mSaveFileName = fileName.substr(0, fileName.length() - 4) + ".cps";
  else
    mSaveFileName = fileName + ".cps";

  SBMLImporter importer;
  CModel * pModel = importer.readSBML(fileName, mpFunctionList);

  if (pModel == NULL) return false;

  return newModel(pModel);
}

bool CCopasiDataModel::exportSBML(const std::string & fileName, bool overwriteFile)
{
  if (fileName == "") return false;

  SBMLExporter exporter;

  return exporter.exportSBML(mpModel, fileName.c_str(), overwriteFile);
}

CModel * CCopasiDataModel::getModel()
{return mpModel;}

CCopasiVectorN< CCopasiTask > * CCopasiDataModel::getTaskList()
{return mpTaskList;}

// static
CCopasiTask * CCopasiDataModel::addTask(const CCopasiTask::Type & taskType)
{
  CCopasiTask * pTask = NULL;

  switch (taskType)
    {
    case CCopasiTask::steadyState:
      pTask = new CSteadyStateTask(mpTaskList);
      break;

    case CCopasiTask::timeCourse:
      pTask = new CTrajectoryTask(mpTaskList);
      break;

    case CCopasiTask::scan:
      pTask = new CScanTask(mpTaskList);
      break;

    case CCopasiTask::fluxMode:
      // :TODO: implement task for elementary flux mode analysis
      return pTask;
      break;

    case CCopasiTask::optimization:
      // :TODO: implement task for optimization
      return pTask;
      break;

    case CCopasiTask::parameterFitting:
      // :TODO: implement task for parameter fitting
      return pTask;
      break;

    case CCopasiTask::mca:
      pTask = new CMCATask(mpTaskList);
      // :TODO: This must be avoided.
      static_cast<CMCAMethod *>(pTask->getMethod())->setModel(mpModel);
      break;

    default:
      return pTask;
    }

  pTask->getProblem()->setModel(mpModel);
  mpTaskList->add(pTask);

  return pTask;
}

bool CCopasiDataModel::addDefaultTasks()
{
  if (mpTaskList->getIndex("Steady-State") == C_INVALID_INDEX)
    addTask(CCopasiTask::steadyState);

  if (mpTaskList->getIndex("Time-Course") == C_INVALID_INDEX)
    addTask(CCopasiTask::timeCourse);

  if (mpTaskList->getIndex("Scan") == C_INVALID_INDEX)
    addTask(CCopasiTask::scan);

  if (mpTaskList->getIndex("Elementary Flux Modes") == C_INVALID_INDEX)
    addTask(CCopasiTask::fluxMode);

  if (mpTaskList->getIndex("Optimization") == C_INVALID_INDEX)
    addTask(CCopasiTask::optimization);

  if (mpTaskList->getIndex("Parameter Fitting") == C_INVALID_INDEX)
    addTask(CCopasiTask::parameterFitting);

  if (mpTaskList->getIndex("Metabolic Control Analysis") == C_INVALID_INDEX)
    addTask(CCopasiTask::mca);

  return true;
}

CReportDefinitionVector * CCopasiDataModel::getReportDefinitionList()
{return mpReportDefinitionList;}

CCopasiVectorN<CPlotSpecification> * CCopasiDataModel::getPlotDefinitionList()
{return mpPlotDefinitionList;}

CFunctionDB * CCopasiDataModel::getFunctionList()
{return mpFunctionList;}

SCopasiXMLGUI * CCopasiDataModel::getGUI()
{return mpGUI;}

CVersion * CCopasiDataModel::getVersion()
{return mpVersion;}

bool CCopasiDataModel::isChanged() const
  {return mChanged;}

void CCopasiDataModel::changed(const bool & changed)
{
  mChanged = changed;
  mAutoSaveNeeded = changed;
}
<|endoftext|>


<commit_before>#include "Workspace.h"
#include <algorithm>
#include <QtCore/QMetaType>
#include "Logger.h"
#include "Scripter.h"
#include "pjobrunnerpool.h"
#include "PJobRunnerSessionThread.h"

Workspace::Workspace(void)
: m_pjob_file(0), m_running(false)
{
	qRegisterMetaType< QHash<QString,double> >("QHash<QString,double>");
	qRegisterMetaType<QHash< QHash<QString,double>, QHash<QString,double> > >("QHash< QHash<QString,double>, QHash<QString,double> >");
        connect(this, SIGNAL(jobAdded(Job*,unsigned int)), &Logger::getInstance(), SLOT(jobAdded(Job*,unsigned int)));
        connect(this, SIGNAL(jobRemoved(Job*)), &Logger::getInstance(), SLOT(jobRemoved(Job*)));
        connect(this, SIGNAL(jobMoved(Job*,unsigned int)), &Logger::getInstance(), SLOT(jobMoved(Job*,unsigned int)));
	connect(this, SIGNAL(started()), &Logger::getInstance(), SLOT(started()));
	connect(this, SIGNAL(stopped()), &Logger::getInstance(), SLOT(stopped()));
	connect(&m_results, SIGNAL(newValueSet(QString , QString , QHash<QString,double> , double )),
		&Logger::getInstance(), SLOT(newValueSet(QString , QString , QHash<QString,double> , double )));
	Scripter::getInstance();
}

Workspace::~Workspace(void)
{
}

Workspace& Workspace::getInstace(void)
{
        static Workspace p;
	return p;
}

Results& Workspace::getResults(){
	return m_results;
}

void Workspace::setPJobFile(PJobFile* p){
    m_pjob_file = p;
    emit pjobFileChanged(p);
}

PJobFile* Workspace::getPJobFile(){
    return m_pjob_file;
}

void Workspace::addJob(Job* j){
	m_jobsQueued.push_back(j);
        connect(j, SIGNAL(stateChanged(Job*, Job::State)), this, SLOT(jobStateChanged(Job*, Job::State)));
        connect(j, SIGNAL(stateChanged(Job*, Job::State)), &Logger::getInstance(), SLOT(jobStateChanged(Job*, Job::State)));
	connect(j, SIGNAL(results(QHash< QHash<QString,double>, QHash<QString,double> > , QString )),
		&m_results, SLOT(newValues(QHash< QHash<QString,double>, QHash<QString,double> > , QString )));
	connect(j, SIGNAL(results(QHash< QHash<QString,double>, QHash<QString,double> > , QString )),
		&Logger::getInstance(), SLOT(jobResults(QHash< QHash<QString,double>, QHash<QString,double> > , QString )));
        connect(j, SIGNAL(problemReadingResults(Job*,QString)), &Logger::getInstance(), SLOT(jobHasProblemsReadingResult(Job*,QString)));
	emit jobAdded(j, m_jobsQueued.indexOf(j));
}

void Workspace::removeJob(Job* j){
	m_jobsSubmited.removeOne(j);
	m_jobsRunning.removeOne(j);
	m_jobsFinished.removeOne(j);
	m_jobsQueued.removeOne(j);
        disconnect(j, SIGNAL(stateChanged(Job*, Job::State)), this, SLOT(jobStateChanged(Job*, Job::State)));
        disconnect(j, SIGNAL(stateChanged(Job*, Job::State)), &Logger::getInstance(), SLOT(jobStateChanged(Job*, Job::State)));
	disconnect(j, SIGNAL(results(QHash< QHash<QString,double>, QHash<QString,double> > , QString )),
		&m_results, SLOT(newValues(QHash< QHash<QString,double>, QHash<QString,double> > , QString )));
	disconnect(j, SIGNAL(results(QHash< QHash<QString,double>, QHash<QString,double> > , QString )),
		&Logger::getInstance(), SLOT(jobResults(QHash< QHash<QString,double>, QHash<QString,double> > , QString )));
	emit jobRemoved(j);
}

void Workspace::setQueuePosition(Job* j, unsigned int position){
        if(static_cast<int>(position) >= m_jobsQueued.size()) position = m_jobsQueued.size()-1;
	m_jobsQueued.removeOne(j);
	m_jobsQueued.insert(position,j);
	emit jobMoved(j,position);
}

void Workspace::start(){
    populate_session_threads();
    foreach(PJobRunnerSessionThread* thread, m_session_threads){
        thread->start();
    }

    m_running = true;
    emit started();
}

void Workspace::stop(){
	m_running = false;
	emit stopped();
}

void Workspace::jobStateChanged(Job* job, Job::State state){
	switch(state){
                case Job::FINISHED:
			jobFinished(job);
			break;
                case Job::SUBMITED:
			jobSubmited(job);
			break;
                case Job::RUNNING:
			jobStarted(job);
			break;
                case Job::QUEUED:
			break;
	}
}

void Workspace::jobSubmited(Job* j){
	m_jobsQueued.removeOne(j);
	m_jobsSubmited.push_back(j);
}

void Workspace::jobStarted(Job* j){
	m_jobsQueued.removeOne(j);
	m_jobsSubmited.removeOne(j);
	m_jobsRunning.push_back(j);
}

void Workspace::jobFinished(Job* j){
	m_jobsRunning.removeOne(j);
	m_jobsFinished.push_back(j);
}

bool Workspace::isRunning(){
	return m_running;
}

void Workspace::import_results_from_pjobfile(QString filename)
{
	PJobFile file(filename);
	QStringList runList = file.runDirectoryEntries();

	//run_DATUM - Strings herausfiltern
	int i = 0;
	foreach(QString s, runList)
	{
		runList.replace(i++,s.section('/',0,1));
	}

	//doppelte Einträge verwerfen (jeder run soll logischerweise nur einmal geladen werden)
	runList.removeDuplicates();

	unsigned int size = runList.size();
	QString message = QString("Importing results from %1...").arg(filename);
	if(size > 10) emit progress(message,0);
	i = 0;
	foreach(QString s, runList)
	{
		//Results auslesen...
		QHash< QHash<QString,double>, QHash<QString,double> > result;
		try
		{
			result = file.getResultsForRun(s);
		}
		catch(QString s)
		{
			//Ursache z.B.: Dateien unvollständig => parametercombination.xml passt nicht zu den Ergebnissen)
			//=> überspringen
			continue;
		}

		//...und weiterleiten
		m_results.newValues(result,file.pjobFile());
		if(size > 10) emit progress(message,i++*100/size);
		if(m_progresses_to_abort.contains(message)){
			m_progresses_to_abort.remove(message);
			break;
		}
	}
	if(size > 10) emit progress(message,100);
}

void Workspace::abort_progress(const QString& what){
	m_progresses_to_abort.insert(what);
}

Job* Workspace::startNextQueuedJob(){
    QMutexLocker locker(&m_mutex);
    if(m_jobsQueued.isEmpty()) return 0;
    Job* job = m_jobsQueued.takeFirst();
    m_jobsRunning.push_back(job);
    return job;
}

void Workspace::session_finished(){
    if(!m_running) return;
    QObject* object = sender();
    PJobRunnerSessionThread* thread = dynamic_cast<PJobRunnerSessionThread*>(object);
    if(!thread) return;
    thread->start();
}

void Workspace::populate_session_threads(){
    clear_session_threads();
    unsigned int jobs = m_jobsQueued.size();
    foreach(QHostAddress host, PJobRunnerPool::instance().known_pjob_runners()){
        unsigned int thread_count = PJobRunnerPool::instance().thread_count(host);
        for(unsigned int i=0; i<thread_count; i++){
            PJobRunnerSessionThread* thread = new PJobRunnerSessionThread(host, this);
            connect(thread, SIGNAL(finished()), this, SLOT(session_finished()), Qt::QueuedConnection);
            m_session_threads.insert(thread);
            jobs--;
            if(jobs <= 0) return;
        }
    }
}

void Workspace::clear_session_threads(){
    foreach(PJobRunnerSessionThread* thread, m_session_threads){
        if(thread->isRunning()) continue;
        m_session_threads.remove(thread);
        delete thread;
    }
}
<commit_msg>PQueue: Workspace: fixed Job::State signal/slot connections.<commit_after>#include "Workspace.h"
#include <algorithm>
#include <QtCore/QMetaType>
#include "Logger.h"
#include "Scripter.h"
#include "pjobrunnerpool.h"
#include "PJobRunnerSessionThread.h"

Workspace::Workspace(void)
: m_pjob_file(0), m_running(false)
{
	qRegisterMetaType< QHash<QString,double> >("QHash<QString,double>");
	qRegisterMetaType<QHash< QHash<QString,double>, QHash<QString,double> > >("QHash< QHash<QString,double>, QHash<QString,double> >");
        connect(this, SIGNAL(jobAdded(Job*,unsigned int)), &Logger::getInstance(), SLOT(jobAdded(Job*,unsigned int)));
        connect(this, SIGNAL(jobRemoved(Job*)), &Logger::getInstance(), SLOT(jobRemoved(Job*)));
        connect(this, SIGNAL(jobMoved(Job*,unsigned int)), &Logger::getInstance(), SLOT(jobMoved(Job*,unsigned int)));
	connect(this, SIGNAL(started()), &Logger::getInstance(), SLOT(started()));
	connect(this, SIGNAL(stopped()), &Logger::getInstance(), SLOT(stopped()));
	connect(&m_results, SIGNAL(newValueSet(QString , QString , QHash<QString,double> , double )),
		&Logger::getInstance(), SLOT(newValueSet(QString , QString , QHash<QString,double> , double )));
	Scripter::getInstance();
        qRegisterMetaType<Job::State>("Job::State");
}

Workspace::~Workspace(void)
{
}

Workspace& Workspace::getInstace(void)
{
        static Workspace p;
	return p;
}

Results& Workspace::getResults(){
	return m_results;
}

void Workspace::setPJobFile(PJobFile* p){
    m_pjob_file = p;
    emit pjobFileChanged(p);
}

PJobFile* Workspace::getPJobFile(){
    return m_pjob_file;
}

void Workspace::addJob(Job* j){
	m_jobsQueued.push_back(j);
        connect(j, SIGNAL(stateChanged(Job*, Job::State)), this, SLOT(jobStateChanged(Job*, Job::State)), Qt::QueuedConnection);
        connect(j, SIGNAL(stateChanged(Job*, Job::State)), &Logger::getInstance(), SLOT(jobStateChanged(Job*, Job::State)), Qt::QueuedConnection);
	connect(j, SIGNAL(results(QHash< QHash<QString,double>, QHash<QString,double> > , QString )),
                &m_results, SLOT(newValues(QHash< QHash<QString,double>, QHash<QString,double> > , QString )), Qt::QueuedConnection);
	connect(j, SIGNAL(results(QHash< QHash<QString,double>, QHash<QString,double> > , QString )),
                &Logger::getInstance(), SLOT(jobResults(QHash< QHash<QString,double>, QHash<QString,double> > , QString )), Qt::QueuedConnection);
        connect(j, SIGNAL(problemReadingResults(Job*,QString)), &Logger::getInstance(), SLOT(jobHasProblemsReadingResult(Job*,QString)), Qt::QueuedConnection);
	emit jobAdded(j, m_jobsQueued.indexOf(j));
}

void Workspace::removeJob(Job* j){
	m_jobsSubmited.removeOne(j);
	m_jobsRunning.removeOne(j);
	m_jobsFinished.removeOne(j);
	m_jobsQueued.removeOne(j);
        disconnect(j, SIGNAL(stateChanged(Job*, Job::State)), this, SLOT(jobStateChanged(Job*, Job::State)));
        disconnect(j, SIGNAL(stateChanged(Job*, Job::State)), &Logger::getInstance(), SLOT(jobStateChanged(Job*, Job::State)));
	disconnect(j, SIGNAL(results(QHash< QHash<QString,double>, QHash<QString,double> > , QString )),
		&m_results, SLOT(newValues(QHash< QHash<QString,double>, QHash<QString,double> > , QString )));
	disconnect(j, SIGNAL(results(QHash< QHash<QString,double>, QHash<QString,double> > , QString )),
		&Logger::getInstance(), SLOT(jobResults(QHash< QHash<QString,double>, QHash<QString,double> > , QString )));
	emit jobRemoved(j);
}

void Workspace::setQueuePosition(Job* j, unsigned int position){
        if(static_cast<int>(position) >= m_jobsQueued.size()) position = m_jobsQueued.size()-1;
	m_jobsQueued.removeOne(j);
	m_jobsQueued.insert(position,j);
	emit jobMoved(j,position);
}

void Workspace::start(){
    populate_session_threads();
    foreach(PJobRunnerSessionThread* thread, m_session_threads){
        thread->start();
    }

    m_running = true;
    emit started();
}

void Workspace::stop(){
	m_running = false;
	emit stopped();
}

void Workspace::jobStateChanged(Job* job, Job::State state){
	switch(state){
                case Job::FINISHED:
			jobFinished(job);
			break;
                case Job::SUBMITED:
			jobSubmited(job);
			break;
                case Job::RUNNING:
			jobStarted(job);
			break;
                case Job::QUEUED:
			break;
	}
}

void Workspace::jobSubmited(Job* j){
	m_jobsQueued.removeOne(j);
	m_jobsSubmited.push_back(j);
}

void Workspace::jobStarted(Job* j){
	m_jobsQueued.removeOne(j);
	m_jobsSubmited.removeOne(j);
	m_jobsRunning.push_back(j);
}

void Workspace::jobFinished(Job* j){
	m_jobsRunning.removeOne(j);
	m_jobsFinished.push_back(j);
}

bool Workspace::isRunning(){
	return m_running;
}

void Workspace::import_results_from_pjobfile(QString filename)
{
	PJobFile file(filename);
	QStringList runList = file.runDirectoryEntries();

	//run_DATUM - Strings herausfiltern
	int i = 0;
	foreach(QString s, runList)
	{
		runList.replace(i++,s.section('/',0,1));
	}

	//doppelte Einträge verwerfen (jeder run soll logischerweise nur einmal geladen werden)
	runList.removeDuplicates();

	unsigned int size = runList.size();
	QString message = QString("Importing results from %1...").arg(filename);
	if(size > 10) emit progress(message,0);
	i = 0;
	foreach(QString s, runList)
	{
		//Results auslesen...
		QHash< QHash<QString,double>, QHash<QString,double> > result;
		try
		{
			result = file.getResultsForRun(s);
		}
		catch(QString s)
		{
			//Ursache z.B.: Dateien unvollständig => parametercombination.xml passt nicht zu den Ergebnissen)
			//=> überspringen
			continue;
		}

		//...und weiterleiten
		m_results.newValues(result,file.pjobFile());
		if(size > 10) emit progress(message,i++*100/size);
		if(m_progresses_to_abort.contains(message)){
			m_progresses_to_abort.remove(message);
			break;
		}
	}
	if(size > 10) emit progress(message,100);
}

void Workspace::abort_progress(const QString& what){
	m_progresses_to_abort.insert(what);
}

Job* Workspace::startNextQueuedJob(){
    QMutexLocker locker(&m_mutex);
    if(m_jobsQueued.isEmpty()) return 0;
    Job* job = m_jobsQueued.takeFirst();
    m_jobsRunning.push_back(job);
    return job;
}

void Workspace::session_finished(){
    if(!m_running) return;
    QObject* object = sender();
    PJobRunnerSessionThread* thread = dynamic_cast<PJobRunnerSessionThread*>(object);
    if(!thread) return;
    thread->start();
}

void Workspace::populate_session_threads(){
    clear_session_threads();
    unsigned int jobs = m_jobsQueued.size();
    foreach(QHostAddress host, PJobRunnerPool::instance().known_pjob_runners()){
        unsigned int thread_count = PJobRunnerPool::instance().thread_count(host);
        for(unsigned int i=0; i<thread_count; i++){
            PJobRunnerSessionThread* thread = new PJobRunnerSessionThread(host, this);
            connect(thread, SIGNAL(finished()), this, SLOT(session_finished()), Qt::QueuedConnection);
            m_session_threads.insert(thread);
            jobs--;
            if(jobs <= 0) return;
        }
    }
}

void Workspace::clear_session_threads(){
    foreach(PJobRunnerSessionThread* thread, m_session_threads){
        if(thread->isRunning()) continue;
        m_session_threads.remove(thread);
        delete thread;
    }
}
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "EmbeddedPython.h"
#include "ModsLocation.h"
#include "ExceptionFetcher.h"
#include "ResourceLoader.h"
#include <iostream>
#include "Logger.h"
#include "ResponseWriter.h"
#include "SQFReader.h"
#include "SQFWriter.h"
#include "Paths.h"
#include "Modules/pythiainternal.h"
#include "Modules/pythialogger.h"

#ifndef _WIN32
#include <dlfcn.h>
#endif

#define THROW_PYEXCEPTION(_msg_) throw std::runtime_error(_msg_ + std::string(": ") + PyExceptionFetcher().getError());
//#define EXTENSION_DEVELOPMENT 1

EmbeddedPython *python = nullptr;
std::string pythonInitializationError = "";
unsigned long multipartCounter = 0;
std::unordered_map<unsigned long long int, multipart_t> multiparts;

namespace
{
    class PyObjectGuard final
    {
    public:
        PyObjectGuard(PyObject* source) : ptr(source)
        {
        }

        ~PyObjectGuard()
        {
            if (ptr != nullptr)
            {
                Py_DECREF(ptr);
            }
        }

        PyObject* get() const
        {
            return ptr;
        }

        explicit operator bool() const
        {
            return ptr != nullptr;
        }

        /// Release ownership
        PyObject* transfer()
        {
            PyObject* tmp = ptr;
            ptr = nullptr;
            return tmp;
        }

    private:
        PyObjectGuard(const PyObjectGuard&) = delete;
        void operator=(const PyObjectGuard&) = delete;

    private:
        PyObject *ptr;
    };
}

std::wstring joinPaths(std::vector<std::wstring> const paths)
{
    std::wstring out;
    bool firstTime = true;
    for(const auto& path : paths)
    {
        if(firstTime)
        {
            firstTime = false;
            out += path;
        }
        else
        {
#ifdef _WIN32
            out += L";";
#else
            out += L":";
#endif
            out += path;
        }
    }
    return out;
}

void EmbeddedPython::DoPythonMagic(tstring path)
{
    // Python pre-initialization magic
    LOG_INFO(std::string("Python version: ") + Py_GetVersion());

    // Clear the env variables, just in case
    #ifdef _WIN32
    _putenv_s("PYTHONHOME", "");
    _putenv_s("PYTHONPATH", "");
    _putenv_s("PYTHONNOUSERSITE", "1");  // Disable custom user site
    std::wstring wpath = path;
    #else
    setenv("PYTHONHOME", "", true);
    setenv("PYTHONPATH", "", true);
    setenv("PYTHONNOUSERSITE", "1", true);  // Disable custom user site

    std::wstring wpath = Logger::s2w(path);
    #endif

    Py_IgnoreEnvironmentFlag = 1;
    Py_IsolatedFlag = 1;
    Py_NoSiteFlag = 1;
    Py_NoUserSiteDirectory = 1;

    // Py_SetPythonHome(L"D:\\Steam\\steamapps\\common\\Arma 3\\@Pythia\\python-embed-amd64");
    this->pythonHomeString = wpath;
    Py_SetPythonHome(pythonHomeString.data());
    LOG_INFO(std::string("Python home: ") + Logger::w2s(Py_GetPythonHome()));

    // Py_SetProgramName(L"D:\\Steam\\steamapps\\common\\Arma 3\\@Pythia\\python-embed-amd64\\python.exe");
#ifdef _WIN32
    this->programNameString = wpath + L"\\python.exe"; // Not sure if that should be the value here
#else
    this->programNameString = wpath + L"/bin/python"; // Not sure if that should be the value here
#endif
    Py_SetProgramName(programNameString.data());
    LOG_INFO(std::string("Program name: ") + Logger::w2s(Py_GetProgramName()));

    /*
    Py_SetPath(L"D:\\Steam\\SteamApps\\common\\Arma 3\\@Pythia\\python-embed-amd64\\python35.zip;"
        L"D:\\Steam\\SteamApps\\common\\Arma 3\\@Pythia\\python-embed-amd64\\DLLs;"
        L"D:\\Steam\\SteamApps\\common\\Arma 3\\@Pythia\\python-embed-amd64\\lib;"
        L"D:\\Steam\\SteamApps\\common\\Arma 3\\@Pythia\\python-embed-amd64;"
        L"D:\\Steam\\SteamApps\\common\\Arma 3\\@Pythia\\python-embed-amd64\\Lib\\site-packages;"
        L"D:\\Steam\\SteamApps\\common\\Arma 3");
    */

    /*
        # Obtain the current paths by running the embedded python binary
        import sys
        base_dir = sys.executable.split('/bin/')[0]
        for path in sys.path:
            print(path.replace(base_dir, ''))
     */

    #ifdef _WIN32
    std::wstring allPaths = joinPaths({
        wpath + L"\\python" PYTHON_VERSION + L".zip",
        wpath + L"\\DLLs",
        wpath + L"\\lib",
        wpath,
        wpath + L"\\Lib\\site-packages",
        getProgramDirectory() // For `python/` directory access. TODO: Use import hooks for that
    });
    #else
    std::wstring allPaths = joinPaths({
        wpath + L"/lib/python" PYTHON_VERSION + L".zip",
        wpath + L"/lib/python" PYTHON_VERSION_DOTTED,
        wpath + L"/lib/python" PYTHON_VERSION_DOTTED L"/lib-dynload",
        wpath + L"/lib/python" PYTHON_VERSION_DOTTED L"/site-packages",
        wpath,
        Logger::s2w(getProgramDirectory()) // For `python/` directory access. TODO: Use import hooks for that
    });
    #endif

    // Not setting PySetPath overwrites the Py_SetProgramName value (it seems to be ignored then),
    Py_SetPath(allPaths.c_str());;
    LOG_INFO(std::string("Python paths: ") + Logger::w2s(Py_GetPath()));
    LOG_INFO(std::string("Current directory: ") + GetCurrentWorkingDir());

    #ifndef _WIN32
    // https://stackoverflow.com/a/60746446/6543759
    // https://docs.python.org/3/whatsnew/3.8.html#changes-in-the-c-api
    // undefined symbol: PyExc_ImportError
    // Manually load libpythonX.Y.so with dlopen(RTLD_GLOBAL) to allow numpy to access python symbols
    // and in Python 3.8+ any C extension
    // FIXME: Technically speaking, this is a leak
    void* const libpython_handle = dlopen("libpython" PYTHON_VERSION_DOTTED "m.so", RTLD_LAZY | RTLD_GLOBAL);
    if(!libpython_handle)
    {
        LOG_INFO("Could not load libpython3.7m.so");
    }
    #endif // ifndef _WIN32
}

EmbeddedPython::EmbeddedPython()
{
    DoPythonMagic(getPythonPath());
    PyImport_AppendInittab("pythiainternal", PyInit_pythiainternal);
    PyImport_AppendInittab("pythialogger", PyInit_pythialogger);
    Py_Initialize();
    PyEval_InitThreads(); // Initialize and acquire GIL
    initialize();
    leavePythonThread();
}

void EmbeddedPython::enterPythonThread()
{
    PyEval_RestoreThread(pThreadState);
}

void EmbeddedPython::leavePythonThread()
{
    pThreadState = PyEval_SaveThread();
}

void EmbeddedPython::initialize()
{
    #ifdef EXTENSION_DEVELOPMENT
    PyObjectGuard mainModuleName(PyUnicode_DecodeFSDefault("python.Adapter"));
    if (!mainModuleName)
    {
        THROW_PYEXCEPTION("Failed to create unicode module name");
    }

    PyObjectGuard moduleOriginal(PyImport_Import(mainModuleName.get()));
    if (!moduleOriginal)
    {
        THROW_PYEXCEPTION("Failed to import adapter module");
    }

    // Reload the module to force re-reading the file
    PyObjectGuard module(PyImport_ReloadModule(moduleOriginal.get()));
    if (!module)
    {
        THROW_PYEXCEPTION("Failed to reload adapter module");
    }

    #else

    const std::string text_resource = ResourceLoader::loadTextResource();
    PyObject *compiledString = Py_CompileString(
        text_resource.c_str(),
        "python-adapter.py",
        Py_file_input);

    PyObjectGuard pCompiledContents(compiledString);
    if (!pCompiledContents)
    {
        THROW_PYEXCEPTION("Failed to compile embedded python module");
    }

    PyObjectGuard module(PyImport_ExecCodeModule("adapter", pCompiledContents.get()));
    if (!module)
    {
        THROW_PYEXCEPTION("Failed to add compiled module");
    }
    #endif

    PyObjectGuard function(PyObject_GetAttrString(module.get(), "python_adapter"));
    if (!function || !PyCallable_Check(function.get()))
    {
        THROW_PYEXCEPTION("Failed to reference python function 'python_adapter'");
    }

    pModule = module.transfer();
    pFunc = function.transfer();
}

void EmbeddedPython::initModules(modules_t mods)
{
    /**
    Initialize python sources for modules.
    The sources passed here will be used to import `pythia.modulename`.
    */

    if (!pModule)
    {
        THROW_PYEXCEPTION("Pythia adapter not loaded correctly. Not initializing python modules.")
    }

    PyObjectGuard pDict(PyDict_New());
    if (!pDict)
    {
        THROW_PYEXCEPTION("Could not create a new python dict.")
    }

    // Fill the dict with the items in the unordered_map
    for (const auto& entry: mods)
    {
        #ifdef _WIN32
        PyObjectGuard pString(PyUnicode_FromWideChar(entry.second.c_str(), -1));
        #else
        PyObjectGuard pString(PyUnicode_FromString(entry.second.c_str()));
        #endif
        if (!pString)
        {
            continue;
        }

        int retval = PyDict_SetItemString(pDict.get(), entry.first.c_str(), pString.get());
        if (retval == -1)
        {
            THROW_PYEXCEPTION("Error while running PyDict_SetItemString.")
        }
    }

    // Perform the call adding the mods sources
    PyObjectGuard function(PyObject_GetAttrString(pModule, "init_modules"));
    if (!function || !PyCallable_Check(function.get()))
    {
        THROW_PYEXCEPTION("Failed to reference python function 'init_modules'");
    }

    PyObjectGuard pResult(PyObject_CallFunctionObjArgs(function.get(), pDict.get(), NULL));
    if (pResult)
    {
        return; // Yay!
    }
    else
    {
        THROW_PYEXCEPTION("Failed to execute python init_modules function");
    }
}

void EmbeddedPython::deinitialize()
{
    Py_CLEAR(pFunc);
    Py_CLEAR(pModule);
}

void EmbeddedPython::reload()
{
    deinitialize();
    try
    {
        initialize();
        LOG_INFO("Python extension successfully reloaded");
    }
    catch (const std::exception& ex)
    {
        LOG_ERROR(std::string("Caught error when reloading the extension: ") + ex.what());
        pythonInitializationError = ex.what();
    }
}

EmbeddedPython::~EmbeddedPython()
{
    enterPythonThread();
    deinitialize();
    Py_Finalize();
}

// Note: outputSize is the size CONTAINING the null terminator
void handleMultipart(char *output, int outputSize, multipart_t entry)
{
    if (entry.empty())
    {
        return;
    }

    multiparts[multipartCounter] = entry;

    //["m", MULTIPART_COUNTER, len(responses)]
    snprintf(output, outputSize - 1, "[\"m\",%lu,%lu]", multipartCounter++, (unsigned long)entry.size());
}

// Note: outputSize is the size CONTAINING the null terminator
void returnMultipart(unsigned long multipartID, char *output, int outputSize)
{
    try
    {
        auto &entry = multiparts.at(multipartID);
        auto &retval = entry.front();

        size_t minSize = std::min<size_t>((size_t)outputSize, retval.size() + 1);
        snprintf(output, minSize, "%s", retval.data());

        entry.pop();
        if (entry.empty())
        {
            multiparts.erase(multipartID);
        }
    }
    catch (std::out_of_range)
    {
        output[0] = '\0';
    }
}

// Note: outputSize is the size CONTAINING the null terminator
// A value of 10240 means that you can have 10239 characters + '\0' there
void EmbeddedPython::execute(char *output, int outputSize, const char *input)
{
    #ifdef EXTENSION_DEVELOPMENT
        reload();
    #endif

    auto timeStart = std::chrono::high_resolution_clock::now();
    if (!pFunc)
    {
        LOG_ERROR("Calling function {}", input);
        throw std::runtime_error("No bootstrapping function. Additional error: " + pythonInitializationError);
    }

    PyObjectGuard pArgs(SQFReader::decode(input));
    auto timeDecodeEnded = std::chrono::high_resolution_clock::now();

    PyObjectGuard pTuple(PyTuple_Pack(1, pArgs.get()));
    if (!pTuple)
    {
        LOG_ERROR("Calling function {}", input);
        throw std::runtime_error("Failed to convert argument string to tuple");
    }

    PyObject* PyFunction = PyList_GetItem(pArgs.get(), 0); // Borrows reference
    if (PyFunction)
    {
        // Multipart
        // TODO: Do a Python string comparison
        if (PyUnicode_CompareWithASCIIString(PyFunction, "pythia.multipart") == 0)
        {
            PyObject* PyMultipartID = PyList_GetItem(pArgs.get(), 1); // Borrows reference
            if (PyMultipartID)
            {
                int overflow;
                long multipartID = PyLong_AsLongAndOverflow(PyMultipartID, &overflow);
                if (overflow == 0 && multipartID >= 0)
                {
                    returnMultipart(multipartID, output, outputSize);
                    // Do not log multipart requests for performance reasons
                    return;
                }
                else
                {
                    LOG_ERROR("Calling function {}", input);
                    throw std::runtime_error("Could not read the multipart ID");
                }
            }
            else
            {
                LOG_ERROR("Calling function {}", input);
                throw std::runtime_error("Could not get the multipart ID from the request");
            }
        }
    }
    else
    {
        LOG_ERROR("Calling function {}", input);
        throw std::runtime_error("Failed to get the function name from the request");
    }

    auto timeAfterMultipartCheck = std::chrono::high_resolution_clock::now();

    PyObjectGuard pResult(PyObject_CallObject(pFunc, pTuple.get()));
    auto timeAfterCall = std::chrono::high_resolution_clock::now();
    if (pResult)
    {
        MultipartResponseWriter writer(output, outputSize);
        writer.initialize();
        SQFWriter::encode(pResult.get(), &writer);
        writer.finalize();

        auto multipartResponse = writer.getMultipart();
        handleMultipart(output, outputSize, multipartResponse);
        auto timeEnd = std::chrono::high_resolution_clock::now();
        LOG_INFO(
            "Calling function {}(...). Total: {}us", //, Decoding: {}us, Call: {}us, Encoding: {}us, Multipart: {}us",
            PyUnicode_AsUTF8(PyFunction),
            (std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeStart)).count()/*,
            (std::chrono::duration_cast<std::chrono::microseconds>(timeDecodeEnded - timeStart)).count(),
            (std::chrono::duration_cast<std::chrono::microseconds>(timeAfterCall - timeAfterMultipartCheck)).count(),
            (std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeAfterCall)).count(),
            (std::chrono::duration_cast<std::chrono::microseconds>(timeAfterMultipartCheck - timeDecodeEnded)).count()*/
        );
        return;
    }
    else
    {
        LOG_ERROR("Calling function {}", input);
        THROW_PYEXCEPTION("Failed to execute python extension");
    }
}
<commit_msg>Using outputSize - 1 is not needed<commit_after>#include "stdafx.h"
#include "EmbeddedPython.h"
#include "ModsLocation.h"
#include "ExceptionFetcher.h"
#include "ResourceLoader.h"
#include <iostream>
#include "Logger.h"
#include "ResponseWriter.h"
#include "SQFReader.h"
#include "SQFWriter.h"
#include "Paths.h"
#include "Modules/pythiainternal.h"
#include "Modules/pythialogger.h"

#ifndef _WIN32
#include <dlfcn.h>
#endif

#define THROW_PYEXCEPTION(_msg_) throw std::runtime_error(_msg_ + std::string(": ") + PyExceptionFetcher().getError());
//#define EXTENSION_DEVELOPMENT 1

EmbeddedPython *python = nullptr;
std::string pythonInitializationError = "";
unsigned long multipartCounter = 0;
std::unordered_map<unsigned long long int, multipart_t> multiparts;

namespace
{
    class PyObjectGuard final
    {
    public:
        PyObjectGuard(PyObject* source) : ptr(source)
        {
        }

        ~PyObjectGuard()
        {
            if (ptr != nullptr)
            {
                Py_DECREF(ptr);
            }
        }

        PyObject* get() const
        {
            return ptr;
        }

        explicit operator bool() const
        {
            return ptr != nullptr;
        }

        /// Release ownership
        PyObject* transfer()
        {
            PyObject* tmp = ptr;
            ptr = nullptr;
            return tmp;
        }

    private:
        PyObjectGuard(const PyObjectGuard&) = delete;
        void operator=(const PyObjectGuard&) = delete;

    private:
        PyObject *ptr;
    };
}

std::wstring joinPaths(std::vector<std::wstring> const paths)
{
    std::wstring out;
    bool firstTime = true;
    for(const auto& path : paths)
    {
        if(firstTime)
        {
            firstTime = false;
            out += path;
        }
        else
        {
#ifdef _WIN32
            out += L";";
#else
            out += L":";
#endif
            out += path;
        }
    }
    return out;
}

void EmbeddedPython::DoPythonMagic(tstring path)
{
    // Python pre-initialization magic
    LOG_INFO(std::string("Python version: ") + Py_GetVersion());

    // Clear the env variables, just in case
    #ifdef _WIN32
    _putenv_s("PYTHONHOME", "");
    _putenv_s("PYTHONPATH", "");
    _putenv_s("PYTHONNOUSERSITE", "1");  // Disable custom user site
    std::wstring wpath = path;
    #else
    setenv("PYTHONHOME", "", true);
    setenv("PYTHONPATH", "", true);
    setenv("PYTHONNOUSERSITE", "1", true);  // Disable custom user site

    std::wstring wpath = Logger::s2w(path);
    #endif

    Py_IgnoreEnvironmentFlag = 1;
    Py_IsolatedFlag = 1;
    Py_NoSiteFlag = 1;
    Py_NoUserSiteDirectory = 1;

    // Py_SetPythonHome(L"D:\\Steam\\steamapps\\common\\Arma 3\\@Pythia\\python-embed-amd64");
    this->pythonHomeString = wpath;
    Py_SetPythonHome(pythonHomeString.data());
    LOG_INFO(std::string("Python home: ") + Logger::w2s(Py_GetPythonHome()));

    // Py_SetProgramName(L"D:\\Steam\\steamapps\\common\\Arma 3\\@Pythia\\python-embed-amd64\\python.exe");
#ifdef _WIN32
    this->programNameString = wpath + L"\\python.exe"; // Not sure if that should be the value here
#else
    this->programNameString = wpath + L"/bin/python"; // Not sure if that should be the value here
#endif
    Py_SetProgramName(programNameString.data());
    LOG_INFO(std::string("Program name: ") + Logger::w2s(Py_GetProgramName()));

    /*
    Py_SetPath(L"D:\\Steam\\SteamApps\\common\\Arma 3\\@Pythia\\python-embed-amd64\\python35.zip;"
        L"D:\\Steam\\SteamApps\\common\\Arma 3\\@Pythia\\python-embed-amd64\\DLLs;"
        L"D:\\Steam\\SteamApps\\common\\Arma 3\\@Pythia\\python-embed-amd64\\lib;"
        L"D:\\Steam\\SteamApps\\common\\Arma 3\\@Pythia\\python-embed-amd64;"
        L"D:\\Steam\\SteamApps\\common\\Arma 3\\@Pythia\\python-embed-amd64\\Lib\\site-packages;"
        L"D:\\Steam\\SteamApps\\common\\Arma 3");
    */

    /*
        # Obtain the current paths by running the embedded python binary
        import sys
        base_dir = sys.executable.split('/bin/')[0]
        for path in sys.path:
            print(path.replace(base_dir, ''))
     */

    #ifdef _WIN32
    std::wstring allPaths = joinPaths({
        wpath + L"\\python" PYTHON_VERSION + L".zip",
        wpath + L"\\DLLs",
        wpath + L"\\lib",
        wpath,
        wpath + L"\\Lib\\site-packages",
        getProgramDirectory() // For `python/` directory access. TODO: Use import hooks for that
    });
    #else
    std::wstring allPaths = joinPaths({
        wpath + L"/lib/python" PYTHON_VERSION + L".zip",
        wpath + L"/lib/python" PYTHON_VERSION_DOTTED,
        wpath + L"/lib/python" PYTHON_VERSION_DOTTED L"/lib-dynload",
        wpath + L"/lib/python" PYTHON_VERSION_DOTTED L"/site-packages",
        wpath,
        Logger::s2w(getProgramDirectory()) // For `python/` directory access. TODO: Use import hooks for that
    });
    #endif

    // Not setting PySetPath overwrites the Py_SetProgramName value (it seems to be ignored then),
    Py_SetPath(allPaths.c_str());;
    LOG_INFO(std::string("Python paths: ") + Logger::w2s(Py_GetPath()));
    LOG_INFO(std::string("Current directory: ") + GetCurrentWorkingDir());

    #ifndef _WIN32
    // https://stackoverflow.com/a/60746446/6543759
    // https://docs.python.org/3/whatsnew/3.8.html#changes-in-the-c-api
    // undefined symbol: PyExc_ImportError
    // Manually load libpythonX.Y.so with dlopen(RTLD_GLOBAL) to allow numpy to access python symbols
    // and in Python 3.8+ any C extension
    // FIXME: Technically speaking, this is a leak
    void* const libpython_handle = dlopen("libpython" PYTHON_VERSION_DOTTED "m.so", RTLD_LAZY | RTLD_GLOBAL);
    if(!libpython_handle)
    {
        LOG_INFO("Could not load libpython3.7m.so");
    }
    #endif // ifndef _WIN32
}

EmbeddedPython::EmbeddedPython()
{
    DoPythonMagic(getPythonPath());
    PyImport_AppendInittab("pythiainternal", PyInit_pythiainternal);
    PyImport_AppendInittab("pythialogger", PyInit_pythialogger);
    Py_Initialize();
    PyEval_InitThreads(); // Initialize and acquire GIL
    initialize();
    leavePythonThread();
}

void EmbeddedPython::enterPythonThread()
{
    PyEval_RestoreThread(pThreadState);
}

void EmbeddedPython::leavePythonThread()
{
    pThreadState = PyEval_SaveThread();
}

void EmbeddedPython::initialize()
{
    #ifdef EXTENSION_DEVELOPMENT
    PyObjectGuard mainModuleName(PyUnicode_DecodeFSDefault("python.Adapter"));
    if (!mainModuleName)
    {
        THROW_PYEXCEPTION("Failed to create unicode module name");
    }

    PyObjectGuard moduleOriginal(PyImport_Import(mainModuleName.get()));
    if (!moduleOriginal)
    {
        THROW_PYEXCEPTION("Failed to import adapter module");
    }

    // Reload the module to force re-reading the file
    PyObjectGuard module(PyImport_ReloadModule(moduleOriginal.get()));
    if (!module)
    {
        THROW_PYEXCEPTION("Failed to reload adapter module");
    }

    #else

    const std::string text_resource = ResourceLoader::loadTextResource();
    PyObject *compiledString = Py_CompileString(
        text_resource.c_str(),
        "python-adapter.py",
        Py_file_input);

    PyObjectGuard pCompiledContents(compiledString);
    if (!pCompiledContents)
    {
        THROW_PYEXCEPTION("Failed to compile embedded python module");
    }

    PyObjectGuard module(PyImport_ExecCodeModule("adapter", pCompiledContents.get()));
    if (!module)
    {
        THROW_PYEXCEPTION("Failed to add compiled module");
    }
    #endif

    PyObjectGuard function(PyObject_GetAttrString(module.get(), "python_adapter"));
    if (!function || !PyCallable_Check(function.get()))
    {
        THROW_PYEXCEPTION("Failed to reference python function 'python_adapter'");
    }

    pModule = module.transfer();
    pFunc = function.transfer();
}

void EmbeddedPython::initModules(modules_t mods)
{
    /**
    Initialize python sources for modules.
    The sources passed here will be used to import `pythia.modulename`.
    */

    if (!pModule)
    {
        THROW_PYEXCEPTION("Pythia adapter not loaded correctly. Not initializing python modules.")
    }

    PyObjectGuard pDict(PyDict_New());
    if (!pDict)
    {
        THROW_PYEXCEPTION("Could not create a new python dict.")
    }

    // Fill the dict with the items in the unordered_map
    for (const auto& entry: mods)
    {
        #ifdef _WIN32
        PyObjectGuard pString(PyUnicode_FromWideChar(entry.second.c_str(), -1));
        #else
        PyObjectGuard pString(PyUnicode_FromString(entry.second.c_str()));
        #endif
        if (!pString)
        {
            continue;
        }

        int retval = PyDict_SetItemString(pDict.get(), entry.first.c_str(), pString.get());
        if (retval == -1)
        {
            THROW_PYEXCEPTION("Error while running PyDict_SetItemString.")
        }
    }

    // Perform the call adding the mods sources
    PyObjectGuard function(PyObject_GetAttrString(pModule, "init_modules"));
    if (!function || !PyCallable_Check(function.get()))
    {
        THROW_PYEXCEPTION("Failed to reference python function 'init_modules'");
    }

    PyObjectGuard pResult(PyObject_CallFunctionObjArgs(function.get(), pDict.get(), NULL));
    if (pResult)
    {
        return; // Yay!
    }
    else
    {
        THROW_PYEXCEPTION("Failed to execute python init_modules function");
    }
}

void EmbeddedPython::deinitialize()
{
    Py_CLEAR(pFunc);
    Py_CLEAR(pModule);
}

void EmbeddedPython::reload()
{
    deinitialize();
    try
    {
        initialize();
        LOG_INFO("Python extension successfully reloaded");
    }
    catch (const std::exception& ex)
    {
        LOG_ERROR(std::string("Caught error when reloading the extension: ") + ex.what());
        pythonInitializationError = ex.what();
    }
}

EmbeddedPython::~EmbeddedPython()
{
    enterPythonThread();
    deinitialize();
    Py_Finalize();
}

// Note: outputSize is the size CONTAINING the null terminator
void handleMultipart(char *output, int outputSize, multipart_t entry)
{
    if (entry.empty())
    {
        return;
    }

    multiparts[multipartCounter] = entry;

    //["m", MULTIPART_COUNTER, len(responses)]
    snprintf(output, outputSize, "[\"m\",%lu,%lu]", multipartCounter++, (unsigned long)entry.size());
}

// Note: outputSize is the size CONTAINING the null terminator
void returnMultipart(unsigned long multipartID, char *output, int outputSize)
{
    try
    {
        auto &entry = multiparts.at(multipartID);
        auto &retval = entry.front();

        size_t minSize = std::min<size_t>((size_t)outputSize, retval.size() + 1);
        snprintf(output, minSize, "%s", retval.data());

        entry.pop();
        if (entry.empty())
        {
            multiparts.erase(multipartID);
        }
    }
    catch (std::out_of_range)
    {
        output[0] = '\0';
    }
}

// Note: outputSize is the size CONTAINING the null terminator
// A value of 10240 means that you can have 10239 characters + '\0' there
void EmbeddedPython::execute(char *output, int outputSize, const char *input)
{
    #ifdef EXTENSION_DEVELOPMENT
        reload();
    #endif

    auto timeStart = std::chrono::high_resolution_clock::now();
    if (!pFunc)
    {
        LOG_ERROR("Calling function {}", input);
        throw std::runtime_error("No bootstrapping function. Additional error: " + pythonInitializationError);
    }

    PyObjectGuard pArgs(SQFReader::decode(input));
    auto timeDecodeEnded = std::chrono::high_resolution_clock::now();

    PyObjectGuard pTuple(PyTuple_Pack(1, pArgs.get()));
    if (!pTuple)
    {
        LOG_ERROR("Calling function {}", input);
        throw std::runtime_error("Failed to convert argument string to tuple");
    }

    PyObject* PyFunction = PyList_GetItem(pArgs.get(), 0); // Borrows reference
    if (PyFunction)
    {
        // Multipart
        // TODO: Do a Python string comparison
        if (PyUnicode_CompareWithASCIIString(PyFunction, "pythia.multipart") == 0)
        {
            PyObject* PyMultipartID = PyList_GetItem(pArgs.get(), 1); // Borrows reference
            if (PyMultipartID)
            {
                int overflow;
                long multipartID = PyLong_AsLongAndOverflow(PyMultipartID, &overflow);
                if (overflow == 0 && multipartID >= 0)
                {
                    returnMultipart(multipartID, output, outputSize);
                    // Do not log multipart requests for performance reasons
                    return;
                }
                else
                {
                    LOG_ERROR("Calling function {}", input);
                    throw std::runtime_error("Could not read the multipart ID");
                }
            }
            else
            {
                LOG_ERROR("Calling function {}", input);
                throw std::runtime_error("Could not get the multipart ID from the request");
            }
        }
    }
    else
    {
        LOG_ERROR("Calling function {}", input);
        throw std::runtime_error("Failed to get the function name from the request");
    }

    auto timeAfterMultipartCheck = std::chrono::high_resolution_clock::now();

    PyObjectGuard pResult(PyObject_CallObject(pFunc, pTuple.get()));
    auto timeAfterCall = std::chrono::high_resolution_clock::now();
    if (pResult)
    {
        MultipartResponseWriter writer(output, outputSize);
        writer.initialize();
        SQFWriter::encode(pResult.get(), &writer);
        writer.finalize();

        auto multipartResponse = writer.getMultipart();
        handleMultipart(output, outputSize, multipartResponse);
        auto timeEnd = std::chrono::high_resolution_clock::now();
        LOG_INFO(
            "Calling function {}(...). Total: {}us", //, Decoding: {}us, Call: {}us, Encoding: {}us, Multipart: {}us",
            PyUnicode_AsUTF8(PyFunction),
            (std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeStart)).count()/*,
            (std::chrono::duration_cast<std::chrono::microseconds>(timeDecodeEnded - timeStart)).count(),
            (std::chrono::duration_cast<std::chrono::microseconds>(timeAfterCall - timeAfterMultipartCheck)).count(),
            (std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeAfterCall)).count(),
            (std::chrono::duration_cast<std::chrono::microseconds>(timeAfterMultipartCheck - timeDecodeEnded)).count()*/
        );
        return;
    }
    else
    {
        LOG_ERROR("Calling function {}", input);
        THROW_PYEXCEPTION("Failed to execute python extension");
    }
}
<|endoftext|>


<commit_before>/*
  NUI3 - C++ cross-platform GUI framework for OpenGL based applications
  Copyright (C) 2002-2003 Sebastien Metrot

  licence: see nui3/LICENCE.TXT
*/

// nuiGradient.cpp

#include "nui.h"
#include "nuiColor.h"
#include "nuiRect.h"
#include "nuiGradient.h"
#include "nuiDrawContext.h"
#include "nuiContour.h"

nuiGradient::nuiGradient()
{
}

nuiGradient::nuiGradient(const nuiGradient& rGradient)
{
  mStops.assign(rGradient.GetStopList().begin(), rGradient.GetStopList().end());
}

nuiGradient::~nuiGradient()
{
}

static bool PositionIsLesser(const nuiGradientPair& rElem1, const nuiGradientPair& rElem2)
{
  return rElem1.first < rElem2.first;
}

void nuiGradient::AddStop(const nuiColor& rColor, nuiSize position)
{
  mStops.push_back(nuiGradientPair(position, rColor));
  mStops.sort(PositionIsLesser);
}

const nuiGradientStopList& nuiGradient::GetStopList() const
{
  return mStops;
}

const nuiColor nuiGradient::GetColorAt(nuiSize position) const
{
  if (mStops.size() == 0)
  {
    return nuiColor(0.f, 0.f, 0.f);
  }

  nuiGradientStopList::const_iterator stopIt = mStops.begin();
  nuiGradientStopList::const_iterator stopEnd = mStops.end();
  if (position <= (*stopIt).first)
  {
    return (*stopIt).second;
  }

  bool found = false;
  nuiGradientPair leftStop;
  for (; stopIt != stopEnd && !found; ++stopIt)
  {
    if (position > (*stopIt).first)
    {
      found = true;
      leftStop = *stopIt;
    }
  }

    if (stopIt == stopEnd)
    {
      return leftStop.second;
    }
    else
    {
      nuiGradientPair rightStop = *stopIt;
      float ratio = (position - leftStop.first) / (rightStop.first - leftStop.first);
      float red = ((rightStop.second.Red() - leftStop.second.Red()) * ratio) + leftStop.second.Red();
      float green = ((rightStop.second.Green() - leftStop.second.Green()) * ratio) + leftStop.second.Green();
      float blue = ((rightStop.second.Blue() - leftStop.second.Blue()) * ratio) + leftStop.second.Blue();
      float alpha = ((rightStop.second.Alpha() - leftStop.second.Alpha()) * ratio) + leftStop.second.Alpha();
      return nuiColor(red, green, blue, alpha);
    }
}

nuiTexture* nuiGradient::CreateTexture(int32 size, nglImagePixelFormat format) const
{
  //  int32 bpp = 32;
  //  switch (format)
  //  {
  //    case eImagePixelRGB:
  //      {
  //        bpp = 24;
  //      }
  //      break;
  //    case eImagePixelRGBA:
  //      {
  //        bpp = 32;
  //      }
  //      break;
  //    case eImagePixelAlpha:
  //    case eImagePixelLum:
  //      {
  //        bpp = 8;
  //      }
  //      break;
  //    case eImagePixelLumA:
  //      {
  //        bpp = 16;
  //      }
  //      break;
  //  }

  format = eImagePixelRGBA;
  nglImageInfo imageinfo(size, 1, 32);
  imageinfo.mPixelFormat = format;
  imageinfo.AllocateBuffer();

  uint32* pBuffer = (uint32*)imageinfo.mpBuffer;

  nuiGradientStopList::const_iterator it = mStops.begin();
  nuiGradientStopList::const_iterator end = mStops.end();

  if (it == end)
    memset(pBuffer, 0, size * 4);
  float pos0 = 0;
  nuiColor col0 = it->second;

  while (it != end)
  {
    float pos1 = it->first;
    nuiColor col1 = it->second;

    int32 ipos0 = ToBelow(pos0) * size;
    int32 ipos1 = ToBelow(pos1) * size;

    float r = 0;
    const float incr = 1.0f / (pos1 - pos0);
    for (int32 i = pos0; i < pos1; i++)
    {
      nuiColor col(col0);
      col.Mix(col1, r);
      *pBuffer = col.GetRGBA();
      pBuffer++;
    }

    pos0 = pos1;
    col0 = col1;

    ++it;
  }

  // go to the end of the line:
  float pos1 = size;

  int32 ipos0 = ToBelow(pos0) * size;
  int32 ipos1 = ToBelow(pos1) * size;

  float r = 0;
  const float incr = 1.0f / (pos1 - pos0);
  for (int32 i = pos0; i < pos1; i++)
  {
    *pBuffer = col0.GetRGBA();
    pBuffer++;
  }

  nglImage* pImage = new nglImage(imageinfo);
  nuiTexture* pTexture = nuiTexture::GetTexture(pImage, true);
  return pTexture;
}

//////////////

nuiReflection::nuiReflection(float Intensity, float Center)
{
  mColor = nuiColor(1.f,1.f,1.f, Intensity);
  mCenter = Center;
  mpSkyGradient = NULL;
  mpGroundGradient = NULL;
  mRecalc = true;
}

nuiReflection::~nuiReflection()
{
  delete mpSkyGradient;
  delete mpGroundGradient;
}

void nuiReflection::Draw(nuiDrawContext* pContext, const nuiRect& rRect, nuiShape* pShp)
{
  if (!(mRect == rRect))
    mRecalc = true;

  mRect = rRect;
  float x = mRect.Left();
  float y = mRect.Top();

  float h = mRect.GetHeight();
  if (mRecalc)
    Recalc();

  // Draw the sky:
  pContext->SetBlendFunc(nuiBlendTransp);
  pContext->DrawGradient(*mpSkyGradient, mRect, x, y, x, y+mCenter * h);

  // Draw the ground:
  pContext->SetBlendFunc(nuiBlendTransp);
  pContext->DrawGradient(*mpGroundGradient, mRect, x, y + mCenter * h, x, y + h);
}

void nuiReflection::Recalc()
{
  float x = mRect.Left();
  float y = mRect.Top();
  float w = mRect.GetWidth();
  float h = mRect.GetHeight();
  float h_2 = h * mCenter;

  mRecalc = false;

  delete mpSkyGradient;
  delete mpGroundGradient;
  mpSkyGradient = new nuiGradient();
  mpGroundGradient = new nuiGradient();

  nuiColor tmp;

  // Sky:
  tmp = mColor;
  float alpha = mColor.Alpha();
  tmp.SetOpacity(alpha * .2f);
  tmp.Crop();
  mpSkyGradient->AddStop(tmp, 0);

  mpSkyGradient->AddStop(nuiColor(1.f,1.f,1.f, alpha), 0.1f);

  tmp.SetOpacity(alpha * .6f);
  tmp.Crop();
  mpSkyGradient->AddStop(tmp, .4f);

  tmp.SetOpacity(alpha * .4f);
  tmp.Crop();
  mpSkyGradient->AddStop(tmp, 1);

  // Ground:
  mpGroundGradient = new nuiGradient();
  tmp.SetOpacity(alpha * .1f);
  tmp.Crop();
  mpGroundGradient->AddStop(tmp, 0);

  tmp.SetOpacity(alpha * .4f);
  tmp.Crop();
  mpGroundGradient->AddStop(tmp, .8f);

  tmp.SetOpacity(alpha *.6f);
  tmp.Crop();
  mpGroundGradient->AddStop(tmp, 1);
}

<commit_msg>started to debug nuiGradient texture (from Seb Metrot)<commit_after>/*
  NUI3 - C++ cross-platform GUI framework for OpenGL based applications
  Copyright (C) 2002-2003 Sebastien Metrot

  licence: see nui3/LICENCE.TXT
*/

// nuiGradient.cpp

#include "nui.h"
#include "nuiColor.h"
#include "nuiRect.h"
#include "nuiGradient.h"
#include "nuiDrawContext.h"
#include "nuiContour.h"

nuiGradient::nuiGradient()
{
}

nuiGradient::nuiGradient(const nuiGradient& rGradient)
{
  mStops.assign(rGradient.GetStopList().begin(), rGradient.GetStopList().end());
}

nuiGradient::~nuiGradient()
{
}

static bool PositionIsLesser(const nuiGradientPair& rElem1, const nuiGradientPair& rElem2)
{
  return rElem1.first < rElem2.first;
}

void nuiGradient::AddStop(const nuiColor& rColor, nuiSize position)
{
  mStops.push_back(nuiGradientPair(position, rColor));
  mStops.sort(PositionIsLesser);
}

const nuiGradientStopList& nuiGradient::GetStopList() const
{
  return mStops;
}

const nuiColor nuiGradient::GetColorAt(nuiSize position) const
{
  if (mStops.size() == 0)
  {
    return nuiColor(0.f, 0.f, 0.f);
  }

  nuiGradientStopList::const_iterator stopIt = mStops.begin();
  nuiGradientStopList::const_iterator stopEnd = mStops.end();
  if (position <= (*stopIt).first)
  {
    return (*stopIt).second;
  }

  bool found = false;
  nuiGradientPair leftStop;
  for (; stopIt != stopEnd && !found; ++stopIt)
  {
    if (position > (*stopIt).first)
    {
      found = true;
      leftStop = *stopIt;
    }
  }

    if (stopIt == stopEnd)
    {
      return leftStop.second;
    }
    else
    {
      nuiGradientPair rightStop = *stopIt;
      float ratio = (position - leftStop.first) / (rightStop.first - leftStop.first);
      float red = ((rightStop.second.Red() - leftStop.second.Red()) * ratio) + leftStop.second.Red();
      float green = ((rightStop.second.Green() - leftStop.second.Green()) * ratio) + leftStop.second.Green();
      float blue = ((rightStop.second.Blue() - leftStop.second.Blue()) * ratio) + leftStop.second.Blue();
      float alpha = ((rightStop.second.Alpha() - leftStop.second.Alpha()) * ratio) + leftStop.second.Alpha();
      return nuiColor(red, green, blue, alpha);
    }
}

nuiTexture* nuiGradient::CreateTexture(int32 size, nglImagePixelFormat format) const
{
  //  int32 bpp = 32;
  //  switch (format)
  //  {
  //    case eImagePixelRGB:
  //      {
  //        bpp = 24;
  //      }
  //      break;
  //    case eImagePixelRGBA:
  //      {
  //        bpp = 32;
  //      }
  //      break;
  //    case eImagePixelAlpha:
  //    case eImagePixelLum:
  //      {
  //        bpp = 8;
  //      }
  //      break;
  //    case eImagePixelLumA:
  //      {
  //        bpp = 16;
  //      }
  //      break;
  //  }

  format = eImagePixelRGBA;
  nglImageInfo imageinfo(size, 1, 32);
  imageinfo.mPixelFormat = format;
  imageinfo.AllocateBuffer();

  uint32* pBuffer = (uint32*)imageinfo.mpBuffer;

  nuiGradientStopList::const_iterator it = mStops.begin();
  nuiGradientStopList::const_iterator end = mStops.end();

  if (it == end)
    memset(pBuffer, 0, size * 4);
  float pos0 = 0;
  nuiColor col0 = it->second;

  while (it != end)
  {
    float pos1 = it->first;
    nuiColor col1 = it->second;

    int32 ipos0 = ToBelow(pos0 * size);
    int32 ipos1 = ToBelow(pos1 * size);

    float r = 0;
    const float incr = 1.0f / (pos1 - pos0);
    for (int32 i = pos0; i < pos1; i++)
    {
      nuiColor col(col0);
      col.Mix(col1, r);
      *pBuffer = col.GetRGBA();
      pBuffer++;
    }

    pos0 = pos1;
    col0 = col1;

    ++it;
  }

  // go to the end of the line:
  float pos1 = size;

  int32 ipos0 = ToBelow(pos0 * size);
  int32 ipos1 = ToBelow(pos1 * size);

  float r = 0;
  const float incr = 1.0f / (pos1 - pos0);
  for (int32 i = pos0; i < pos1; i++)
  {
    *pBuffer = col0.GetRGBA();
    pBuffer++;
  }

  nglImage* pImage = new nglImage(imageinfo);
  nuiTexture* pTexture = nuiTexture::GetTexture(pImage, true);
  return pTexture;
}

//////////////

nuiReflection::nuiReflection(float Intensity, float Center)
{
  mColor = nuiColor(1.f,1.f,1.f, Intensity);
  mCenter = Center;
  mpSkyGradient = NULL;
  mpGroundGradient = NULL;
  mRecalc = true;
}

nuiReflection::~nuiReflection()
{
  delete mpSkyGradient;
  delete mpGroundGradient;
}

void nuiReflection::Draw(nuiDrawContext* pContext, const nuiRect& rRect, nuiShape* pShp)
{
  if (!(mRect == rRect))
    mRecalc = true;

  mRect = rRect;
  float x = mRect.Left();
  float y = mRect.Top();

  float h = mRect.GetHeight();
  if (mRecalc)
    Recalc();

  // Draw the sky:
  pContext->SetBlendFunc(nuiBlendTransp);
  pContext->DrawGradient(*mpSkyGradient, mRect, x, y, x, y+mCenter * h);

  // Draw the ground:
  pContext->SetBlendFunc(nuiBlendTransp);
  pContext->DrawGradient(*mpGroundGradient, mRect, x, y + mCenter * h, x, y + h);
}

void nuiReflection::Recalc()
{
  float x = mRect.Left();
  float y = mRect.Top();
  float w = mRect.GetWidth();
  float h = mRect.GetHeight();
  float h_2 = h * mCenter;

  mRecalc = false;

  delete mpSkyGradient;
  delete mpGroundGradient;
  mpSkyGradient = new nuiGradient();
  mpGroundGradient = new nuiGradient();

  nuiColor tmp;

  // Sky:
  tmp = mColor;
  float alpha = mColor.Alpha();
  tmp.SetOpacity(alpha * .2f);
  tmp.Crop();
  mpSkyGradient->AddStop(tmp, 0);

  mpSkyGradient->AddStop(nuiColor(1.f,1.f,1.f, alpha), 0.1f);

  tmp.SetOpacity(alpha * .6f);
  tmp.Crop();
  mpSkyGradient->AddStop(tmp, .4f);

  tmp.SetOpacity(alpha * .4f);
  tmp.Crop();
  mpSkyGradient->AddStop(tmp, 1);

  // Ground:
  mpGroundGradient = new nuiGradient();
  tmp.SetOpacity(alpha * .1f);
  tmp.Crop();
  mpGroundGradient->AddStop(tmp, 0);

  tmp.SetOpacity(alpha * .4f);
  tmp.Crop();
  mpGroundGradient->AddStop(tmp, .8f);

  tmp.SetOpacity(alpha *.6f);
  tmp.Crop();
  mpGroundGradient->AddStop(tmp, 1);
}

<|endoftext|>