Technical Blog | All posts with tag: any tag , RPA, RPA-C, Release Notes, cycle analysis, news, scripting, thermodynamics

Embedding RPA Scripting Utility into third-party applications

29-Jul-2017, 21:00 GMT | Tags: RPA, scripting

Scripting utility can be embedded into third-party software using provided simple API.

After embedding the scripting utility, the third-party application may

  • Load and execute external RPA script
  • Execute RPA script from given string
  • Access objects within the scripting context, modify properties and obtain results


C++ API is defined in header file <RPA>/include/rpas.hpp as follows:

/*
 * RPA - Tool for Rocket Propulsion Analysis
 * RPA Scripting Utility
 *
 * Copyright 2009,2015 Alexander Ponomarenko.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This software is a commercial product; you can use it under the terms of the
 * RPA Standard Edition License as published at http://propulsion-analysis.com/lic_standard.htm
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the RPA SDK License for more details (a copy is included
 * in the sdk_eula.htm file that accompanied this program).
 *
 * You should have received a copy of the RPA SDK License along with this program;
 * if not, write to author <contact@propulsion-analysis.com>
 *
 * Please contact author <contact@propulsion-analysis.com> or visit http://www.propulsion-analysis.com
 * if you need additional information or have any questions.
 */

#ifndef SCRIPTING_SRC_RPAS_HPP_
#define SCRIPTING_SRC_RPAS_HPP_

#include <QtCore/QtGlobal>

#if defined(SHARED)
#  define SDLL Q_DECL_EXPORT
#else
#  define SDLL Q_DECL_IMPORT
#endif

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

/**
 * Initializes the RPA library, creating loggers and loading database files using following paths:
 * resources/thermo.inp 
 * resources/usr_thermo.inp 
 * resources/properties.inp 
 * resources/usr_properties.inp 
 * resources/trans.inp 
 *
 * @param consoleOutput if true, duplicate the logging in console
 */
SDLL void rpaInitialize(bool consoleOutput);

/**
 * Initializes the RPA library, creating loggers and loading database files using specified paths:
 * $(logpath)/rpa_info.log 
 * $(respath)/thermo.inp 
 * $(respath)/usr_thermo.inp 
 * $(respath)/properties.inp 
 * $(respath)/usr_properties.inp 
 * $(respath)/trans.inp 
 *
 * @param respath path to resource directory with database files (default is <RPA-dist-path>/resources)
 * @param usrrespath path to resource directory with user-defined database files (default is <User-data-path>/RPA/resources)
 * @param logpath path to log directory
 * @param consoleOutput if true, duplicate the logging in console
 */
SDLL void rpaInitializeWithPath(const std::string& respath, const std::string& usrrespath, const std::string& logpath, bool consoleOutput);

/**
 * Finalizes the RPA library, closing all open file loggers.
 */
SDLL void rpaFinalize();

/**
 * Initializes the RPA Scripting engine
 *
 * @param script contents of the script which has to be executed; if empty, scripting will be started as an interactive interpreter
 * @param scriptPath path to script file which has to be executed
 * @param scriptLineNumbers
 * @return Pointer to initialized instance of QScriptEngine
 */
SDLL QScriptEngine* rpaScriptingInitialize();

/**
 * Starts the scripting console in interactive mode
 *
 * @param eng pointer to initialized instance of QScriptEngine
 */
SDLL void rpaScriptingInteractive(QScriptEngine* eng);

/**
 * Loads and executes the script from specified file
 *
 * @param eng pointer to initialized instance of QScriptEngine
 * @param scriptPath path to script file
 * @return Result of execution of the script file
 */
SDLL QScriptValue rpaScriptingEvaluateFile(QScriptEngine* eng, QFileInfo scriptPath);

/**
 * Executes the script from specified string
 *
 * @param eng pointer to initialized instance of QScriptEngine
 * @param script script
 * @return Result of execution of the script
 */
SDLL QScriptValue rpaScriptingEvaluate(QScriptEngine* eng, QString script);

/**
 * Initializes the RPA Scripting engine
 *
 * @param eng pointer to initialized instance of QScriptEngine
 */
SDLL void rpaScriptingFinalize(QScriptEngine* eng);

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


#endif /* SCRIPTING_SRC_RPAS_HPP_ */

Prerequisites

To be able to use this API, the following software should be available:

  • C++ compiler (tested with GCC v.4.8 and VS C++ 2010)
  • Development libraries and headers for Qt 4.x
  • Development libraries and headers for third-party software where you want to use RPA Scripting

Lifecycle of embedded scripting module

Before you can use it, the embedded scripting module has to be initialized. At this step, you create an instance of the module and (optionally) specify where it should look for thermodynamic database files.

Initialization

There are two initialization functions you may use:

