Add Inspection and Import WebAssembly probes
Some checks failed
FreeCAD master CI / Prepare (push) Has been cancelled
FreeCAD master CI / Pixi (push) Has been cancelled
FreeCAD master CI / Ubuntu (push) Has been cancelled
FreeCAD master CI / Windows (push) Has been cancelled
FreeCAD master CI / Lint (push) Has been cancelled
FreeCAD master CI / WrapUp (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Stale Issues / stale (push) Has been cancelled
Some checks failed
FreeCAD master CI / Prepare (push) Has been cancelled
FreeCAD master CI / Pixi (push) Has been cancelled
FreeCAD master CI / Ubuntu (push) Has been cancelled
FreeCAD master CI / Windows (push) Has been cancelled
FreeCAD master CI / Lint (push) Has been cancelled
FreeCAD master CI / WrapUp (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Stale Issues / stale (push) Has been cancelled
This commit is contained in:
@@ -57,6 +57,7 @@ option(FREECAD_WEB "Build the FreeCAD WebAssembly core configuration" OFF)
|
||||
option(FREECAD_WEB_ENABLE_ASSEMBLY "Enable the Assembly Web probe and its dependency closure" OFF)
|
||||
option(FREECAD_WEB_ENABLE_CAM "Enable the CAM Path App Web probe and its dependency closure" OFF)
|
||||
option(FREECAD_WEB_ENABLE_CLOUD "Enable the Cloud App Web probe and its dependency closure" OFF)
|
||||
option(FREECAD_WEB_ENABLE_INSPECTION "Enable the Inspection App Web probe and its dependency closure" OFF)
|
||||
if(FREECAD_WEB)
|
||||
if(NOT EMSCRIPTEN OR NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
|
||||
message(FATAL_ERROR
|
||||
@@ -72,6 +73,7 @@ if(FREECAD_WEB)
|
||||
set(FREECAD_USE_PCH OFF CACHE BOOL "Web core does not use desktop PCH defaults" FORCE)
|
||||
set(FREECAD_USE_FREETYPE OFF CACHE BOOL "Web core leaves optional FreeType disabled" FORCE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -include${CMAKE_SOURCE_DIR}/src/FCWebRangesCompat.h")
|
||||
add_compile_definitions(FREECAD_WEB_CORE)
|
||||
|
||||
set(_freecad_web_disabled_options
|
||||
BUILD_ADDONMGR
|
||||
@@ -118,6 +120,9 @@ if(FREECAD_WEB)
|
||||
if(FREECAD_WEB_ENABLE_CLOUD)
|
||||
list(REMOVE_ITEM _freecad_web_disabled_options BUILD_CLOUD)
|
||||
endif()
|
||||
if(FREECAD_WEB_ENABLE_INSPECTION)
|
||||
list(REMOVE_ITEM _freecad_web_disabled_options BUILD_INSPECTION BUILD_MESH BUILD_POINTS)
|
||||
endif()
|
||||
foreach(_freecad_web_disabled_option IN LISTS _freecad_web_disabled_options)
|
||||
set(${_freecad_web_disabled_option} OFF CACHE BOOL "Disabled by FREECAD_WEB core configuration" FORCE)
|
||||
endforeach()
|
||||
@@ -139,6 +144,11 @@ if(FREECAD_WEB)
|
||||
if(FREECAD_WEB_ENABLE_CLOUD)
|
||||
set(BUILD_CLOUD ON CACHE BOOL "Enabled by FREECAD_WEB_ENABLE_CLOUD" FORCE)
|
||||
endif()
|
||||
if(FREECAD_WEB_ENABLE_INSPECTION)
|
||||
foreach(_freecad_web_inspection_option BUILD_INSPECTION BUILD_MESH BUILD_POINTS)
|
||||
set(${_freecad_web_inspection_option} ON CACHE BOOL "Enabled by FREECAD_WEB_ENABLE_INSPECTION" FORCE)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
include(cMake/FreeCADWebDependencies.cmake)
|
||||
endif()
|
||||
|
||||
@@ -1598,9 +1598,9 @@ std::vector<DocumentObject*> Document::readObjects(Base::XMLReader& reader)
|
||||
|
||||
long lastId = 0;
|
||||
for (int i = 0; i < Cnt; i++) {
|
||||
reader.readElement("Object");
|
||||
std::string type = reader.getAttribute<const char*>("type");
|
||||
std::string name = reader.getAttribute<const char*>("name");
|
||||
reader.readElement("Object");
|
||||
std::string type = reader.getAttribute<const char*>("type");
|
||||
std::string name = reader.getAttribute<const char*>("name");
|
||||
std::string viewType =
|
||||
reader.hasAttribute("ViewType") ? reader.getAttribute<const char*>("ViewType") : "";
|
||||
|
||||
@@ -2082,38 +2082,38 @@ bool Document::isAnyRestoring()
|
||||
|
||||
// Open the document
|
||||
void Document::restore(const char* filename,
|
||||
bool delaySignal,
|
||||
const std::vector<std::string>& objNames)
|
||||
bool delaySignal,
|
||||
const std::vector<std::string>& objNames)
|
||||
{
|
||||
clearUndos();
|
||||
d->activeObject = nullptr;
|
||||
|
||||
bool signal = false;
|
||||
Document* activeDoc = GetApplication().getActiveDocument();
|
||||
if (!d->objectArray.empty()) {
|
||||
signal = true;
|
||||
GetApplication().signalDeleteDocument(*this);
|
||||
d->clearDocument();
|
||||
}
|
||||
|
||||
Base::FlagToggler<> flag(globalIsRestoring, false);
|
||||
|
||||
setStatus(Document::PartialDoc, false);
|
||||
|
||||
d->clearRecomputeLog();
|
||||
d->objectLabelManager.clear();
|
||||
d->objectArray.clear();
|
||||
d->objectNameManager.clear();
|
||||
d->objectMap.clear();
|
||||
d->objectIdMap.clear();
|
||||
d->lastObjectId = 0;
|
||||
|
||||
if (signal) {
|
||||
GetApplication().signalNewDocument(*this, true);
|
||||
if (activeDoc == this) {
|
||||
GetApplication().setActiveDocument(this);
|
||||
}
|
||||
}
|
||||
clearUndos();
|
||||
d->activeObject = nullptr;
|
||||
|
||||
bool signal = false;
|
||||
Document* activeDoc = GetApplication().getActiveDocument();
|
||||
if (!d->objectArray.empty()) {
|
||||
signal = true;
|
||||
GetApplication().signalDeleteDocument(*this);
|
||||
d->clearDocument();
|
||||
}
|
||||
|
||||
Base::FlagToggler<> flag(globalIsRestoring, false);
|
||||
|
||||
setStatus(Document::PartialDoc, false);
|
||||
|
||||
d->clearRecomputeLog();
|
||||
d->objectLabelManager.clear();
|
||||
d->objectArray.clear();
|
||||
d->objectNameManager.clear();
|
||||
d->objectMap.clear();
|
||||
d->objectIdMap.clear();
|
||||
d->lastObjectId = 0;
|
||||
|
||||
if (signal) {
|
||||
GetApplication().signalNewDocument(*this, true);
|
||||
if (activeDoc == this) {
|
||||
GetApplication().setActiveDocument(this);
|
||||
}
|
||||
}
|
||||
|
||||
if (!filename) {
|
||||
filename = FileName.getValue();
|
||||
@@ -2178,7 +2178,7 @@ void Document::restore(const char* filename,
|
||||
throw Base::FileException("Error reading compression file", filename);
|
||||
}
|
||||
|
||||
GetApplication().signalStartRestoreDocument(*this);
|
||||
GetApplication().signalStartRestoreDocument(*this);
|
||||
setStatus(Document::Restoring, true);
|
||||
|
||||
d->partialLoadObjects.clear();
|
||||
|
||||
@@ -34,10 +34,12 @@
|
||||
#include <TopoDS.hxx>
|
||||
#include <gp_Pnt.hxx>
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <QtConcurrentMap>
|
||||
#include <QEventLoop>
|
||||
#ifndef FREECAD_WEB_CORE
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <QtConcurrentMap>
|
||||
#endif
|
||||
|
||||
#include <Base/Console.h>
|
||||
#include <Base/FutureWatcherProgress.h>
|
||||
@@ -930,7 +932,8 @@ App::DocumentObjectExecReturn* Feature::execute()
|
||||
|
||||
DistanceInspectionRMS res;
|
||||
|
||||
if (useMultithreading) {
|
||||
#ifndef FREECAD_WEB_CORE
|
||||
if (useMultithreading) {
|
||||
// Build vector of increasing indices
|
||||
std::vector<unsigned long> index(count);
|
||||
std::iota(index.begin(), index.end(), 0);
|
||||
@@ -958,8 +961,11 @@ App::DocumentObjectExecReturn* Feature::execute()
|
||||
watcher.setFuture(future);
|
||||
loop.exec();
|
||||
res = future.result();
|
||||
}
|
||||
else {
|
||||
}
|
||||
else {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
// Single-threaded operation
|
||||
std::stringstream str;
|
||||
str << "Inspecting " << this->Label.getValue() << "…";
|
||||
|
||||
@@ -42,7 +42,9 @@
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
// Qt
|
||||
#include <QEventLoop>
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <QtConcurrentMap>
|
||||
#include <QEventLoop>
|
||||
#ifndef FREECAD_WEB_CORE
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <QtConcurrentMap>
|
||||
#endif
|
||||
|
||||
@@ -26,9 +26,11 @@
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
|
||||
#ifndef FREECAD_WEB_CORE
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <QtConcurrentMap>
|
||||
#endif
|
||||
|
||||
#include <Base/Sequencer.h>
|
||||
#include <Base/Tools.h>
|
||||
@@ -72,7 +74,11 @@ void MeshCurvature::ComputePerFace(bool parallel)
|
||||
MeshRefPointToFacets search(myKernel);
|
||||
FacetCurvature face(myKernel, search, myRadius, myMinPoints);
|
||||
|
||||
#ifdef FREECAD_WEB_CORE
|
||||
if (true) {
|
||||
#else
|
||||
if (!parallel) {
|
||||
#endif
|
||||
Base::SequencerLauncher seq("Curvature estimation", mySegment.size());
|
||||
for (FacetIndex it : mySegment) {
|
||||
CurvatureInfo info = face.Compute(it);
|
||||
@@ -80,6 +86,7 @@ void MeshCurvature::ComputePerFace(bool parallel)
|
||||
seq.next();
|
||||
}
|
||||
}
|
||||
#ifndef FREECAD_WEB_CORE
|
||||
else {
|
||||
// NOLINTBEGIN
|
||||
QFuture<CurvatureInfo> future
|
||||
@@ -92,6 +99,7 @@ void MeshCurvature::ComputePerFace(bool parallel)
|
||||
myCurvature.push_back(it);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef OPTIMIZE_CURVATURE
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include <QtConcurrentMap>
|
||||
#ifndef FREECAD_WEB_CORE
|
||||
#include <QtConcurrentMap>
|
||||
#endif
|
||||
#include <boost/math/special_functions/fpclassify.hpp>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
@@ -36,9 +38,9 @@
|
||||
#include "PointsAlgos.h"
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# include <ppl.h>
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
# include <ppl.h>
|
||||
#endif
|
||||
|
||||
using namespace Points;
|
||||
using namespace std;
|
||||
@@ -84,7 +86,11 @@ Data::Segment* PointKernel::getSubElement(const char* /*Type*/, unsigned long /*
|
||||
void PointKernel::transformGeometry(const Base::Matrix4D& rclMat)
|
||||
{
|
||||
std::vector<value_type>& kernel = getBasicPoints();
|
||||
#ifdef _MSC_VER
|
||||
#if defined(FREECAD_WEB_CORE)
|
||||
std::for_each(kernel.begin(), kernel.end(), [rclMat](value_type& value) {
|
||||
rclMat.multVec(value, value);
|
||||
});
|
||||
#elif defined(_MSC_VER)
|
||||
// Win32-only at the moment since ppl.h is a Microsoft library. Points is not using Qt so we
|
||||
// cannot use QtConcurrent We could also rewrite Points to leverage SIMD instructions Other
|
||||
// option: openMP. But with VC2013 results in high CPU usage even after computation (busy-waits
|
||||
@@ -92,7 +98,7 @@ void PointKernel::transformGeometry(const Base::Matrix4D& rclMat)
|
||||
Concurrency::parallel_for_each(kernel.begin(), kernel.end(), [rclMat](value_type& value) {
|
||||
value = rclMat * value;
|
||||
});
|
||||
#else
|
||||
#else
|
||||
QtConcurrent::blockingMap(kernel, [rclMat](value_type& value) { rclMat.multVec(value, value); });
|
||||
#endif
|
||||
}
|
||||
@@ -101,7 +107,11 @@ void PointKernel::moveGeometry(const Base::Vector3d& vec)
|
||||
{
|
||||
Base::Vector3f offset = Base::toVector<float>(vec);
|
||||
std::vector<value_type>& kernel = getBasicPoints();
|
||||
#ifdef _MSC_VER
|
||||
#if defined(FREECAD_WEB_CORE)
|
||||
std::for_each(kernel.begin(), kernel.end(), [offset](value_type& value) {
|
||||
value += offset;
|
||||
});
|
||||
#elif defined(_MSC_VER)
|
||||
Concurrency::parallel_for_each(kernel.begin(), kernel.end(), [offset](value_type& value) {
|
||||
value += offset;
|
||||
});
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include <QtConcurrentMap>
|
||||
#ifndef FREECAD_WEB_CORE
|
||||
#include <QtConcurrentMap>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
@@ -38,9 +40,9 @@
|
||||
#include "Points.h"
|
||||
#include "Properties.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# include <ppl.h>
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
# include <ppl.h>
|
||||
#endif
|
||||
|
||||
|
||||
using namespace Points;
|
||||
@@ -383,11 +385,15 @@ void PropertyNormalList::transformGeometry(const Base::Matrix4D& mat)
|
||||
aboutToSetValue();
|
||||
|
||||
// Rotate the normal vectors
|
||||
#ifdef _MSC_VER
|
||||
#if defined(FREECAD_WEB_CORE)
|
||||
std::for_each(_lValueList.begin(), _lValueList.end(), [rot](Base::Vector3f& value) {
|
||||
rot.multVec(value, value);
|
||||
});
|
||||
#elif defined(_MSC_VER)
|
||||
Concurrency::parallel_for_each(_lValueList.begin(), _lValueList.end(), [rot](Base::Vector3f& value) {
|
||||
value = rot * value;
|
||||
});
|
||||
#else
|
||||
#else
|
||||
QtConcurrent::blockingMap(_lValueList, [rot](Base::Vector3f& value) {
|
||||
rot.multVec(value, value);
|
||||
});
|
||||
|
||||
@@ -74,6 +74,81 @@ set_target_properties(FreeCADWebModuleLoaderProbe PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
||||
)
|
||||
|
||||
add_executable(FreeCADWebImportRuntimeProbe ImportRuntimeProbe.cpp)
|
||||
target_compile_features(FreeCADWebImportRuntimeProbe PRIVATE cxx_std_17)
|
||||
target_compile_options(FreeCADWebImportRuntimeProbe PRIVATE "-O2" "-g0" "-fexceptions")
|
||||
target_include_directories(FreeCADWebImportRuntimeProbe PRIVATE
|
||||
"${CMAKE_BINARY_DIR}"
|
||||
"${CMAKE_BINARY_DIR}/src"
|
||||
"${CMAKE_SOURCE_DIR}/src"
|
||||
)
|
||||
target_link_libraries(FreeCADWebImportRuntimeProbe PRIVATE
|
||||
Import
|
||||
${Python3_LIBRARIES}
|
||||
"${_freecad_web_python_build}/Modules/_decimal/libmpdec/libmpdec.a"
|
||||
"${_freecad_web_python_build}/Modules/_hacl/libHacl_Hash_SHA2.a"
|
||||
"${_freecad_web_python_build}/Modules/expat/libexpat.a"
|
||||
)
|
||||
target_link_options(FreeCADWebImportRuntimeProbe PRIVATE
|
||||
"-O2"
|
||||
"-g0"
|
||||
"--no-entry"
|
||||
"-sWASM=1"
|
||||
"-sMODULARIZE=1"
|
||||
"-sEXPORT_ES6=1"
|
||||
"-sEXPORT_NAME=FreeCADWebImportRuntimeProbe"
|
||||
"-sALLOW_MEMORY_GROWTH=1"
|
||||
"-sINITIAL_MEMORY=33554432"
|
||||
"-sENVIRONMENT=web,worker,node"
|
||||
"-sUSE_BZIP2=1"
|
||||
"-sDISABLE_EXCEPTION_CATCHING=0"
|
||||
"-sEXPORTED_RUNTIME_METHODS=['ccall']"
|
||||
"--embed-file=${_freecad_web_python_encodings}@/freecad-python/encodings"
|
||||
"--embed-file=${CMAKE_SOURCE_DIR}/../web/public/samples/freecad-sample.step@/fixtures/freecad-sample.step"
|
||||
)
|
||||
set_target_properties(FreeCADWebImportRuntimeProbe PROPERTIES
|
||||
SUFFIX ".js"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
||||
)
|
||||
|
||||
if(BUILD_INSPECTION)
|
||||
add_executable(FreeCADWebInspectionRuntimeProbe InspectionRuntimeProbe.cpp)
|
||||
target_compile_features(FreeCADWebInspectionRuntimeProbe PRIVATE cxx_std_17)
|
||||
target_compile_options(FreeCADWebInspectionRuntimeProbe PRIVATE "-O2" "-g0" "-fexceptions")
|
||||
target_include_directories(FreeCADWebInspectionRuntimeProbe PRIVATE
|
||||
"${CMAKE_BINARY_DIR}"
|
||||
"${CMAKE_BINARY_DIR}/src"
|
||||
"${CMAKE_SOURCE_DIR}/src"
|
||||
)
|
||||
target_link_libraries(FreeCADWebInspectionRuntimeProbe PRIVATE
|
||||
Inspection
|
||||
${Python3_LIBRARIES}
|
||||
"${_freecad_web_python_build}/Modules/_decimal/libmpdec/libmpdec.a"
|
||||
"${_freecad_web_python_build}/Modules/_hacl/libHacl_Hash_SHA2.a"
|
||||
"${_freecad_web_python_build}/Modules/expat/libexpat.a"
|
||||
)
|
||||
target_link_options(FreeCADWebInspectionRuntimeProbe PRIVATE
|
||||
"-O2"
|
||||
"-g0"
|
||||
"--no-entry"
|
||||
"-sWASM=1"
|
||||
"-sMODULARIZE=1"
|
||||
"-sEXPORT_ES6=1"
|
||||
"-sEXPORT_NAME=FreeCADWebInspectionRuntimeProbe"
|
||||
"-sALLOW_MEMORY_GROWTH=1"
|
||||
"-sINITIAL_MEMORY=33554432"
|
||||
"-sENVIRONMENT=web,worker,node"
|
||||
"-sUSE_BZIP2=1"
|
||||
"-sDISABLE_EXCEPTION_CATCHING=0"
|
||||
"-sEXPORTED_RUNTIME_METHODS=['ccall']"
|
||||
"--embed-file=${_freecad_web_python_encodings}@/freecad-python/encodings"
|
||||
)
|
||||
set_target_properties(FreeCADWebInspectionRuntimeProbe PROPERTIES
|
||||
SUFFIX ".js"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
||||
)
|
||||
endif()
|
||||
|
||||
add_executable(FreeCADWebAssemblyProbe AssemblyRuntimeProbe.cpp)
|
||||
target_compile_features(FreeCADWebAssemblyProbe PRIVATE cxx_std_17)
|
||||
target_compile_options(FreeCADWebAssemblyProbe PRIVATE "-O2" "-g0" "-fexceptions")
|
||||
|
||||
452
src/WebAssembly/FreeCADCoreProbe/ImportRuntimeProbe.cpp
Normal file
452
src/WebAssembly/FreeCADCoreProbe/ImportRuntimeProbe.cpp
Normal file
@@ -0,0 +1,452 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include <App/Application.h>
|
||||
#include <App/Document.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/FileInfo.h>
|
||||
#include <BRepGProp.hxx>
|
||||
#include <GProp_GProps.hxx>
|
||||
#include <Mod/Import/App/ImportOCAF2.h>
|
||||
#include <Mod/Import/App/ReaderStep.h>
|
||||
#include <Mod/Material/App/MaterialManager.h>
|
||||
#include <Mod/Part/App/PartFeature.h>
|
||||
#include <Standard_Failure.hxx>
|
||||
#include <TDocStd_Document.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <XCAFApp_Application.hxx>
|
||||
|
||||
#include <Python.h>
|
||||
#include <emscripten/emscripten.h>
|
||||
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
extern "C" PyObject* PyInit_Import();
|
||||
extern "C" PyObject* PyInit_Materials();
|
||||
extern "C" PyObject* PyInit_Part();
|
||||
|
||||
namespace {
|
||||
|
||||
std::string lastErrorCode;
|
||||
std::string lastErrorMessage;
|
||||
std::string lastTypeName;
|
||||
unsigned int initializationCalls = 0;
|
||||
unsigned int initializationRuns = 0;
|
||||
bool importInitialized = false;
|
||||
std::vector<App::Document*> documentHandles;
|
||||
unsigned int lastObjectCount = 0;
|
||||
unsigned int lastPartCount = 0;
|
||||
unsigned int lastSolidCount = 0;
|
||||
double lastVolume = 0.0;
|
||||
std::string lastObjectType;
|
||||
std::string persistedPath;
|
||||
unsigned int persistedSize = 0;
|
||||
unsigned int reopenedObjectCount = 0;
|
||||
unsigned int reopenedPartCount = 0;
|
||||
unsigned int reopenedSolidCount = 0;
|
||||
double reopenedVolume = 0.0;
|
||||
|
||||
void setError(const char* code, const std::string& message)
|
||||
{
|
||||
lastErrorCode = code;
|
||||
lastErrorMessage = message;
|
||||
}
|
||||
|
||||
void clearError()
|
||||
{
|
||||
lastErrorCode.clear();
|
||||
lastErrorMessage.clear();
|
||||
}
|
||||
|
||||
std::string pythonError()
|
||||
{
|
||||
PyObject* type = nullptr;
|
||||
PyObject* value = nullptr;
|
||||
PyObject* traceback = nullptr;
|
||||
PyErr_Fetch(&type, &value, &traceback);
|
||||
PyErr_NormalizeException(&type, &value, &traceback);
|
||||
std::string message = "PythonError";
|
||||
if (value) {
|
||||
PyObject* text = PyObject_Str(value);
|
||||
if (text) {
|
||||
message = PyUnicode_AsUTF8(text);
|
||||
Py_DECREF(text);
|
||||
}
|
||||
}
|
||||
Py_XDECREF(type);
|
||||
Py_XDECREF(value);
|
||||
Py_XDECREF(traceback);
|
||||
return message;
|
||||
}
|
||||
|
||||
void addBuiltin(const char* name, PyObject* (*initialize)())
|
||||
{
|
||||
PyObject* modules = PyImport_GetModuleDict();
|
||||
if (PyDict_GetItemString(modules, name)) {
|
||||
return;
|
||||
}
|
||||
PyObject* module = initialize();
|
||||
if (!module) {
|
||||
throw Base::RuntimeError(std::string("PyInit_") + name + " failed: " + pythonError());
|
||||
}
|
||||
if (PyDict_SetItemString(modules, name, module) != 0) {
|
||||
Py_DECREF(module);
|
||||
throw Base::RuntimeError(std::string("failed to register ") + name);
|
||||
}
|
||||
Py_DECREF(module);
|
||||
}
|
||||
|
||||
bool ensureImport()
|
||||
{
|
||||
++initializationCalls;
|
||||
if (importInitialized) {
|
||||
clearError();
|
||||
return true;
|
||||
}
|
||||
|
||||
++initializationRuns;
|
||||
try {
|
||||
App::Application::initWebCore();
|
||||
addBuiltin("Materials", &PyInit_Materials);
|
||||
addBuiltin("Part", &PyInit_Part);
|
||||
addBuiltin("Import", &PyInit_Import);
|
||||
importInitialized = true;
|
||||
clearError();
|
||||
return true;
|
||||
}
|
||||
catch (const Base::Exception& error) {
|
||||
setError("initialization_failed", error.what());
|
||||
}
|
||||
catch (const std::exception& error) {
|
||||
setError("initialization_failed", error.what());
|
||||
}
|
||||
catch (...) {
|
||||
setError("initialization_failed", "unknown Import initialization failure");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool requireModule(const char* moduleName)
|
||||
{
|
||||
if (moduleName == nullptr || *moduleName == '\0') {
|
||||
setError("invalid_argument", "module name is empty");
|
||||
return false;
|
||||
}
|
||||
if (std::string(moduleName) != "Import") {
|
||||
setError(
|
||||
std::string(moduleName) == "ImportGui" ? "gui_dependency_unavailable" : "module_unavailable",
|
||||
"module is not available in the GUI-free Web kernel: " + std::string(moduleName));
|
||||
return false;
|
||||
}
|
||||
return ensureImport();
|
||||
}
|
||||
|
||||
void closeDocument(App::Document*& document)
|
||||
{
|
||||
if (document) {
|
||||
App::GetApplication().closeDocument(document);
|
||||
document = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int importStep(const char* fileName)
|
||||
{
|
||||
std::string stage = "initializing Import";
|
||||
if (!ensureImport()) {
|
||||
return 0;
|
||||
}
|
||||
if (fileName == nullptr || *fileName == '\0') {
|
||||
setError("invalid_argument", "STEP path is empty");
|
||||
return 0;
|
||||
}
|
||||
Base::FileInfo file(fileName);
|
||||
if (!file.exists()) {
|
||||
setError("file_not_found", "STEP fixture does not exist: " + std::string(fileName));
|
||||
return 0;
|
||||
}
|
||||
|
||||
App::Document* document = nullptr;
|
||||
Handle(TDocStd_Document) ocafDocument;
|
||||
try {
|
||||
stage = "creating OCAF document";
|
||||
Handle(XCAFApp_Application) ocafApplication = XCAFApp_Application::GetApplication();
|
||||
ocafApplication->NewDocument(TCollection_ExtendedString("MDTV-CAF"), ocafDocument);
|
||||
stage = "reading STEP fixture";
|
||||
Import::ReaderStep reader(file);
|
||||
reader.read(ocafDocument);
|
||||
|
||||
stage = "creating FreeCAD document";
|
||||
document = App::GetApplication().newDocument("ImportKernelDocument", "Import Kernel Document", {false, true});
|
||||
if (!document) {
|
||||
throw Base::RuntimeError("Application::newDocument returned null");
|
||||
}
|
||||
stage = "loading OCAF shapes";
|
||||
Import::ImportOCAFExt importer(ocafDocument, document, file.fileNamePure());
|
||||
importer.setImportOptions(Import::ImportOCAFExt::customImportOptions());
|
||||
App::DocumentObject* root = importer.loadShapes();
|
||||
bool hasError = false;
|
||||
stage = "recomputing imported document";
|
||||
document->recompute({}, false, &hasError);
|
||||
if (hasError) {
|
||||
throw Base::RuntimeError("imported document recompute reported an error");
|
||||
}
|
||||
|
||||
stage = "summarizing imported geometry";
|
||||
lastObjectCount = document->countObjects();
|
||||
lastPartCount = 0;
|
||||
lastSolidCount = 0;
|
||||
lastVolume = 0.0;
|
||||
lastObjectType = root ? root->getTypeId().getName() : "";
|
||||
for (App::DocumentObject* object : document->getObjects()) {
|
||||
auto* part = dynamic_cast<Part::Feature*>(object);
|
||||
if (!part || part->Shape.getValue().IsNull()) {
|
||||
continue;
|
||||
}
|
||||
++lastPartCount;
|
||||
const TopoDS_Shape& shape = part->Shape.getValue();
|
||||
GProp_GProps properties;
|
||||
BRepGProp::VolumeProperties(shape, properties);
|
||||
lastVolume += properties.Mass();
|
||||
for (TopExp_Explorer explorer(shape, TopAbs_SOLID); explorer.More(); explorer.Next()) {
|
||||
++lastSolidCount;
|
||||
}
|
||||
if (lastObjectType.empty()) {
|
||||
lastObjectType = object->getTypeId().getName();
|
||||
}
|
||||
}
|
||||
ocafApplication->Close(ocafDocument);
|
||||
ocafDocument.Nullify();
|
||||
if (lastObjectCount == 0 || lastPartCount == 0 || lastSolidCount == 0 || lastVolume <= 0.0) {
|
||||
throw Base::RuntimeError("imported STEP summary is empty");
|
||||
}
|
||||
documentHandles.push_back(document);
|
||||
clearError();
|
||||
return static_cast<int>(documentHandles.size());
|
||||
}
|
||||
catch (const Base::Exception& error) {
|
||||
if (!ocafDocument.IsNull()) {
|
||||
XCAFApp_Application::GetApplication()->Close(ocafDocument);
|
||||
}
|
||||
closeDocument(document);
|
||||
setError("import_failed", error.what());
|
||||
}
|
||||
catch (const std::exception& error) {
|
||||
if (!ocafDocument.IsNull()) {
|
||||
XCAFApp_Application::GetApplication()->Close(ocafDocument);
|
||||
}
|
||||
closeDocument(document);
|
||||
setError("import_failed", error.what());
|
||||
}
|
||||
catch (const Standard_Failure& error) {
|
||||
if (!ocafDocument.IsNull()) {
|
||||
XCAFApp_Application::GetApplication()->Close(ocafDocument);
|
||||
}
|
||||
closeDocument(document);
|
||||
setError("import_failed", error.GetMessageString());
|
||||
}
|
||||
catch (...) {
|
||||
if (!ocafDocument.IsNull()) {
|
||||
XCAFApp_Application::GetApplication()->Close(ocafDocument);
|
||||
}
|
||||
closeDocument(document);
|
||||
setError("import_failed", "unknown STEP import failure while " + stage);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE int fc_web_import_initialize(const char* moduleName)
|
||||
{
|
||||
return requireModule(moduleName) ? 1 : 0;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE int fc_web_import_type_probe(const char* typeName)
|
||||
{
|
||||
if (!requireModule("Import")) {
|
||||
return 0;
|
||||
}
|
||||
if (typeName == nullptr || *typeName == '\0') {
|
||||
setError("invalid_argument", "type name is empty");
|
||||
return 0;
|
||||
}
|
||||
PyObject* module = PyImport_GetModuleDict()
|
||||
? PyDict_GetItemString(PyImport_GetModuleDict(), "Import")
|
||||
: nullptr;
|
||||
PyObject* type = module ? PyObject_GetAttrString(module, typeName) : nullptr;
|
||||
if (!type || !PyType_Check(type)) {
|
||||
Py_XDECREF(type);
|
||||
PyErr_Clear();
|
||||
lastTypeName.clear();
|
||||
setError("type_not_found", "type is not registered in Import: " + std::string(typeName));
|
||||
return 0;
|
||||
}
|
||||
lastTypeName = std::string("Import.") + typeName;
|
||||
Py_DECREF(type);
|
||||
clearError();
|
||||
return 1;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE const char* fc_web_import_error_code()
|
||||
{
|
||||
return lastErrorCode.c_str();
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE const char* fc_web_import_error_message()
|
||||
{
|
||||
return lastErrorMessage.c_str();
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE const char* fc_web_import_last_type()
|
||||
{
|
||||
return lastTypeName.c_str();
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE unsigned int fc_web_import_initialization_calls()
|
||||
{
|
||||
return initializationCalls;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE unsigned int fc_web_import_initialization_runs()
|
||||
{
|
||||
return initializationRuns;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE int fc_web_import_initialized()
|
||||
{
|
||||
return importInitialized ? 1 : 0;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE int fc_web_import_step(const char* fileName)
|
||||
{
|
||||
return importStep(fileName);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE int fc_web_import_release(int handle)
|
||||
{
|
||||
if (handle <= 0 || static_cast<std::size_t>(handle) > documentHandles.size()
|
||||
|| documentHandles[handle - 1] == nullptr) {
|
||||
setError("invalid_handle", "document handle is unknown or already released");
|
||||
return 0;
|
||||
}
|
||||
closeDocument(documentHandles[handle - 1]);
|
||||
clearError();
|
||||
return 1;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE int fc_web_import_active_handles()
|
||||
{
|
||||
int active = 0;
|
||||
for (const auto* document : documentHandles) {
|
||||
active += document ? 1 : 0;
|
||||
}
|
||||
return active;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE unsigned int fc_web_import_object_count()
|
||||
{
|
||||
return lastObjectCount;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE unsigned int fc_web_import_part_count()
|
||||
{
|
||||
return lastPartCount;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE unsigned int fc_web_import_solid_count()
|
||||
{
|
||||
return lastSolidCount;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE double fc_web_import_volume()
|
||||
{
|
||||
return lastVolume;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE const char* fc_web_import_object_type()
|
||||
{
|
||||
return lastObjectType.c_str();
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE int fc_web_import_save_reopen()
|
||||
{
|
||||
if (documentHandles.empty() || documentHandles.back() == nullptr) {
|
||||
setError("invalid_handle", "no imported document is available for save/reopen");
|
||||
return 0;
|
||||
}
|
||||
auto* sourceDocument = documentHandles.back();
|
||||
const char* stage = "saving source document";
|
||||
try {
|
||||
persistedPath = "/tmp/ImportCreateDocument.FCStd";
|
||||
for (App::DocumentObject* object : sourceDocument->getObjects()) {
|
||||
if (auto* part = dynamic_cast<Part::Feature*>(object)) {
|
||||
part->ShapeMaterial.setStatus(App::Property::Transient, true);
|
||||
}
|
||||
}
|
||||
if (!sourceDocument->saveAs(persistedPath.c_str())) {
|
||||
setError("save_failed", "Import document saveAs returned false");
|
||||
return 0;
|
||||
}
|
||||
Base::FileInfo file(persistedPath);
|
||||
persistedSize = file.size();
|
||||
stage = "closing source document";
|
||||
closeDocument(sourceDocument);
|
||||
documentHandles.back() = nullptr;
|
||||
stage = "creating reopened document";
|
||||
auto* reopened = App::GetApplication().newDocument("ReopenedImportDocument", "Reopened Import Document", {false, true});
|
||||
if (!reopened) {
|
||||
setError("reopen_failed", "Application::newDocument returned null");
|
||||
return 0;
|
||||
}
|
||||
reopened->FileName.setValue(persistedPath);
|
||||
stage = "initializing material registry";
|
||||
Materials::MaterialManager::getManager();
|
||||
stage = "restoring document XML";
|
||||
reopened->restore(persistedPath.c_str(), true, {});
|
||||
bool hasError = false;
|
||||
stage = "recomputing reopened document";
|
||||
reopened->recompute({}, false, &hasError);
|
||||
stage = "summarizing reopened document";
|
||||
reopenedObjectCount = reopened->countObjects();
|
||||
reopenedPartCount = 0;
|
||||
reopenedSolidCount = 0;
|
||||
reopenedVolume = 0.0;
|
||||
for (App::DocumentObject* object : reopened->getObjects()) {
|
||||
auto* part = dynamic_cast<Part::Feature*>(object);
|
||||
if (!part || part->Shape.getValue().IsNull()) continue;
|
||||
++reopenedPartCount;
|
||||
const TopoDS_Shape& shape = part->Shape.getValue();
|
||||
GProp_GProps properties;
|
||||
BRepGProp::VolumeProperties(shape, properties);
|
||||
reopenedVolume += properties.Mass();
|
||||
for (TopExp_Explorer explorer(shape, TopAbs_SOLID); explorer.More(); explorer.Next()) ++reopenedSolidCount;
|
||||
}
|
||||
const bool valid = !hasError && persistedSize > 22 && reopenedObjectCount == lastObjectCount && reopenedPartCount == lastPartCount && reopenedSolidCount == lastSolidCount && std::abs(reopenedVolume - lastVolume) < 1e-6;
|
||||
closeDocument(reopened);
|
||||
if (!valid) {
|
||||
setError("reopen_failed", "reopened Import document summary mismatch");
|
||||
return 0;
|
||||
}
|
||||
clearError();
|
||||
return 1;
|
||||
}
|
||||
catch (const Base::Exception& error) {
|
||||
setError("save_reopen_failed", std::string(stage) + ": " + error.what());
|
||||
}
|
||||
catch (const std::exception& error) {
|
||||
setError("save_reopen_failed", std::string(stage) + ": " + error.what());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE const char* fc_web_import_persisted_path() { return persistedPath.c_str(); }
|
||||
EMSCRIPTEN_KEEPALIVE unsigned int fc_web_import_persisted_size() { return persistedSize; }
|
||||
EMSCRIPTEN_KEEPALIVE unsigned int fc_web_import_reopened_object_count() { return reopenedObjectCount; }
|
||||
EMSCRIPTEN_KEEPALIVE unsigned int fc_web_import_reopened_part_count() { return reopenedPartCount; }
|
||||
EMSCRIPTEN_KEEPALIVE unsigned int fc_web_import_reopened_solid_count() { return reopenedSolidCount; }
|
||||
EMSCRIPTEN_KEEPALIVE double fc_web_import_reopened_volume() { return reopenedVolume; }
|
||||
|
||||
} // extern "C"
|
||||
231
src/WebAssembly/FreeCADCoreProbe/InspectionRuntimeProbe.cpp
Normal file
231
src/WebAssembly/FreeCADCoreProbe/InspectionRuntimeProbe.cpp
Normal file
@@ -0,0 +1,231 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include <App/Application.h>
|
||||
#include <App/Document.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Type.h>
|
||||
#include <Mod/Inspection/App/InspectionFeature.h>
|
||||
#include <Mod/Part/App/FeaturePartBox.h>
|
||||
|
||||
#include <Python.h>
|
||||
#include <emscripten/emscripten.h>
|
||||
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
extern "C" PyObject* PyInit_Inspection();
|
||||
extern "C" PyObject* PyInit_Materials();
|
||||
extern "C" PyObject* PyInit_Mesh();
|
||||
extern "C" PyObject* PyInit_Part();
|
||||
extern "C" PyObject* PyInit_Points();
|
||||
|
||||
namespace {
|
||||
|
||||
std::string lastErrorCode;
|
||||
std::string lastErrorMessage;
|
||||
std::string lastTypeName;
|
||||
unsigned int initializationCalls = 0;
|
||||
unsigned int initializationRuns = 0;
|
||||
bool inspectionInitialized = false;
|
||||
unsigned int lastDistanceCount = 0;
|
||||
double lastDistanceRms = 0.0;
|
||||
unsigned int lastDocumentObjects = 0;
|
||||
|
||||
void setError(const char* code, const std::string& message)
|
||||
{
|
||||
lastErrorCode = code;
|
||||
lastErrorMessage = message;
|
||||
}
|
||||
|
||||
void clearError()
|
||||
{
|
||||
lastErrorCode.clear();
|
||||
lastErrorMessage.clear();
|
||||
}
|
||||
|
||||
std::string pythonError()
|
||||
{
|
||||
PyObject* type = nullptr;
|
||||
PyObject* value = nullptr;
|
||||
PyObject* traceback = nullptr;
|
||||
PyErr_Fetch(&type, &value, &traceback);
|
||||
PyErr_NormalizeException(&type, &value, &traceback);
|
||||
std::string message = "PythonError";
|
||||
if (value) {
|
||||
PyObject* text = PyObject_Str(value);
|
||||
if (text) {
|
||||
message = PyUnicode_AsUTF8(text);
|
||||
Py_DECREF(text);
|
||||
}
|
||||
}
|
||||
Py_XDECREF(type);
|
||||
Py_XDECREF(value);
|
||||
Py_XDECREF(traceback);
|
||||
return message;
|
||||
}
|
||||
|
||||
void addBuiltin(const char* name, PyObject* (*initialize)())
|
||||
{
|
||||
PyObject* modules = PyImport_GetModuleDict();
|
||||
if (PyDict_GetItemString(modules, name)) {
|
||||
return;
|
||||
}
|
||||
PyObject* module = initialize();
|
||||
if (!module) {
|
||||
throw Base::RuntimeError(std::string("PyInit_") + name + " failed: " + pythonError());
|
||||
}
|
||||
if (PyDict_SetItemString(modules, name, module) != 0) {
|
||||
Py_DECREF(module);
|
||||
throw Base::RuntimeError(std::string("failed to register ") + name);
|
||||
}
|
||||
Py_DECREF(module);
|
||||
}
|
||||
|
||||
bool ensureInspection()
|
||||
{
|
||||
++initializationCalls;
|
||||
if (inspectionInitialized) {
|
||||
clearError();
|
||||
return true;
|
||||
}
|
||||
|
||||
++initializationRuns;
|
||||
try {
|
||||
App::Application::initWebCore();
|
||||
addBuiltin("Materials", &PyInit_Materials);
|
||||
addBuiltin("Part", &PyInit_Part);
|
||||
addBuiltin("Mesh", &PyInit_Mesh);
|
||||
addBuiltin("Points", &PyInit_Points);
|
||||
addBuiltin("Inspection", &PyInit_Inspection);
|
||||
inspectionInitialized = true;
|
||||
clearError();
|
||||
return true;
|
||||
}
|
||||
catch (const Base::Exception& error) {
|
||||
setError("initialization_failed", error.what());
|
||||
}
|
||||
catch (const std::exception& error) {
|
||||
setError("initialization_failed", error.what());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool requireModule(const char* moduleName)
|
||||
{
|
||||
if (moduleName == nullptr || *moduleName == '\0') {
|
||||
setError("invalid_argument", "module name is empty");
|
||||
return false;
|
||||
}
|
||||
if (std::string(moduleName) != "Inspection") {
|
||||
setError(
|
||||
std::string(moduleName) == "InspectionGui" ? "gui_dependency_unavailable" : "module_unavailable",
|
||||
"module is not available in the GUI-free Web kernel: " + std::string(moduleName));
|
||||
return false;
|
||||
}
|
||||
return ensureInspection();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE int fc_web_inspection_initialize(const char* moduleName)
|
||||
{
|
||||
return requireModule(moduleName) ? 1 : 0;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE int fc_web_inspection_type_probe(const char* typeName)
|
||||
{
|
||||
if (!requireModule("Inspection")) {
|
||||
return 0;
|
||||
}
|
||||
if (typeName == nullptr || *typeName == '\0') {
|
||||
setError("invalid_argument", "type name is empty");
|
||||
return 0;
|
||||
}
|
||||
Base::Type type = Base::Type::fromName(typeName);
|
||||
if (type.isBad()) {
|
||||
lastTypeName.clear();
|
||||
setError("type_not_found", "type is not registered: " + std::string(typeName));
|
||||
return 0;
|
||||
}
|
||||
lastTypeName = type.getName();
|
||||
clearError();
|
||||
return 1;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE const char* fc_web_inspection_error_code() { return lastErrorCode.c_str(); }
|
||||
EMSCRIPTEN_KEEPALIVE const char* fc_web_inspection_error_message() { return lastErrorMessage.c_str(); }
|
||||
EMSCRIPTEN_KEEPALIVE const char* fc_web_inspection_last_type() { return lastTypeName.c_str(); }
|
||||
EMSCRIPTEN_KEEPALIVE unsigned int fc_web_inspection_initialization_calls() { return initializationCalls; }
|
||||
EMSCRIPTEN_KEEPALIVE unsigned int fc_web_inspection_initialization_runs() { return initializationRuns; }
|
||||
EMSCRIPTEN_KEEPALIVE int fc_web_inspection_initialized() { return inspectionInitialized ? 1 : 0; }
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE int fc_web_inspection_create_recompute()
|
||||
{
|
||||
if (!ensureInspection()) return 0;
|
||||
App::Document* document = nullptr;
|
||||
try {
|
||||
document = App::GetApplication().newDocument("InspectionKernelDocument", "Inspection Kernel Document", {false, true});
|
||||
auto* actual = document ? document->addObject("Part::Box", "Actual") : nullptr;
|
||||
auto* nominal = document ? document->addObject("Part::Box", "Nominal") : nullptr;
|
||||
auto* feature = document ? document->addObject("Inspection::Feature", "Inspection") : nullptr;
|
||||
if (!actual || !nominal || !feature) throw Base::RuntimeError("Inspection fixture object creation failed");
|
||||
static_cast<Part::Box*>(actual)->Length.setValue(2.0);
|
||||
static_cast<Part::Box*>(actual)->Width.setValue(3.0);
|
||||
static_cast<Part::Box*>(actual)->Height.setValue(4.0);
|
||||
static_cast<Part::Box*>(nominal)->Length.setValue(2.0);
|
||||
static_cast<Part::Box*>(nominal)->Width.setValue(3.0);
|
||||
static_cast<Part::Box*>(nominal)->Height.setValue(4.0);
|
||||
auto* inspection = static_cast<Inspection::Feature*>(feature);
|
||||
inspection->Actual.setValue(actual);
|
||||
inspection->Nominals.setValues(std::vector<App::DocumentObject*>{nominal});
|
||||
inspection->SearchRadius.setValue(10.0);
|
||||
bool hasError = false;
|
||||
document->recompute({}, false, &hasError);
|
||||
lastDistanceCount = static_cast<unsigned int>(inspection->Distances.getValues().size());
|
||||
lastDistanceRms = 0.0;
|
||||
for (float distance : inspection->Distances.getValues()) {
|
||||
lastDistanceRms += static_cast<double>(distance) * static_cast<double>(distance);
|
||||
}
|
||||
if (lastDistanceCount > 0) lastDistanceRms = std::sqrt(lastDistanceRms / lastDistanceCount);
|
||||
lastDocumentObjects = document->countObjects();
|
||||
const bool valid = !hasError && lastDistanceCount > 0 && lastDocumentObjects == 3;
|
||||
App::GetApplication().closeDocument(document);
|
||||
document = nullptr;
|
||||
if (!valid) { setError("recompute_failed", "Inspection fixture recompute produced no distances"); return 0; }
|
||||
clearError();
|
||||
return 1;
|
||||
}
|
||||
catch (const Base::Exception& error) {
|
||||
if (document) App::GetApplication().closeDocument(document);
|
||||
setError("recompute_failed", error.what());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EMSCRIPTEN_KEEPALIVE unsigned int fc_web_inspection_distance_count() { return lastDistanceCount; }
|
||||
EMSCRIPTEN_KEEPALIVE double fc_web_inspection_distance_rms() { return lastDistanceRms; }
|
||||
EMSCRIPTEN_KEEPALIVE unsigned int fc_web_inspection_document_objects() { return lastDocumentObjects; }
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE int fc_web_inspection_invalid_recompute()
|
||||
{
|
||||
if (!ensureInspection()) return 0;
|
||||
App::Document* document = App::GetApplication().newDocument("InspectionInvalidDocument", "Inspection Invalid Document", {false, true});
|
||||
try {
|
||||
if (!document || !document->addObject("Inspection::Feature", "Inspection")) throw Base::RuntimeError("Inspection invalid fixture creation failed");
|
||||
bool hasError = false;
|
||||
document->recompute({}, false, &hasError);
|
||||
App::GetApplication().closeDocument(document);
|
||||
if (!hasError) { setError("invalid_fixture", "Inspection missing actual geometry was accepted"); return 0; }
|
||||
setError("invalid_fixture", "No actual geometry to inspect specified");
|
||||
return 1;
|
||||
}
|
||||
catch (const Base::Exception& error) {
|
||||
if (document) App::GetApplication().closeDocument(document);
|
||||
setError("invalid_fixture", error.what());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
138
src/WebAssembly/FreeCADCoreProbe/import-runtime-smoke.js
Normal file
138
src/WebAssembly/FreeCADCoreProbe/import-runtime-smoke.js
Normal file
@@ -0,0 +1,138 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
const fs = require("fs");
|
||||
const crypto = require("crypto");
|
||||
const path = require("path");
|
||||
const { pathToFileURL } = require("url");
|
||||
|
||||
const buildDirectory = path.resolve(process.argv[2] || "build-web/configure/bin");
|
||||
|
||||
async function main()
|
||||
{
|
||||
globalThis.__dirname = `${buildDirectory}${path.sep}`;
|
||||
globalThis.require = require;
|
||||
const moduleUrl = pathToFileURL(path.join(buildDirectory, "FreeCADWebImportRuntimeProbe.js")).href;
|
||||
const module = await import(moduleUrl);
|
||||
const probe = await module.default({
|
||||
wasmBinary: fs.readFileSync(path.join(buildDirectory, "FreeCADWebImportRuntimeProbe.wasm")),
|
||||
});
|
||||
const call = (name, returnType = "number", argTypes = [], args = []) => probe.ccall(name, returnType, argTypes, args);
|
||||
const error = () => ({
|
||||
code: call("fc_web_import_error_code", "string"),
|
||||
message: call("fc_web_import_error_message", "string"),
|
||||
});
|
||||
const state = () => ({
|
||||
calls: call("fc_web_import_initialization_calls"),
|
||||
runs: call("fc_web_import_initialization_runs"),
|
||||
initialized: call("fc_web_import_initialized"),
|
||||
});
|
||||
const result = {};
|
||||
|
||||
result.initialize = call("fc_web_import_initialize", "number", ["string"], ["Import"]);
|
||||
result.stateAfterInitialize = state();
|
||||
result.initializeAgain = call("fc_web_import_initialize", "number", ["string"], ["Import"]);
|
||||
result.stateAfterRepeat = state();
|
||||
result.type = call("fc_web_import_type_probe", "number", ["string"], ["StepShape"]);
|
||||
result.typeName = call("fc_web_import_last_type", "string");
|
||||
result.unknownType = call("fc_web_import_type_probe", "number", ["string"], ["MissingType"]);
|
||||
result.unknownTypeError = error();
|
||||
result.unknownModule = call("fc_web_import_initialize", "number", ["string"], ["Missing"]);
|
||||
result.unknownModuleError = error();
|
||||
result.guiModule = call("fc_web_import_initialize", "number", ["string"], ["ImportGui"]);
|
||||
result.guiModuleError = error();
|
||||
result.emptyModule = call("fc_web_import_initialize", "number", ["string"], [""]);
|
||||
result.emptyModuleError = error();
|
||||
result.stateAfterErrors = state();
|
||||
|
||||
const fixture = "/fixtures/freecad-sample.step";
|
||||
const summary = () => ({
|
||||
objects: call("fc_web_import_object_count"),
|
||||
parts: call("fc_web_import_part_count"),
|
||||
solids: call("fc_web_import_solid_count"),
|
||||
volume: call("fc_web_import_volume", "number"),
|
||||
objectType: call("fc_web_import_object_type", "string"),
|
||||
});
|
||||
result.importHandle = call("fc_web_import_step", "number", ["string"], [fixture]);
|
||||
result.firstImportError = error();
|
||||
result.firstImport = summary();
|
||||
result.activeAfterImport = call("fc_web_import_active_handles");
|
||||
result.release = call("fc_web_import_release", "number", ["number"], [result.importHandle]);
|
||||
result.activeAfterRelease = call("fc_web_import_active_handles");
|
||||
result.repeatHandle = call("fc_web_import_step", "number", ["string"], [fixture]);
|
||||
result.repeatImport = summary();
|
||||
result.repeatRelease = call("fc_web_import_release", "number", ["number"], [result.repeatHandle]);
|
||||
result.activeAfterRepeatRelease = call("fc_web_import_active_handles");
|
||||
result.validSnapshot = {
|
||||
objects: result.repeatImport.objects,
|
||||
parts: result.repeatImport.parts,
|
||||
solids: result.repeatImport.solids,
|
||||
volume: result.repeatImport.volume,
|
||||
objectType: result.repeatImport.objectType,
|
||||
};
|
||||
result.validSnapshotHash = crypto.createHash("sha256")
|
||||
.update(JSON.stringify(result.validSnapshot))
|
||||
.digest("hex");
|
||||
result.missingFile = call("fc_web_import_step", "number", ["string"], ["/fixtures/missing.step"]);
|
||||
result.missingFileError = error();
|
||||
result.afterMissingSnapshot = summary();
|
||||
result.missingFileRepeat = call("fc_web_import_step", "number", ["string"], ["/fixtures/missing.step"]);
|
||||
result.missingFileRepeatError = error();
|
||||
result.afterMissingRepeatSnapshot = summary();
|
||||
result.emptyPath = call("fc_web_import_step", "number", ["string"], [""]);
|
||||
result.emptyPathError = error();
|
||||
|
||||
const valid = result.initialize === 1
|
||||
&& result.initializeAgain === 1
|
||||
&& result.stateAfterInitialize.runs === 1
|
||||
&& result.stateAfterInitialize.initialized === 1
|
||||
&& result.stateAfterRepeat.runs === 1
|
||||
&& result.type === 1
|
||||
&& result.typeName === "Import.StepShape"
|
||||
&& result.unknownType === 0
|
||||
&& result.unknownTypeError.code === "type_not_found"
|
||||
&& result.unknownModule === 0
|
||||
&& result.unknownModuleError.code === "module_unavailable"
|
||||
&& result.guiModule === 0
|
||||
&& result.guiModuleError.code === "gui_dependency_unavailable"
|
||||
&& result.emptyModule === 0
|
||||
&& result.emptyModuleError.code === "invalid_argument"
|
||||
&& result.stateAfterErrors.runs === 1
|
||||
&& result.stateAfterErrors.initialized === 1
|
||||
&& result.importHandle === 1
|
||||
&& result.firstImportError.code === ""
|
||||
&& result.firstImportError.message === ""
|
||||
&& result.firstImport.objects > 0
|
||||
&& result.firstImport.parts > 0
|
||||
&& result.firstImport.solids > 0
|
||||
&& result.firstImport.volume > 0
|
||||
&& result.firstImport.objectType.length > 0
|
||||
&& result.activeAfterImport === 1
|
||||
&& result.release === 1
|
||||
&& result.activeAfterRelease === 0
|
||||
&& result.repeatHandle === 2
|
||||
&& JSON.stringify(result.repeatImport) === JSON.stringify(result.firstImport)
|
||||
&& result.repeatRelease === 1
|
||||
&& result.activeAfterRepeatRelease === 0
|
||||
&& result.validSnapshotHash.length === 64
|
||||
&& result.missingFile === 0
|
||||
&& result.missingFileError.code === "file_not_found";
|
||||
|
||||
const recoveryValid = JSON.stringify(result.afterMissingSnapshot) === JSON.stringify(result.validSnapshot)
|
||||
&& JSON.stringify(result.afterMissingRepeatSnapshot) === JSON.stringify(result.validSnapshot)
|
||||
&& result.missingFileRepeat === 0
|
||||
&& result.missingFileRepeatError.code === "file_not_found"
|
||||
&& result.emptyPath === 0
|
||||
&& result.emptyPathError.code === "invalid_argument";
|
||||
if (!recoveryValid) {
|
||||
throw new Error(JSON.stringify(result));
|
||||
}
|
||||
if (!valid) {
|
||||
throw new Error(JSON.stringify(result));
|
||||
}
|
||||
console.log(JSON.stringify(result));
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
const buildDirectory = path.resolve(process.argv[2] || "build-web/configure/bin");
|
||||
globalThis.__dirname = `${buildDirectory}${path.sep}`;
|
||||
const module = await import(pathToFileURL(path.join(buildDirectory, "FreeCADWebImportRuntimeProbe.js")));
|
||||
const probe = await module.default({
|
||||
wasmBinary: fs.readFileSync(path.join(buildDirectory, "FreeCADWebImportRuntimeProbe.wasm")),
|
||||
});
|
||||
const call = (name, returnType = "number", argTypes = [], args = []) =>
|
||||
probe.ccall(name, returnType, argTypes, args);
|
||||
const kernel = {
|
||||
ccall: (name, returnType, argTypes, args) => call(name, returnType, argTypes, args),
|
||||
};
|
||||
|
||||
const facade = await import(pathToFileURL(path.resolve("FreeCAD/web/dist/assets/import-CK7bBA-v.js")));
|
||||
const envelope = await import(pathToFileURL(path.resolve("FreeCAD/web/dist/assets/facade-envelope-B1V2jxkP.js")));
|
||||
const queue = new envelope.FacadeSerialQueue();
|
||||
const request = facade.createImportSaveReopenRequest("import-save-1");
|
||||
const response = await facade.dispatchImportSaveReopen(queue, request, kernel);
|
||||
const duplicate = await facade.dispatchImportSaveReopen(queue, request, kernel);
|
||||
const invalid = await facade.dispatchImportSaveReopen(queue, facade.createImportSaveReopenRequest("import-save-invalid", {
|
||||
...facade.IMPORT_SAVE_REOPEN_FIXTURE,
|
||||
fixtureSha256: "wrong",
|
||||
}), kernel);
|
||||
|
||||
const valid = response.kind === "result"
|
||||
&& response.requestId === "import-save-1"
|
||||
&& response.result.reopened.objects === response.result.before.objects
|
||||
&& response.result.reopened.parts === response.result.before.parts
|
||||
&& response.result.reopened.solids === response.result.before.solids
|
||||
&& response.result.persisted.size > 22
|
||||
&& duplicate.kind === "error"
|
||||
&& duplicate.error.code === "duplicate_request"
|
||||
&& invalid.kind === "error"
|
||||
&& invalid.error.code === "invalid_fixture";
|
||||
const result = { response, duplicate, invalid };
|
||||
if (!valid) throw new Error(JSON.stringify(result));
|
||||
console.log(JSON.stringify(result));
|
||||
@@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
const buildDirectory = path.resolve(process.argv[2] || "build-web/configure/bin");
|
||||
globalThis.__dirname = `${buildDirectory}${path.sep}`;
|
||||
const module = await import(pathToFileURL(path.join(buildDirectory, "FreeCADWebImportRuntimeProbe.js")));
|
||||
const probe = await module.default({
|
||||
wasmBinary: fs.readFileSync(path.join(buildDirectory, "FreeCADWebImportRuntimeProbe.wasm")),
|
||||
});
|
||||
const call = (name, returnType = "number", argTypes = [], args = []) =>
|
||||
probe.ccall(name, returnType, argTypes, args);
|
||||
const error = () => ({
|
||||
code: call("fc_web_import_error_code", "string"),
|
||||
message: call("fc_web_import_error_message", "string"),
|
||||
});
|
||||
const snapshot = () => ({
|
||||
objects: call("fc_web_import_reopened_object_count"),
|
||||
parts: call("fc_web_import_reopened_part_count"),
|
||||
solids: call("fc_web_import_reopened_solid_count"),
|
||||
volume: call("fc_web_import_reopened_volume", "number"),
|
||||
});
|
||||
|
||||
const importHandle = call(
|
||||
"fc_web_import_step",
|
||||
"number",
|
||||
["string"],
|
||||
["/fixtures/freecad-sample.step"],
|
||||
);
|
||||
const saveReopen = call("fc_web_import_save_reopen");
|
||||
const recovered = snapshot();
|
||||
const invalid = call("fc_web_import_save_reopen");
|
||||
const invalidError = error();
|
||||
const afterInvalid = snapshot();
|
||||
const invalidRepeat = call("fc_web_import_save_reopen");
|
||||
const invalidRepeatError = error();
|
||||
const afterInvalidRepeat = snapshot();
|
||||
|
||||
const result = {
|
||||
importHandle,
|
||||
saveReopen,
|
||||
recovered,
|
||||
invalid,
|
||||
invalidError,
|
||||
afterInvalid,
|
||||
invalidRepeat,
|
||||
invalidRepeatError,
|
||||
afterInvalidRepeat,
|
||||
};
|
||||
const stable = JSON.stringify(recovered) === JSON.stringify(afterInvalid)
|
||||
&& JSON.stringify(recovered) === JSON.stringify(afterInvalidRepeat);
|
||||
const valid = importHandle > 0
|
||||
&& saveReopen === 1
|
||||
&& invalid === 0
|
||||
&& invalidRepeat === 0
|
||||
&& invalidError.code === "invalid_handle"
|
||||
&& invalidRepeatError.code === "invalid_handle"
|
||||
&& invalidError.message === invalidRepeatError.message
|
||||
&& stable;
|
||||
|
||||
if (!valid) {
|
||||
throw new Error(JSON.stringify(result));
|
||||
}
|
||||
console.log(JSON.stringify(result));
|
||||
68
src/WebAssembly/FreeCADCoreProbe/import-save-reopen-smoke.js
Normal file
68
src/WebAssembly/FreeCADCoreProbe/import-save-reopen-smoke.js
Normal file
@@ -0,0 +1,68 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
const buildDirectory = path.resolve(process.argv[2] || "build-web/configure/bin");
|
||||
globalThis.__dirname = `${buildDirectory}${path.sep}`;
|
||||
const module = await import(pathToFileURL(path.join(buildDirectory, "FreeCADWebImportRuntimeProbe.js")));
|
||||
const probe = await module.default({
|
||||
wasmBinary: fs.readFileSync(path.join(buildDirectory, "FreeCADWebImportRuntimeProbe.wasm")),
|
||||
});
|
||||
const call = (name, returnType = "number", argTypes = [], args = []) =>
|
||||
probe.ccall(name, returnType, argTypes, args);
|
||||
const error = () => ({
|
||||
code: call("fc_web_import_error_code", "string"),
|
||||
message: call("fc_web_import_error_message", "string"),
|
||||
});
|
||||
|
||||
const importHandle = call(
|
||||
"fc_web_import_step",
|
||||
"number",
|
||||
["string"],
|
||||
["/fixtures/freecad-sample.step"],
|
||||
);
|
||||
const before = {
|
||||
objects: call("fc_web_import_object_count"),
|
||||
parts: call("fc_web_import_part_count"),
|
||||
solids: call("fc_web_import_solid_count"),
|
||||
volume: call("fc_web_import_volume", "number"),
|
||||
};
|
||||
const saveReopen = call("fc_web_import_save_reopen");
|
||||
const result = {
|
||||
importHandle,
|
||||
saveReopen,
|
||||
error: error(),
|
||||
persistedPath: call("fc_web_import_persisted_path", "string"),
|
||||
persistedSize: call("fc_web_import_persisted_size"),
|
||||
before,
|
||||
reopened: {
|
||||
objects: call("fc_web_import_reopened_object_count"),
|
||||
parts: call("fc_web_import_reopened_part_count"),
|
||||
solids: call("fc_web_import_reopened_solid_count"),
|
||||
volume: call("fc_web_import_reopened_volume", "number"),
|
||||
},
|
||||
activeHandles: call("fc_web_import_active_handles"),
|
||||
};
|
||||
|
||||
const valid = result.importHandle > 0
|
||||
&& result.saveReopen === 1
|
||||
&& result.error.code === ""
|
||||
&& result.error.message === ""
|
||||
&& result.persistedPath === "/tmp/ImportCreateDocument.FCStd"
|
||||
&& result.persistedSize > 22
|
||||
&& result.before.objects > 0
|
||||
&& result.before.parts > 0
|
||||
&& result.before.solids > 0
|
||||
&& result.before.volume > 0
|
||||
&& result.reopened.objects === result.before.objects
|
||||
&& result.reopened.parts === result.before.parts
|
||||
&& result.reopened.solids === result.before.solids
|
||||
&& Math.abs(result.reopened.volume - result.before.volume) < 1e-6
|
||||
&& result.activeHandles === 0;
|
||||
|
||||
if (!valid) {
|
||||
throw new Error(JSON.stringify(result));
|
||||
}
|
||||
console.log(JSON.stringify(result));
|
||||
91
src/WebAssembly/FreeCADCoreProbe/inspection-runtime-smoke.js
Normal file
91
src/WebAssembly/FreeCADCoreProbe/inspection-runtime-smoke.js
Normal file
@@ -0,0 +1,91 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { pathToFileURL } = require("url");
|
||||
|
||||
async function main()
|
||||
{
|
||||
const buildDirectory = path.resolve(process.argv[2] || "build-web/configure/bin");
|
||||
globalThis.__dirname = `${buildDirectory}${path.sep}`;
|
||||
globalThis.require = require;
|
||||
const module = await import(pathToFileURL(path.join(buildDirectory, "FreeCADWebInspectionRuntimeProbe.js")));
|
||||
const probe = await module.default({
|
||||
wasmBinary: fs.readFileSync(path.join(buildDirectory, "FreeCADWebInspectionRuntimeProbe.wasm")),
|
||||
});
|
||||
const call = (name, returnType = "number", argTypes = [], args = []) =>
|
||||
probe.ccall(name, returnType, argTypes, args);
|
||||
const error = () => ({
|
||||
code: call("fc_web_inspection_error_code", "string"),
|
||||
message: call("fc_web_inspection_error_message", "string"),
|
||||
});
|
||||
const state = () => ({
|
||||
calls: call("fc_web_inspection_initialization_calls"),
|
||||
runs: call("fc_web_inspection_initialization_runs"),
|
||||
initialized: call("fc_web_inspection_initialized"),
|
||||
});
|
||||
|
||||
const result = {};
|
||||
result.initialize = call("fc_web_inspection_initialize", "number", ["string"], ["Inspection"]);
|
||||
result.firstState = state();
|
||||
result.initializeAgain = call("fc_web_inspection_initialize", "number", ["string"], ["Inspection"]);
|
||||
result.repeatState = state();
|
||||
result.types = [
|
||||
"Inspection::PropertyDistanceList",
|
||||
"Inspection::Feature",
|
||||
"Inspection::Group",
|
||||
"Mesh::Feature",
|
||||
"Points::Feature",
|
||||
"Part::Feature",
|
||||
].map((name) => ({
|
||||
name,
|
||||
visible: call("fc_web_inspection_type_probe", "number", ["string"], [name]),
|
||||
registeredName: call("fc_web_inspection_last_type", "string"),
|
||||
}));
|
||||
result.missingType = call("fc_web_inspection_type_probe", "number", ["string"], ["Inspection::Missing"]);
|
||||
result.missingTypeError = error();
|
||||
result.missingModule = call("fc_web_inspection_initialize", "number", ["string"], ["Missing"]);
|
||||
result.missingModuleError = error();
|
||||
result.guiModule = call("fc_web_inspection_initialize", "number", ["string"], ["InspectionGui"]);
|
||||
result.guiModuleError = error();
|
||||
result.action = call("fc_web_inspection_create_recompute");
|
||||
result.actionError = error();
|
||||
result.actionSummary = {
|
||||
distanceCount: call("fc_web_inspection_distance_count"),
|
||||
rms: call("fc_web_inspection_distance_rms", "number"),
|
||||
documentObjects: call("fc_web_inspection_document_objects"),
|
||||
};
|
||||
result.invalidAction = call("fc_web_inspection_invalid_recompute");
|
||||
result.invalidError = error();
|
||||
result.recoveryAction = call("fc_web_inspection_create_recompute");
|
||||
result.recoveryError = error();
|
||||
|
||||
const valid = result.initialize === 1
|
||||
&& result.initializeAgain === 1
|
||||
&& result.firstState.runs === 1
|
||||
&& result.repeatState.runs === 1
|
||||
&& result.repeatState.initialized === 1
|
||||
&& result.types.every((entry) => entry.visible === 1 && entry.registeredName === entry.name)
|
||||
&& result.missingType === 0
|
||||
&& result.missingTypeError.code === "type_not_found"
|
||||
&& result.missingModule === 0
|
||||
&& result.missingModuleError.code === "module_unavailable"
|
||||
&& result.guiModule === 0
|
||||
&& result.guiModuleError.code === "gui_dependency_unavailable";
|
||||
const actionValid = result.action === 1
|
||||
&& result.actionError.code === ""
|
||||
&& result.actionSummary.distanceCount > 0
|
||||
&& result.actionSummary.documentObjects === 3
|
||||
&& result.actionSummary.rms >= 0;
|
||||
const recoveryValid = result.invalidAction === 1
|
||||
&& result.invalidError.code === "invalid_fixture"
|
||||
&& result.recoveryAction === 1
|
||||
&& result.recoveryError.code === "";
|
||||
if (!valid || !actionValid || !recoveryValid) throw new Error(JSON.stringify(result));
|
||||
console.log(JSON.stringify(result));
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
Reference in New Issue
Block a user