diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e16368..453dd4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +# Version 2.2.0 + +This is a major change to the underlying code to remove the deprecated `pdal::PipelineExecutor` and replace it with `pdal::PipelineManager`. + +This version also introduces the following non-breaking changes to the ABI: + +- Addition of the following method to allow the consuming app to tell if a pipeline is streamable: + +``` +bool PDALPipelineIsStreamable(PDALPipelinePtr pipeline) +``` + +- Addition of the following method to allow the consuming application to run a pipeline in streaming mode. If the pipeline is non-streamable it will be silently run in standard mode: + +``` +bool PDALExecutePipelineAsStream(PDALPipelinePtr pipeline) +``` + + # Version 2.1.1 Changes to allow compilation with PDAL 2.4.0 diff --git a/source/pdal/pdalc_config.cpp b/source/pdal/pdalc_config.cpp index 81514ba..e8b61d1 100644 --- a/source/pdal/pdalc_config.cpp +++ b/source/pdal/pdalc_config.cpp @@ -27,6 +27,7 @@ * POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +#define _CRT_SECURE_NO_WARNINGS #include "pdalc_config.h" #include @@ -36,6 +37,8 @@ #include #include + + namespace pdal { namespace capi diff --git a/source/pdal/pdalc_config.h b/source/pdal/pdalc_config.h index 8bb503f..7ecd3cd 100644 --- a/source/pdal/pdalc_config.h +++ b/source/pdal/pdalc_config.h @@ -32,6 +32,7 @@ #include "pdalc_forward.h" + /** * @file pdalc_config.h * Functions to retrieve PDAL version and configuration information. diff --git a/source/pdal/pdalc_dimtype.cpp b/source/pdal/pdalc_dimtype.cpp index b1d782d..31eb58f 100644 --- a/source/pdal/pdalc_dimtype.cpp +++ b/source/pdal/pdalc_dimtype.cpp @@ -26,11 +26,13 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ - +#define _CRT_SECURE_NO_WARNINGS #include "pdal/pdalc_dimtype.h" #include + + namespace pdal { namespace capi diff --git a/source/pdal/pdalc_forward.h b/source/pdal/pdalc_forward.h index 0c68b16..766b935 100644 --- a/source/pdal/pdalc_forward.h +++ b/source/pdal/pdalc_forward.h @@ -53,7 +53,7 @@ namespace pdal { struct DimType; -class PipelineExecutor; +class PipelineManager; class PointView; class TriangularMesh; @@ -64,10 +64,10 @@ using MeshPtr = std::shared_ptr; namespace capi { class PointViewIterator; -using Pipeline = std::unique_ptr; using PointView = pdal::PointViewPtr; using TriangularMesh = pdal::MeshPtr; using DimTypeList = std::unique_ptr; + } } diff --git a/source/pdal/pdalc_pipeline.cpp b/source/pdal/pdalc_pipeline.cpp index fd7dfe8..ce9d966 100644 --- a/source/pdal/pdalc_pipeline.cpp +++ b/source/pdal/pdalc_pipeline.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright (c) 2019, Simverge Software LLC. All rights reserved. + * Copyright (c) 2019, Simverge Software LLC & Runette Software Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following @@ -26,70 +26,71 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ - +#define _CRT_SECURE_NO_WARNINGS #include "pdalc_pipeline.h" #include -#include - #include "pdalc_pointviewiterator.h" -// TODO Address cause of std::min problems. See https://github.com/PDAL/CAPI/issues/4 +#include +#include +#include +#include + #undef min namespace pdal { namespace capi { + extern "C" { - PDALPipelinePtr PDALCreatePipeline(const char* json) + struct Pipeline { - PDALPipelinePtr pipeline = nullptr; + public: + + PipelineManagerPtr manager = std::make_unique(); + + bool m_executed = false; + std::stringstream logStream; + }; + PDALPipelinePtr PDALCreatePipeline(const char* json) + { + std::unique_ptr pipeline = std::make_unique(); if (json && std::strlen(json) > 0) { - pdal::PipelineExecutor *executor = nullptr; - try { - pdal::PipelineExecutor stackpipe(json); - executor = new pdal::PipelineExecutor(json); + std::stringstream* s = &pipeline->logStream; + LogPtr lptr(pdal::Log::makeLog("pdal capi", s, true)); + pipeline->manager->setLog(lptr); + + std::stringstream strm; + strm << json; + pipeline->manager->readPipeline(strm); } catch (const std::exception &e) { printf("Could not create pipeline: %s\n%s\n", e.what(), json); - executor = nullptr; + return nullptr; } - if (executor) + try { - bool valid = false; - - try - { - valid = executor->validate(); - } - catch (const std::exception &e) - { - printf("Error while validating pipeline: %s\n%s\n", e.what(), json); - } - - if (valid) - { - pipeline = new Pipeline(executor); - } - else - { - delete executor; - executor = NULL; - printf("The pipeline is invalid:\n%s\n", json); - } + pipeline->manager->prepare(); + } + catch (const std::exception &e) + { + printf("Error while validating pipeline: %s\n%s\n", e.what(), json); + return nullptr; } - } - return pipeline; + return pipeline.release(); + } + return nullptr; } void PDALDisposePipeline(PDALPipelinePtr pipeline) @@ -103,208 +104,231 @@ extern "C" size_t PDALGetPipelineAsString(PDALPipelinePtr pipeline, char *buffer, size_t size) { - size_t result = 0; - if (pipeline && buffer && size > 0) { Pipeline *ptr = reinterpret_cast(pipeline); - pdal::PipelineExecutor *executor = ptr->get(); - buffer[0] = '\0'; - buffer[size - 1] = '\0'; - if (executor) + try { - try - { - std::string s = executor->getPipeline(); - std::strncpy(buffer, s.c_str(), size - 1); - result = std::min(s.length(), size); - } - catch (const std::exception &e) - { - printf("Found error while retrieving pipeline's string representation: %s\n", e.what()); - } + if (! ptr->m_executed) + throw pdal_error("Pipeline has not been executed!"); + + std::stringstream strm; + pdal::PipelineWriter::writePipeline(ptr->manager->getStage(), strm); + std::string out = strm.str(); + if (out.length() > size - 1) out.resize(size - 1); + std::strncpy(buffer, out.c_str(), size); + return strlen(out.c_str()); + } + catch (const std::exception &e) + { + printf("Found error while retrieving pipeline's string representation: %s\n", e.what()); + return 0; } - } - - return result; + return 0; } size_t PDALGetPipelineMetadata(PDALPipelinePtr pipeline, char *metadata, size_t size) { - size_t result = 0; - if (pipeline && metadata && size > 0) { Pipeline *ptr = reinterpret_cast(pipeline); - pdal::PipelineExecutor *executor = ptr->get(); - metadata[0] = '\0'; - metadata[size - 1] = '\0'; - if (executor) + try + { + if (! ptr->m_executed) + throw pdal_error("Pipeline has not been executed!"); + + std::stringstream strm; + MetadataNode root = ptr->manager->getMetadata().clone("metadata"); + pdal::Utils::toJSON(root, strm); + std::string out = strm.str(); + if (out.length() > size - 1) out.resize(size - 1); + std::strncpy(metadata, out.c_str(), size); + return strlen(out.c_str()); + } + catch (const std::exception &e) { - try - { - std::string s = executor->getMetadata(); - std::strncpy(metadata, s.c_str(), size); - result = std::min(s.length(), size); - } - catch (const std::exception &e) - { - printf("Found error while retrieving pipeline's metadata: %s\n", e.what()); - } + printf("Found error while retrieving pipeline's metadata: %s\n", e.what()); + return 0; } } - - return result; + return 0; } size_t PDALGetPipelineSchema(PDALPipelinePtr pipeline, char *schema, size_t size) { - size_t result = 0; - if (pipeline && schema && size > 0) { Pipeline *ptr = reinterpret_cast(pipeline); - pdal::PipelineExecutor *executor = ptr->get(); - schema[0] = '\0'; - schema[size - 1] = '\0'; - if (executor) + try { - try - { - std::string s = executor->getSchema(); - std::strncpy(schema, s.c_str(), size); - result = std::min(s.length(), size); - } - catch (const std::exception &e) - { - printf("Found error while retrieving pipeline's schema: %s\n", e.what()); - } + if (! ptr->m_executed) + throw pdal_error("Pipeline has not been executed!"); + + std::stringstream strm; + MetadataNode meta = ptr->manager->pointTable().layout()->toMetadata(); + MetadataNode root = meta.clone("schema"); + pdal::Utils::toJSON(root, strm); + std::string out = strm.str(); + if (out.length() > size - 1) out.resize(size - 1); + std::strncpy(schema, out.c_str(), size); + return strlen(out.c_str()); + } + catch (const std::exception &e) + { + printf("Found error while retrieving pipeline's schema: %s\n", e.what()); + return 0; } - } - return result; + } + return 0; } size_t PDALGetPipelineLog(PDALPipelinePtr pipeline, char *log, size_t size) { - size_t result = 0; - if (pipeline && log && size > 0) { Pipeline *ptr = reinterpret_cast(pipeline); - pdal::PipelineExecutor *executor = ptr->get(); - log[0] = '\0'; - log[size - 1] = '\0'; - if (executor) + try { - try - { - std::string s = executor->getLog(); - std::strncpy(log, s.c_str(), size); - result = std::min(s.length(), size); - } - catch (const std::exception &e) - { - printf("Found error while retrieving pipeline's log: %s\n", e.what()); - } + ptr->logStream.get(log, size); + return std::min(static_cast(ptr->logStream.gcount()), size); + } + catch (const std::exception &e) + { + printf("Found error while retrieving pipeline's log: %s\n", e.what()); + return 0; } } - return result; + return 0; } void PDALSetPipelineLogLevel(PDALPipelinePtr pipeline, int level) { - Pipeline *ptr = reinterpret_cast(pipeline); + try + { + Pipeline *ptr = reinterpret_cast(pipeline); + if (level < 0 || level > 8) + throw pdal_error("log level must be between 0 and 8!"); - if (ptr && ptr->get()) + pdal::LogPtr lptr = ptr->manager->log(); + lptr->setLevel(static_cast(level)); + } + catch (const std::exception &e) { - try - { - ptr->get()->setLogLevel(level); - } - catch (const std::exception &e) - { - printf("Found error while setting log level: %s\n", e.what()); - } + printf("Found error while setting log level: %s\n", e.what()); } } int PDALGetPipelineLogLevel(PDALPipelinePtr pipeline) { - Pipeline *ptr = reinterpret_cast(pipeline); - return (ptr && ptr->get()) ? ptr->get()->getLogLevel() : 0; + if (! pipeline) + return 0; + + try + { + Pipeline *ptr = reinterpret_cast(pipeline); + return (ptr) + ? static_cast( + ptr->manager->log()->getLevel() + ) : 0; + } + catch (const std::exception &e) + { + printf("Found error while getting log level: %s\n", e.what()); + } + return 0; } int64_t PDALExecutePipeline(PDALPipelinePtr pipeline) { - int64_t result = 0; - Pipeline *ptr = reinterpret_cast(pipeline); + if (! pipeline) + return 0; - if (ptr && ptr->get()) + try { - try - { - result = ptr->get()->execute(); - } - catch (const std::exception &e) - { - printf("Found error while executing pipeline: %s", e.what()); - } + int64_t result; + Pipeline *ptr = reinterpret_cast(pipeline); + result = ptr->manager->execute(); + ptr->m_executed = true; + return result; } + catch (const std::exception &e) + { + printf("Found error while executing pipeline: %s", e.what()); + return 0; + } + } + + bool PDALExecutePipelineAsStream(PDALPipelinePtr pipeline) + { + if (! pipeline) + return false; - return result; + try + { + Pipeline *ptr = reinterpret_cast(pipeline); + PipelineManager::ExecResult exec = ptr->manager->execute(ExecMode::Stream); + ptr->m_executed = true; + return true; + } + catch (const std::exception &e) + { + printf("Found error while executing pipeline: %s", e.what()); + return false; + } } - bool PDALValidatePipeline(PDALPipelinePtr pipeline) + bool PDALPipelineIsStreamable(PDALPipelinePtr pipeline) { - int64_t result = 0; + if (! pipeline) + return false; + Pipeline *ptr = reinterpret_cast(pipeline); + return ptr->manager->pipelineStreamable(); + } + - if (ptr && ptr->get()) + + bool PDALValidatePipeline(PDALPipelinePtr pipeline) + { + if (! pipeline) + return false; + try { - try - { - result = ptr->get()->validate(); - } - catch (const std::exception &e) - { - printf("Found error while validating pipeline: %s", e.what()); - } + Pipeline *ptr = reinterpret_cast(pipeline); + ptr->manager->prepare(); + return true; + } + catch (const std::exception &e) + { + printf("Found error while validating pipeline: %s", e.what()); + return false; } - - return result; } PDALPointViewIteratorPtr PDALGetPointViews(PDALPipelinePtr pipeline) { - Pipeline *ptr = reinterpret_cast(pipeline); - pdal::capi::PointViewIterator *views = nullptr; + if (! pipeline) + return nullptr; - if (ptr && ptr->get()) + try { - try - { - auto &v = ptr->get()->getManagerConst().views(); - - if (!v.empty()) - { - views = new pdal::capi::PointViewIterator(v); - } - } - catch (const std::exception &e) - { - printf("Found error while retrieving point views: %s\n", e.what()); - } + Pipeline *ptr = reinterpret_cast(pipeline); + return new pdal::capi::PointViewIterator(ptr->manager->views()); + } + catch (const std::exception &e) + { + printf("Found error while retrieving point views: %s\n", e.what()); + return nullptr; } - - return views; } -} -} -} + +} /* extern c */ +} /* capi */ +} /* pdal */ diff --git a/source/pdal/pdalc_pipeline.h b/source/pdal/pdalc_pipeline.h index d7657c0..da76f70 100644 --- a/source/pdal/pdalc_pipeline.h +++ b/source/pdal/pdalc_pipeline.h @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright (c) 2019, Simverge Software LLC. All rights reserved. + * Copyright (c) 2019, Simverge Software LLC & Runette Software. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following @@ -32,6 +32,7 @@ #include "pdalc_forward.h" + /** * @file pdalc_pipeline.h * Functions to launch and inspect the results of a PDAL pipeline. @@ -45,6 +46,7 @@ namespace capi { extern "C" { + #else #include // for bool #include // for size_t @@ -134,6 +136,22 @@ PDALC_API int PDALGetPipelineLogLevel(PDALPipelinePtr pipeline); */ PDALC_API int64_t PDALExecutePipeline(PDALPipelinePtr pipeline); +/** + * Executes a pipeline as a streamable pipeline. Will run as non-streamed pipeline if the pipeline is not streamable. + * + * @param pipeline The pipeline + * @return true for success + */ +PDALC_API bool PDALExecutePipelineAsStream(PDALPipelinePtr pipeline); + +/** + * Determines if a pipeline is streamable + * + * @param pipeline The pipeline + * @return Whether the pipeline is streamable + */ +PDALC_API bool PDALPipelineIsStreamable(PDALPipelinePtr pipeline); + /** * Validates a pipeline. * @@ -156,8 +174,8 @@ PDALC_API PDALPointViewIteratorPtr PDALGetPointViews(PDALPipelinePtr pipeline); #ifdef __cplusplus -} -} -} +} /* extern C */ +} /* capi*/ +} /* pdal*/ #endif /* _cplusplus */ #endif /* PDALC_PIPELINE_H */ \ No newline at end of file diff --git a/source/pdal/pdalc_pointview.cpp b/source/pdal/pdalc_pointview.cpp index b83ae42..b06ea2e 100644 --- a/source/pdal/pdalc_pointview.cpp +++ b/source/pdal/pdalc_pointview.cpp @@ -26,7 +26,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ - +#define _CRT_SECURE_NO_WARNINGS #include "pdalc_pointview.h" #include diff --git a/tests/pdal/test_pdalc_config.c b/tests/pdal/test_pdalc_config.c index fe2cc2a..1f12898 100644 --- a/tests/pdal/test_pdalc_config.c +++ b/tests/pdal/test_pdalc_config.c @@ -114,12 +114,12 @@ TEST testPDALVersionInfo(void) int patch = PDALVersionPatch(); - char expected[64]; + char expected[128]; sprintf(expected, "%d.%d.%d", major, minor, patch); - char version[64]; - size_t size = PDALVersionString(version, 64); - ASSERT(size > 0 && size <= 64); + char version[128]; + size_t size = PDALVersionString(version, 128); + ASSERT(size > 0 && size <= 128); ASSERT(version[0]); ASSERT_STR_EQ(expected, version); diff --git a/tests/pdal/test_pdalc_pipeline.c.in b/tests/pdal/test_pdalc_pipeline.c.in index 4215daf..87576b8 100644 --- a/tests/pdal/test_pdalc_pipeline.c.in +++ b/tests/pdal/test_pdalc_pipeline.c.in @@ -146,6 +146,23 @@ TEST testPDALGetPipelineAsString(void) FAILm("PDALGetPipelineMetadata generated a JSON string whose object name is not \"pipeline\""); } + char json10[10]; + size = PDALGetPipelineAsString(pipeline, json10, 10); + + if (size != 9) + { + PDALDisposePipeline(pipeline); + pipeline = NULL; + FAILm("PDALGetPipelineAsString truncation not working"); + } + + if (json[0] == '\0') + { + PDALDisposePipeline(pipeline); + pipeline = NULL; + FAILm("PDALGetPipelineAsString generated a JSON string whose first character is null"); + } + PDALDisposePipeline(pipeline); PASS(); } @@ -200,6 +217,16 @@ TEST testPDALGetPipelineMetadata(void) FAILm("PDALGetPipelineMetadata generated a JSON string whose object name is not \"schema\""); } + char json10[10]; + size = PDALGetPipelineMetadata(pipeline, json10, 10); + + if (size != 9) + { + PDALDisposePipeline(pipeline); + pipeline = NULL; + FAILm("PDALGetPipelineMetadata truncation not working"); + } + PDALDisposePipeline(pipeline); PASS(); } @@ -254,6 +281,16 @@ TEST testPDALGetPipelineSchema(void) FAILm("PDALGetPipelineSchema generated a JSON string whose object name is not \"schema\""); } + char json10[10]; + size = PDALGetPipelineSchema(pipeline, json10, 10); + + if (size != 9) + { + PDALDisposePipeline(pipeline); + pipeline = NULL; + FAILm("PDALGetPipelineSchema truncation not working"); + } + PDALDisposePipeline(pipeline); PASS(); } @@ -344,6 +381,31 @@ TEST testPDALExecutePipeline(void) PASS(); } +TEST testPDALExecutePipelineStream(void) +{ + PDALPipelinePtr pipeline = PDALCreatePipeline(gPipelineJson); + ASSERT(pipeline); + + bool isStreamable = PDALPipelineIsStreamable(pipeline); + + ASSERT_EQ(isStreamable, true); + + bool result = PDALExecutePipelineAsStream(pipeline); + + ASSERT_EQ(result, true); + + result = PDALExecutePipelineAsStream(NULL); + + ASSERT_EQ(result, false); + + isStreamable = PDALPipelineIsStreamable(NULL); + + ASSERT_EQ(isStreamable, false); + + PDALDisposePipeline(pipeline); + PASS(); +} + TEST testPDALValidatePipeline(void) { bool valid = PDALValidatePipeline(NULL); @@ -369,6 +431,7 @@ GREATEST_SUITE(test_pdalc_pipeline) RUN_TEST(testPDALCreateAndDisposePipeline); RUN_TEST(testPDALExecutePipeline); + RUN_TEST(testPDALExecutePipelineStream); RUN_TEST(testPDALGetSetPipelineLog); RUN_TEST(testPDALGetPipelineAsString); RUN_TEST(testPDALGetPipelineMetadata); diff --git a/tests/pdal/test_pdalc_pointlayout.c.in b/tests/pdal/test_pdalc_pointlayout.c.in index 9585439..30093a6 100644 --- a/tests/pdal/test_pdalc_pointlayout.c.in +++ b/tests/pdal/test_pdalc_pointlayout.c.in @@ -129,14 +129,14 @@ TEST testPDALFindDimType(void) success &= (PDALGetDimTypeIdName(expected, name, 64) > 0); actual = PDALFindDimType(NULL, name); - success &= (idUnknown == actual.id); + success &= (idUnknown == actual.id); success &= (typeNone == actual.type); success &= (fabs(1.0 - actual.scale) < tolerance); success &= (fabs(actual.offset) < tolerance); actual = PDALFindDimType(gLayout, name); - success &= (expected.id == actual.id); + success &= (expected.id == actual.id); success &= (expected.type == actual.type); success &= (fabs(expected.scale - actual.scale) < tolerance); diff --git a/tests/pdal/test_pdalc_pointview.c.in b/tests/pdal/test_pdalc_pointview.c.in index 6e78813..26462ba 100644 --- a/tests/pdal/test_pdalc_pointview.c.in +++ b/tests/pdal/test_pdalc_pointview.c.in @@ -166,7 +166,7 @@ TEST testPDALGetPointViewSize(void) PDALDisposePointView(view); ASSERT(size > 0); - + PASS(); } @@ -189,7 +189,7 @@ TEST testPDALGetMeshSize(void) PDALDisposePointView(view); ASSERT(size == 2114); - + PASS(); } @@ -538,7 +538,7 @@ TEST testPDALGetPackedPoint(void) size_t expected = (hasView && hasDims && hasBuffer ? layoutPointSize : 0); size_t actual = PDALGetPackedPoint( - hasView ? view : NULL, hasDims ? dims : NULL, i, hasBuffer ? buffer : NULL); + hasView ? view : NULL, hasDims ? dims : NULL, i, hasBuffer ? buffer : NULL); if (expected != actual) {