/**
 * Initializes the RPA library, creating loggers and loading database files using following paths:<br>
 * resources/thermo.inp <br>
 * resources/usr_thermo.inp <br>
 * resources/properties.inp <br>
 * resources/usr_properties.inp <br>
 * resources/trans.inp <br>
 *
 * @param consoleOutput if true, duplicate the logging in console
 */
SDLL void rpaInitialize(bool consoleOutput);

and

/**
 * Initializes the RPA library, creating loggers and loading database files using specified paths:<br>
 * $(logpath)/rpa_info.log <br>
 * $(respath)/thermo.inp <br>
 * $(respath)/usr_thermo.inp <br>
 * $(respath)/properties.inp <br>
 * $(respath)/usr_properties.inp <br>
 * $(respath)/trans.inp <br>
 *
 * @param respath path to resource directory with database files (default is <RPA-dist-path>/resources)
 * @param usrrespath path to resource directory with user-defined database files (default is <User-data-path>/RPA/resources)
 * @param logpath path to log directory
 * @param consoleOutput if true, duplicate the logging in console
 */
SDLL void rpaInitializeWithPath(const std::string& respath, const std::string& usrrespath, const std::string& logpath, bool consoleOutput);

The first function tries to load the standard thermodynamic database files from default location <current directory>/resources/ and user-defined (custom) databases from <User-data-path>/RPA/resources/, while the second one can be used to specify the path to both locations explicitly.

Using the instance of the module

To execute any script, you need to obtain the pointer to the created instance of the module, using the following function:

/**
 * Initializes the RPA Scripting engine
 *
 * @param script contents of the script which has to be executed; if empty, scripting will be started as an interactive interpreter
 * @param scriptPath path to script file which has to be executed
 * @param scriptLineNumbers
 * @return Pointer to initialized instance of QScriptEngine
 */
SDLL QScriptEngine* rpaScriptingInitialize();

After obtaining the pointer to the instance of the module, just pass it as a first parameter into one of the following two functions:

/**
 * Loads and executes the script from specified file
 *
 * @param eng pointer to initialized instance of QScriptEngine
 * @param scriptPath path to script file
 * @return Result of execution of the script file
 */
SDLL QScriptValue rpaScriptingEvaluateFile(QScriptEngine* eng, QFileInfo scriptPath);

/**
 * Executes the script from specified string
 *
 * @param eng pointer to initialized instance of QScriptEngine
 * @param script script
 * @return Result of execution of the script
 */
SDLL QScriptValue rpaScriptingEvaluate(QScriptEngine* eng, QString script);

The first function is used to load and execute an external script specified by the path within the file system on your computer. The second function is used to execute the script or expression specified as a string.

You may execute any of these two functions as many times as you need. All global JavaScript objects created within your scripts will be stored in the instance of the scripting module, so that you may access/manipulate the objects created by the scripts from within other scripts executed with the same instance of the scripting module.

Finalization

To finish the work with the scripting module, execute the following two functions:

/**
 * Executes the script from specified string
 *
 * @param eng pointer to initialized instance of QScriptEngine
 * @param script script
 * @return Result of execution of the script
 */
SDLL QScriptValue rpaScriptingEvaluate(QScriptEngine* eng, QString script);

/**
 * Initializes the RPA Scripting engine
 *
 * @param eng pointer to initialized instance of QScriptEngine
 */
SDLL void rpaScriptingFinalize(QScriptEngine* eng);

Plugin for GNU Octave on Linux

As an example, we will demonstrate how to use RPA Scription Module from within GNU Octave.

"GNU Octave is a high-level language, primarily intended for numerical computations. It provides a convenient command line interface for solving linear and nonlinear problems numerically, and for performing other numerical experiments using a language that is mostly compatible with Matlab. It may also be used as a batch-oriented language.

Octave has extensive tools for solving common numerical linear algebra problems, finding the roots of nonlinear equations, integrating ordinary functions, manipulating polynomials, and integrating ordinary differential and differential-algebraic equations. It is easily extensible and customizable via user-defined functions written in Octave’s own language, or using dynamically loaded modules written in C++, C, Fortran, or other languages."

(The description above is taken from the oficial web site of GNU Octave project, where you may find the complete description, documentation as well as many useful examples)

Compiling the plugin for Octave

To make the RPA Scription Module available inside Octave scripts (programs), you may use the following custom plugin for Octave:

#include <octave/oct.h>
#include <octave/parse.h>

#include <QApplication>
#include <QScriptEngine>
#include <QScriptValue>
#include <QDir>

#include <string>

#include "rpas.hpp"

static QApplication* app = 0;
static QScriptEngine* eng = 0;

