Value of StringParameter not saved in Pro-II
Posted: 15 September 2022, 12:16
In the CPPMixerSplitter example I implemented a StringParameter analogously to a RealParameter, like described here: http://www.cape-open-forum.org/viewtopi ... 3&start=20
I use the UO within Pro-II.
This works well for both cases: the string parameter is an output or input parameter.
The only point I am not happy about is the following: If I specify input values for RealParameters and StringParameters and save the corresponding Pro-II file (.prz), the values of the StringParameters have vanished when opening the .prz file again. This is not so for the RealParameters.
Does anybody know, what needs to be done to save the values of the StringParameters?
Here is my implementation of the StringParameter in StringParameter.h:
I add the StringParameters (and RealParameters) in the constructor of CCPPMixerSplitterUnitOperation to the parameter collection
I use the UO within Pro-II.
This works well for both cases: the string parameter is an output or input parameter.
The only point I am not happy about is the following: If I specify input values for RealParameters and StringParameters and save the corresponding Pro-II file (.prz), the values of the StringParameters have vanished when opening the .prz file again. This is not so for the RealParameters.
Does anybody know, what needs to be done to save the values of the StringParameters?
Here is my implementation of the StringParameter in StringParameter.h:
Code: Select all
#pragma once
#include "resource.h" // main symbols
#include "CPPMixerSplitterexample.h"
#include "CAPEOPENBaseObject.h"
#include <float.h>
#include <iostream>
#include <string>
#include <fstream>
class ATL_NO_VTABLE CStringParameter :
//public CComCoClass<CStringParameter, &CLSID_RealParameter>,
public IDispatchImpl<ICapeParameter, &__uuidof(ICapeParameter), &LIBID_CAPEOPEN110, /* wMajor = */ 1, /* wMinor = */ 1>,
public IDispatchImpl<ICapeParameterSpec, &__uuidof(ICapeParameterSpec), &LIBID_CAPEOPEN110, /* wMajor = */ 1, /* wMinor = */ 1>,
public IDispatchImpl<ICapeOptionParameterSpec, &__uuidof(ICapeOptionParameterSpec), &LIBID_CAPEOPEN110, /* wMajor = */ 1, /* wMinor = */ 1>,
public CAPEOPENBaseObject
{
public:
BSTR defVal; /*!< the default value of this parameter; used to initialize as well, so cannot be NaN*/
BSTR value; /*!< the current value of this parameter, must always be valid */
std::string quantity; // determines possible values, which are listed in get_OptionList()
CapeValidationStatus *valStatus; /*!< points to the unit operation's validation status */
CapeParamMode Mode;
//! Helper function for creating the parameter
/*!
Helper function for creating the parameter as an exposable COM object. After calling CreateParameter, the reference
count is one; do not delete the returned object. Use Release() instead
\param name name of the parameter
\param description description of the parameter
\param defVal the default value of the parameter
\param valStatus points to the unit operation's validation status
\sa CStringParameter()
*/
static CComObject<CStringParameter> *CreateParameter(const OLECHAR *name,const OLECHAR *description, BSTR *defVal, std::string quantity, CapeValidationStatus *valStatus, CapeParamMode ModeIn)
{
CComObject<CStringParameter> *p;
CComObject<CStringParameter>::CreateInstance(&p); //create the instance with zero references
p->AddRef(); //now it has one reference, the caller must Release this object
p->name=name;
p->description=description;
p->defVal = SysAllocString(*defVal);
p->value = SysAllocString(*defVal);
p->quantity = quantity;
p->valStatus=valStatus;
p->Mode = ModeIn;
return p;
}
//! Constructor.
/*!
Creates an parameter of which the name cannot be changed by external applications.
Use CreateParameter instead of new
\sa CreateParameter()
*/
CStringParameter() : CAPEOPENBaseObject(false)
{//everything is initialized via CreateParameter
}
//this object cannot be created using CoCreateInstance, so we do not need to put anything in the registry
DECLARE_NO_REGISTRY()
//COM map, including that of the CAPEOPENBaseObject
BEGIN_COM_MAP(CStringParameter)
COM_INTERFACE_ENTRY2(IDispatch, ICapeParameter)
COM_INTERFACE_ENTRY(ICapeParameter)
COM_INTERFACE_ENTRY(ICapeParameterSpec)
COM_INTERFACE_ENTRY(ICapeOptionParameterSpec)
BASEMAP
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
STDMETHOD(get_Specification)(LPDISPATCH * spec)
{ if (!spec) return E_POINTER; //not a valid pointer
//this object implements the parameter spec. Instead of below, we can also call QueryInterface on ourselves
*spec=(ICapeParameter*)this;
(*spec)->AddRef(); //caller must release
return NOERROR;
}
STDMETHOD(get_value)(VARIANT* value)
{
if (!value) return FALSE; //not a valid pointer
VARIANT res;
res.vt = VT_BSTR;
res.bstrVal = (this->value) ? SysAllocString(this->value) : nullptr;
*value = res;
return NOERROR;
}
STDMETHOD(put_value)(VARIANT value)
{ //convert to a string
VARIANT v;
v.vt = VT_EMPTY;
if (FAILED(VariantChangeType(&v, &value, 0, VT_BSTR)))
{
SetError(L"Invalid data type. Expected basic string: BSTR", L"ICapeParameter", L"put_value");
return ECapeUnknownHR;
}
ATLASSERT(v.vt == VT_BSTR);
//value is ok
this->value = SysAllocString(v.bstrVal);
*valStatus = CAPE_NOT_VALIDATED; //we changed the parameter, the unit needs to be re-validated
dirty = true; //something changed that affects saving
return NOERROR;
}
STDMETHOD(get_ValStatus)(CapeValidationStatus* ValStatus)
{
if (!ValStatus) return E_POINTER; //not a valid value
*ValStatus = CAPE_VALID; //value is always valid
return NOERROR;
}
STDMETHOD(get_Mode)(CapeParamMode* Mode)
{
if (!Mode) return E_POINTER; //not a valid value
*Mode = this->Mode; //this unit operation only has input parameters
return NOERROR;
}
STDMETHOD(put_Mode)(CapeParamMode Mode)
{
SetError(L"The mode of this parameter is read-only", L"ICapeParameter", L"put_Mode");
return ECapeUnknownHR;
}
STDMETHOD(Validate)(BSTR* message, VARIANT_BOOL* isOK)
{
if ((!message) || (!isOK)) return E_POINTER; //not valid values
//note the [in, out] status of the message; if we were to put a new value in there, we should clear its existing value or its memory would leak
*isOK = VARIANT_TRUE; //we are always valid
return NOERROR;
}
STDMETHOD(Reset)()
{
value = SysAllocString(defVal);
*valStatus = CAPE_NOT_VALIDATED; //we changed the parameter, the unit needs to be re-validated
dirty = true; //something changed that affects saving
return NOERROR;
}
STDMETHOD(get_Type)(CapeParamType* Type)
{
if (!Type) return E_POINTER; //not a valid pointer
*Type = CAPE_OPTION;
return NOERROR;
}
STDMETHOD(get_Dimensionality)(VARIANT* dim)
{
if (!dim) return E_POINTER; //not a valid pointer
dim->vt = VT_EMPTY; // return empty VARIANT
return NOERROR;
}
STDMETHOD(get_DefaultValue)(BSTR* DefaultValue)
{
if (!DefaultValue) return E_POINTER; //not a valid pointer
*DefaultValue = SysAllocString(defVal);
return NOERROR;
}
STDMETHOD(get_OptionList)(VARIANT* v)
{
// This function specifies all allowed version numbers.
// Pro-II displays only version numbers, which are allowed by this function.
// set type to string array
v->vt = VT_ARRAY | VT_BSTR;
//ofstream myfile;
//myfile.open("C:\\Users\\liebschs\\src\\repos\\pro-ii-api\\SOC-1D-Solver\\dll_debug.log", std::ios_base::app);
//myfile << "quantity = " << quantity << "\n";
//myfile.close();
// specify allowed BSTRs
vector<BSTR> allowed_BSTRs = {};
if (quantity == "version") {
allowed_BSTRs.push_back(SysAllocString(L"x.y.z"));
for (unsigned int major = 0; major <= 2; major++)
{
for (unsigned int minor = 0; minor <= 11; minor++)
{
for (unsigned int bugfix = 0; bugfix <= 11; bugfix++)
{
// build version STL string
string version_STLstring = to_string(major) + "." + to_string(minor) + "." + to_string(bugfix);
// convert STL string to BSTR and append to vector
CComBSTR temp(version_STLstring.c_str());
allowed_BSTRs.push_back(temp.Detach());
}
}
}
}
else if (quantity == "stack") {
allowed_BSTRs.push_back(SysAllocString(L"default"));
allowed_BSTRs.push_back(SysAllocString(L"Stand 2018"));
allowed_BSTRs.push_back(SysAllocString(L"Stand 2020"));
allowed_BSTRs.push_back(SysAllocString(L"base 2023"));
allowed_BSTRs.push_back(SysAllocString(L"ambitious 2023"));
allowed_BSTRs.push_back(SysAllocString(L"ambitious 2025"));
allowed_BSTRs.push_back(SysAllocString(L"user defined"));
}
else if (quantity == "module") {
allowed_BSTRs.push_back(SysAllocString(L"StackUnit A101"));
allowed_BSTRs.push_back(SysAllocString(L"SU Gen.2 (alpha)"));
allowed_BSTRs.push_back(SysAllocString(L"reiner Stack"));
allowed_BSTRs.push_back(SysAllocString(L"Einzel-STB-B411"));
allowed_BSTRs.push_back(SysAllocString(L"Einzel-STB in EBZ3 (4.4)"));
allowed_BSTRs.push_back(SysAllocString(L"Einzel-STB in FC4 (4.1)"));
allowed_BSTRs.push_back(SysAllocString(L"ICM (240E) (GrInHy)"));
allowed_BSTRs.push_back(SysAllocString(L"SU Gen.3"));
allowed_BSTRs.push_back(SysAllocString(L"user defined"));
}
else if (quantity == "input_quantities") {
allowed_BSTRs.push_back(SysAllocString(L"default"));
allowed_BSTRs.push_back(SysAllocString(L"I, V_F_feed"));
allowed_BSTRs.push_back(SysAllocString(L"FC, V_F_feed (SOEC)"));
}
else if (quantity == "gas_evolution_model") {
allowed_BSTRs.push_back(SysAllocString(L"default"));
allowed_BSTRs.push_back(SysAllocString(L"WGS_eq, SR_exp"));
allowed_BSTRs.push_back(SysAllocString(L"kinetic ansatz"));
}
else if (quantity == "flow_configuration") {
allowed_BSTRs.push_back(SysAllocString(L"default"));
allowed_BSTRs.push_back(SysAllocString(L"parallel current"));
allowed_BSTRs.push_back(SysAllocString(L"counter current"));
}
// write allowed versions to output parameter
v->parray = SafeArrayCreateVector(VT_BSTR, 0, (unsigned int) allowed_BSTRs.size()); // start indexing at 0
long i = 0;
for (vector<BSTR>::iterator it = allowed_BSTRs.begin(); it != allowed_BSTRs.end(); it++, i++ )
{
SafeArrayPutElement(v->parray, &i, *it);
}
return NOERROR;
}
STDMETHOD(get_RestrictedToList)(VARIANT_BOOL * vb)
{
*vb = VARIANT_TRUE;
return NOERROR;
}
STDMETHOD(Validate)(BSTR value, BSTR* message, VARIANT_BOOL* isOK)
{
if ((!message) || (!isOK)) return E_POINTER; //invalid pointer
//notice that message is [in,out]; if we set a value, we must clear the existing value or its memory will leak. Let's do that now
if (*message)
{//it is not wise of the caller to pass a value in here... one cannot trust that all implementors do this properly
SysFreeString(*message);
*message = NULL;
}
*isOK = VARIANT_TRUE;
return NOERROR;
}
};
//convenience definition for variables of COM objects defined by this class
typedef CComObject<CStringParameter> StringParameterObject;
Code: Select all
parameterCollection = CCollection::CreateCollection(L"Parameter collection", L"Parameter collection for SOC-1D-Solver");
RealParameterObject* par;
StringParameterObject* string_par;
BSTR defBSTR = SysAllocString(L"default"); // default value for string input parameters
string_par = StringParameterObject::CreateParameter(L"module type", L"[-]", &defBSTR, "module", & valStatus, CAPE_INPUT);
parameterCollection->AddItem(string_par); // parameter 0
...
par = RealParameterObject::CreateParameter(L"I in A (input)", L"[-]", NaN, NaN, NaN, dimensionality, &valStatus, CAPE_INPUT);
parameterCollection->AddItem(par); // parameter 8