Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 31 additions & 47 deletions Implementation/LibMCEnv/libmcenv_uienvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ CUIEnvironment::CUIEnvironment(AMC::CUIHandler* pUIHandler, const std::string& s

m_pUISystemState = pUIHandler->getUISystemState();
m_pLogger = m_pUISystemState->getLogger();
m_pEventFormValues = std::make_shared<AMC::CUIEventFormValues>(m_pUISystemState->getGlobalChronoInstance());

if (!sSenderUUID.empty()) {
m_sSenderUUID = (AMCCommon::CUtils::normalizeUUIDString(sSenderUUID));
Expand Down Expand Up @@ -373,74 +374,69 @@ LibMCEnv::eParameterDataType CUIEnvironment::GetMachineParameterGroupParameterTy
}


std::string CUIEnvironment::GetUIProperty(const std::string& sElementPath, const std::string& sPropertyName)
{
AMC::PParameterGroup CUIEnvironment::getUIPropertyGroupForRead(const std::string& sElementPath, const std::string& sPropertyName)
{
auto pClientVariableHandler = m_pAPIAuth->getLegacyParameterHandler(true);
if (pClientVariableHandler == nullptr)
throw ELibMCEnvInterfaceException (LIBMCENV_ERROR_COULDNNOTACCESSCLIENTVARIABLES);
throw ELibMCEnvInterfaceException(LIBMCENV_ERROR_COULDNNOTACCESSCLIENTVARIABLES);

auto pGroup = pClientVariableHandler->findGroup(sElementPath, true);
return pGroup->getParameterValueByName(sPropertyName);
return m_pEventFormValues->groupForRead(pClientVariableHandler, sElementPath, sPropertyName);
}

std::string CUIEnvironment::GetUIPropertyAsUUID(const std::string& sElementPath, const std::string& sPropertyName)
AMC::PParameterGroup CUIEnvironment::getUIPropertyGroupForWrite(const std::string& sElementPath, const std::string& sPropertyName)
{
auto pClientVariableHandler = m_pAPIAuth->getLegacyParameterHandler(true);
if (pClientVariableHandler == nullptr)
throw ELibMCEnvInterfaceException(LIBMCENV_ERROR_COULDNNOTACCESSCLIENTVARIABLES);

auto pGroup = pClientVariableHandler->findGroup(sElementPath, true);
return m_pEventFormValues->groupForWrite(pClientVariableHandler, sElementPath, sPropertyName);
}

void CUIEnvironment::capturePostedFormValue(const std::string& sElementPath, AMC::PParameterGroup pClientVariableGroup, const std::string& sPostedValue)
{
m_pEventFormValues->capturePostedValue(sElementPath, pClientVariableGroup, sPostedValue);
}

std::string CUIEnvironment::GetUIProperty(const std::string& sElementPath, const std::string& sPropertyName)
{
auto pGroup = getUIPropertyGroupForRead(sElementPath, sPropertyName);
return pGroup->getParameterValueByName(sPropertyName);
}

std::string CUIEnvironment::GetUIPropertyAsUUID(const std::string& sElementPath, const std::string& sPropertyName)
{
auto pGroup = getUIPropertyGroupForRead(sElementPath, sPropertyName);
return pGroup->getParameterValueByName(sPropertyName);

}

LibMCEnv_double CUIEnvironment::GetUIPropertyAsDouble(const std::string& sElementPath, const std::string& sPropertyName)
{
auto pClientVariableHandler = m_pAPIAuth->getLegacyParameterHandler(true);
if (pClientVariableHandler == nullptr)
throw ELibMCEnvInterfaceException(LIBMCENV_ERROR_COULDNNOTACCESSCLIENTVARIABLES);

auto pGroup = pClientVariableHandler->findGroup(sElementPath, true);
auto pGroup = getUIPropertyGroupForRead(sElementPath, sPropertyName);
return pGroup->getDoubleParameterValueByName(sPropertyName);
}

LibMCEnv_int64 CUIEnvironment::GetUIPropertyAsInteger(const std::string& sElementPath, const std::string& sPropertyName)
{
auto pClientVariableHandler = m_pAPIAuth->getLegacyParameterHandler(true);
if (pClientVariableHandler == nullptr)
throw ELibMCEnvInterfaceException(LIBMCENV_ERROR_COULDNNOTACCESSCLIENTVARIABLES);

auto pGroup = pClientVariableHandler->findGroup(sElementPath, true);
auto pGroup = getUIPropertyGroupForRead(sElementPath, sPropertyName);
return pGroup->getIntParameterValueByName(sPropertyName);
}

bool CUIEnvironment::GetUIPropertyAsBool(const std::string& sElementPath, const std::string& sPropertyName)
{
auto pClientVariableHandler = m_pAPIAuth->getLegacyParameterHandler(true);
if (pClientVariableHandler == nullptr)
throw ELibMCEnvInterfaceException(LIBMCENV_ERROR_COULDNNOTACCESSCLIENTVARIABLES);

auto pGroup = pClientVariableHandler->findGroup(sElementPath, true);
auto pGroup = getUIPropertyGroupForRead(sElementPath, sPropertyName);
return pGroup->getBoolParameterValueByName(sPropertyName);
}

void CUIEnvironment::SetUIProperty(const std::string& sElementPath, const std::string& sPropertyName, const std::string& sValue)
{
auto pClientVariableHandler = m_pAPIAuth->getLegacyParameterHandler(true);
if (pClientVariableHandler == nullptr)
throw ELibMCEnvInterfaceException(LIBMCENV_ERROR_COULDNNOTACCESSCLIENTVARIABLES);

auto pGroup = pClientVariableHandler->findGroup(sElementPath, true);
auto pGroup = getUIPropertyGroupForWrite(sElementPath, sPropertyName);
pGroup->setParameterValueByName(sPropertyName, sValue);
}

void CUIEnvironment::SetUIPropertyAsUUID(const std::string& sElementPath, const std::string& sPropertyName, const std::string& sValue)
{
auto pClientVariableHandler = m_pAPIAuth->getLegacyParameterHandler(true);
if (pClientVariableHandler == nullptr)
throw ELibMCEnvInterfaceException(LIBMCENV_ERROR_COULDNNOTACCESSCLIENTVARIABLES);

auto pGroup = pClientVariableHandler->findGroup(sElementPath, true);
auto pGroup = getUIPropertyGroupForWrite(sElementPath, sPropertyName);
if (!sValue.empty())
pGroup->setParameterValueByName(sPropertyName, AMCCommon::CUtils::normalizeUUIDString (sValue));
else
Expand All @@ -449,32 +445,20 @@ void CUIEnvironment::SetUIPropertyAsUUID(const std::string& sElementPath, const

void CUIEnvironment::SetUIPropertyAsDouble(const std::string& sElementPath, const std::string& sPropertyName, const LibMCEnv_double dValue)
{
auto pClientVariableHandler = m_pAPIAuth->getLegacyParameterHandler(true);
if (pClientVariableHandler == nullptr)
throw ELibMCEnvInterfaceException(LIBMCENV_ERROR_COULDNNOTACCESSCLIENTVARIABLES);

auto pGroup = pClientVariableHandler->findGroup(sElementPath, true);
auto pGroup = getUIPropertyGroupForWrite(sElementPath, sPropertyName);
pGroup->setDoubleParameterValueByName(sPropertyName, dValue);

}

void CUIEnvironment::SetUIPropertyAsInteger(const std::string& sElementPath, const std::string& sPropertyName, const LibMCEnv_int64 nValue)
{
auto pClientVariableHandler = m_pAPIAuth->getLegacyParameterHandler(true);
if (pClientVariableHandler == nullptr)
throw ELibMCEnvInterfaceException(LIBMCENV_ERROR_COULDNNOTACCESSCLIENTVARIABLES);

auto pGroup = pClientVariableHandler->findGroup(sElementPath, true);
auto pGroup = getUIPropertyGroupForWrite(sElementPath, sPropertyName);
pGroup->setIntParameterValueByName(sPropertyName, nValue);
}

void CUIEnvironment::SetUIPropertyAsBool(const std::string& sElementPath, const std::string& sPropertyName, const bool bValue)
{
auto pClientVariableHandler = m_pAPIAuth->getLegacyParameterHandler(true);
if (pClientVariableHandler == nullptr)
throw ELibMCEnvInterfaceException(LIBMCENV_ERROR_COULDNNOTACCESSCLIENTVARIABLES);

auto pGroup = pClientVariableHandler->findGroup(sElementPath, true);
auto pGroup = getUIPropertyGroupForWrite(sElementPath, sPropertyName);
pGroup->setBoolParameterValueByName(sPropertyName, bValue);
}

Expand Down
10 changes: 10 additions & 0 deletions Implementation/LibMCEnv/libmcenv_uienvironment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Abstract: This is the class declaration of CUIEnvironment
#include "amc_userinformation.hpp"
#include "amc_ui_systemstate.hpp"
#include "amc_api_auth.hpp"
#include "amc_ui_eventformvalues.hpp"

#include <vector>

Expand Down Expand Up @@ -90,6 +91,12 @@ class CUIEnvironment : public virtual IUIEnvironment, public virtual CBase {
// Client session that resolves session and user references of formulas; null without a session.
AMC::PUIFrontendState getFormulaFrontendState();

// Form values posted with the event being handled.
AMC::PUIEventFormValues m_pEventFormValues;

AMC::PParameterGroup getUIPropertyGroupForRead(const std::string& sElementPath, const std::string& sPropertyName);
AMC::PParameterGroup getUIPropertyGroupForWrite(const std::string& sElementPath, const std::string& sPropertyName);

protected:

public:
Expand Down Expand Up @@ -213,6 +220,9 @@ class CUIEnvironment : public virtual IUIEnvironment, public virtual CBase {

void setExternalEventParameters(const std::string & sParametersJSON);

// Keeps sPostedValue as this event's value of sElementPath, whatever polls write to the client variable meanwhile.
void capturePostedFormValue(const std::string& sElementPath, AMC::PParameterGroup pClientVariableGroup, const std::string& sPostedValue);

std::string getExternalEventReturnValues();

ITestEnvironment* GetTestEnvironment() override;
Expand Down
103 changes: 103 additions & 0 deletions Implementation/UI/amc_ui_eventformvalues.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*++

Copyright (C) 2026 Autodesk Inc.

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Autodesk Inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AUTODESK INC. BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/


#include "amc_ui_eventformvalues.hpp"
#include "amc_parametergroup.hpp"
#include "amc_parameterhandler.hpp"

#include "libmc_exceptiontypes.hpp"

namespace AMC {

CUIEventFormValues::CUIEventFormValues(AMCCommon::PChrono pGlobalChrono)
: m_pGlobalChrono(pGlobalChrono)
{
LibMCAssertNotNull(m_pGlobalChrono.get());
}

CUIEventFormValues::~CUIEventFormValues()
{
}

PParameterGroup CUIEventFormValues::findPostedGroup(const std::string& sElementPath, const std::string& sPropertyName)
{
if (sPropertyName != AMC_UI_EVENTFORMVALUE_PROPERTY)
return nullptr;

auto iIter = m_PostedGroups.find(sElementPath);
if (iIter == m_PostedGroups.end())
return nullptr;

return iIter->second;
}

void CUIEventFormValues::capturePostedValue(const std::string& sElementPath, PParameterGroup pClientVariableGroup, const std::string& sPostedValue)
{
LibMCAssertNotNull(pClientVariableGroup.get());

// The copy takes the parameter types from the client variables; the value is set from the
// payload, not read back, so a poll that already overwrote the client variable is harmless.
auto pPostedGroup = std::make_shared<CParameterGroup>(sElementPath, "posted form values", m_pGlobalChrono);
pPostedGroup->addDuplicatesFromGroup(pClientVariableGroup.get());
pPostedGroup->setParameterValueByName(AMC_UI_EVENTFORMVALUE_PROPERTY, sPostedValue);

std::lock_guard<std::mutex> lockGuard(m_Mutex);
m_PostedGroups[sElementPath] = pPostedGroup;
}

PParameterGroup CUIEventFormValues::groupForRead(CParameterHandler* pClientVariableHandler, const std::string& sElementPath, const std::string& sPropertyName)
{
LibMCAssertNotNull(pClientVariableHandler);

{
std::lock_guard<std::mutex> lockGuard(m_Mutex);
auto pPostedGroup = findPostedGroup(sElementPath, sPropertyName);
if (pPostedGroup.get() != nullptr)
return pPostedGroup;
}

return pClientVariableHandler->findGroup(sElementPath, true);
}

PParameterGroup CUIEventFormValues::groupForWrite(CParameterHandler* pClientVariableHandler, const std::string& sElementPath, const std::string& sPropertyName)
{
LibMCAssertNotNull(pClientVariableHandler);

{
std::lock_guard<std::mutex> lockGuard(m_Mutex);
if (findPostedGroup(sElementPath, sPropertyName).get() != nullptr)
m_PostedGroups.erase(sElementPath);
}

return pClientVariableHandler->findGroup(sElementPath, true);
}

}
87 changes: 87 additions & 0 deletions Implementation/UI/amc_ui_eventformvalues.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*++

Copyright (C) 2026 Autodesk Inc.

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Autodesk Inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AUTODESK INC. BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/


#ifndef __AMC_UI_EVENTFORMVALUES
#define __AMC_UI_EVENTFORMVALUES

#include <map>
#include <memory>
#include <mutex>
#include <string>

#include "common_chrono.hpp"

#define AMC_UI_EVENTFORMVALUE_PROPERTY "value"

namespace AMC {

class CParameterGroup;
typedef std::shared_ptr<CParameterGroup> PParameterGroup;

class CParameterHandler;

class CUIEventFormValues;
typedef std::shared_ptr<CUIEventFormValues> PUIEventFormValues;

// The form values posted with one UI event, kept apart from the session's client variables.
//
// A posted value is written to the client variable "<entity path>.value", which every content
// poll of a sync:value edit overwrites with the bound parameter. A poll between the payload
// write and the handler's GetUIProperty would hand the handler the parameter instead of what
// the user posted. Reads of "value" for a posted entity are therefore served from
// a copy taken when the payload is written; everything else reads the client variables.
class CUIEventFormValues {
private:
AMCCommon::PChrono m_pGlobalChrono;
std::mutex m_Mutex;
std::map<std::string, PParameterGroup> m_PostedGroups;

PParameterGroup findPostedGroup(const std::string& sElementPath, const std::string& sPropertyName);

public:
CUIEventFormValues(AMCCommon::PChrono pGlobalChrono);
virtual ~CUIEventFormValues();

// Records sPostedValue as the event's value of sElementPath. pClientVariableGroup is that
// entity's client variable group; it supplies the value's type, not its value.
void capturePostedValue(const std::string& sElementPath, PParameterGroup pClientVariableGroup, const std::string& sPostedValue);

// The group to read sPropertyName of sElementPath from during the event.
PParameterGroup groupForRead(CParameterHandler* pClientVariableHandler, const std::string& sElementPath, const std::string& sPropertyName);

// The group to write sPropertyName of sElementPath to during the event. Writing the value
// of a posted entity replaces the posted value, so later reads see the written one.
PParameterGroup groupForWrite(CParameterHandler* pClientVariableHandler, const std::string& sElementPath, const std::string& sPropertyName);
};

}

#endif //__AMC_UI_EVENTFORMVALUES
6 changes: 6 additions & 0 deletions Implementation/UI/amc_ui_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,12 @@ CUIHandleEventResponse CUIHandler::handleEvent(const std::string& sEventName, co
auto pModuleItem = pPage->findModuleItemByUUID(sEntityUUID);
if (pModuleItem.get() != nullptr) {
pModuleItem->setEventPayloadValue(sEventName, sEntityUUID, sPayloadValue, pClientVariableHandler);

// A content poll may resync the client variable before the handler reads it.
if (std::dynamic_pointer_cast<CUIModule_ContentForm>(pModuleItem).get() != nullptr) {
std::string sEntityPath = pModuleItem->findElementPathByUUID(sEntityUUID);
pInternalUIEnvironment->capturePostedFormValue(sEntityPath, pClientVariableHandler->findGroup(sEntityPath, true), sPayloadValue);
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions Implementation/UnitTest/amc_unittests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "amc_unittests_scanlab_jumpfilter.hpp"
#include "amc_unittests_scanlab_powerramp.hpp"
#include "amc_unittests_parquetwriter.hpp"
#include "amc_unittests_eventformvalues.hpp"

// LibMCData unit tests
#include "amc_unittests_libmcdata_storage.hpp"
Expand Down Expand Up @@ -122,6 +123,7 @@ CUnitTests::CUnitTests (PUnitTestIO pIO)
registerTestGroup(std::make_shared <CUnitTestGroup_ScanLab_JumpFilter>());
registerTestGroup(std::make_shared <CUnitTestGroup_ScanLab_PowerRamp>());
registerTestGroup(std::make_shared <CUnitTestGroup_ParquetWriter>());
registerTestGroup(std::make_shared <CUnitTestGroup_EventFormValues>());

// LibMCData unit tests
registerTestGroup(std::make_shared <CUnitTestGroup_LibMCData_Storage>());
Expand Down
Loading
Loading