DEFUN_DLD (rpaInit, args, nargout, "Initialize RPA") {
	int nargin = args.length();

	std::string respath = "";
	std::string logpath = "";
	bool consoleOutput = true;

	int nextIdx = 0;
	if (nargin>0 && args(0).is_string()) {
		respath = args(0).string_value();
		nextIdx++;

		if (nargin>1 && args(1).is_string()) {
			logpath = args(1).string_value();
			nextIdx++;
		}
	}
	if (nargin>nextIdx && args(nextIdx).is_bool_scalar()) {
		consoleOutput = args(nextIdx).bool_value();
	}

	int argc = 0;
	char **argv = 0;

	app = new QApplication(argc, argv);

	rpaInitializeWithPath(respath, "", logpath, consoleOutput);

	eng = rpaScriptingInitialize();

	return octave_value();
}

DEFUN_DLD (rpaFin, args, nargout, "Finalize RPA") {
	if (eng) {
		rpaScriptingFinalize(eng);
		eng = 0;
	}
	rpaFinalize();
	delete app;
	app = 0;
	return octave_value();
}

DEFUN_DLD (rpaEval, args, nargout, "Execute RPA script") {
	octave_value retval;
	if (eng && args.length()>0 && args(0).is_string()) {
		std::string script = args(0).string_value();
		QScriptValue v = rpaScriptingEvaluate(eng, script.c_str());
		if (v.isNumber()) {
			retval = v.toNumber();
		}
	}
	return retval;
}

DEFUN_DLD (rpaEvalFile, args, nargout, "Execute RPA script file") {
	octave_value retval;
	if (eng && args.length()>0 && args(0).is_string()) {
		std::string scriptPath = args(0).string_value();
		QScriptValue v = rpaScriptingEvaluateFile(eng, QFileInfo(scriptPath.c_str()));
		if (v.isNumber()) {
			retval = v.toNumber();
		}
	}
	return retval;
}
Note that this plugin is storing the pointer to scripting engine as a global static variable. This means you may not use this plugin to handle several independent scripting contexts.

To build plugins, GNU Octave project provides the utility mkoctfile. Below is an example of Bash script to compile the Octave plugin stored in the source file ./rpa_octave.cpp:

#!/bin/sh
OCTAVE_INC1=/usr/include/octave-3.8.2/
OCTAVE_INC2=/usr/include/octave-3.8.2/octave/
OCTAVE_LIB=/usr/lib64/octave/3.8.2/
QT_INC1=/usr/include/QtCore/
QT_INC2=/usr/include/QtGui/
QT_INC3=/usr/include/QtScript/

mkoctfile -I$OCTAVE_INC1 -I$OCTAVE_INC2 -I$QT_INC1 -I$QT_INC2 -I$QT_INC3 -L$OCTAVE_LIB \
    -I./ -I../../include -L../../ -lrpas -s ./rpa_octave.cpp

rm -f *.o

Note that you might need to update the Octave-specific variables OCTAVE_INC1, OCTAVE_INC2, OCTAVE_LIB, as well as Qt-specific variables QT_INC1, QT_INC2, and QT_INC3.

Execute the Bash script above. On success, the script should run without any output, and generate the Octave plugin as a shared library rpa_octave.oct.

Octave scripts

To run the RPA Scripting Module inside Octave script, start Octave using the following Bash command:

#!/bin/sh

LD_LIBRARY_PATH=./:examples/scripting_wrapper/:$LD_LIBRARY_PATH octave examples/scripting_wrapper/test-rpa.m

Note that you might need to update the library path LD_LIBRARY_PATH to ensure that both RPA shared libraries and generated Octave plugin can be found.

The command above is using the Octave script stored in the file examples/scripting_wrapper/test-rpa.m:

#
# RPA - Tool for Rocket Propulsion Analysis
# RPA Scripting Utility
#
# Copyright 2009,2015 Alexander Ponomarenko.
#
#
# This is an example Octave script which loads and uses RPA Scripting via plugin rpa_octave.oct (compiled from rpa_octave.cpp).
#

# Load external functions
autoload("rpaInit", "rpa_octave.oct")
autoload("rpaFin", "rpa_octave.oct")    
autoload("rpaEval", "rpa_octave.oct")  
autoload("rpaEvalFile", "rpa_octave.oct")  

# Initialize RPA
rpaInit("./");

# Load and evaluate the RPA scripting file
rpaEvalFile("examples/scripts/RD-275.js")

# Obtain the inlet pressure in two different units: MPa and atm
p_in = rpaEval("oxFeedSystem.getFlowPath().getInletPort().getP('MPa')")
p_in = rpaEval("oxFeedSystem.getFlowPath().getInletPort().getP('atm')")

# Finalize RPA
rpaFin();

RP Software+Engineering UG

53819 Neunkirchen-Seelscheid, Germany

About us  |  Impressum

Customers

Contact information

Custom development

Supplementary services


Copyright © 2009 - 2018 Alexander Ponomarenko