MineTrustConnectorSDK.CPP
NativeApp.cpp
// NativeApp.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include "../../CppCliInterop/MTConnectorSDK.h"
#include <magic_enum.hpp>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <process.h>
#include <random>
#include <thread>
using namespace std::chrono_literals;
namespace fs = std::filesystem;
template <>
struct magic_enum::customize::enum_range<MTConnectorSDK::LogEvent> {
static constexpr int min = 1000;
static constexpr int max = 5000;
};
// https://stackoverflow.com/a/24586587/2911871
static std::string random_string(std::string::size_type length)
{
static auto& chrs = "0123456789"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
thread_local static std::mt19937 rg{ std::random_device{}() };
thread_local static std::uniform_int_distribution<std::string::size_type> pick(0, sizeof(chrs) - 2);
std::string s;
s.reserve(length);
while (length--)
s += chrs[pick(rg)];
return s;
}
namespace
{
constexpr bool showUI = false;
void DoSync(const MTConnectorSDK::PackageConfiguration& newConfiguration)
{
std::cout << "Package processing in main thread" << std::endl;
MTConnectorSDK::LockPackage(newConfiguration.packageUID, _getpid(), showUI);
MTConnectorSDK::SyncNow(newConfiguration.packageUID, showUI);
MTConnectorSDK::UnlockPackage(newConfiguration.packageUID, true, showUI);
std::cout << "Finished processing in main thread" << std::endl;
}
void DoAsync(const MTConnectorSDK::PackageConfiguration& newConfiguration)
{
// Example of some asynchronous background processing on a package
// Cancellation is done programmatically via the cancellation token source
concurrency::cancellation_token_source cts;
auto token = cts.get_token();
auto t = MTConnectorSDK::SyncNowTask(newConfiguration.packageUID, showUI, token)
.then([&newConfiguration, token](MTConnectorSDK::SyncNowResultCodes syncReturnCode)
{
return MTConnectorSDK::LockPackageTask(newConfiguration.packageUID, _getpid(), showUI, token);
}, token)
.then([&newConfiguration, token, &cts](MTConnectorSDK::LockPackageResultCodes lockReturnCode)
{
cts.cancel();
return MTConnectorSDK::SyncNowTask(newConfiguration.packageUID, showUI, token);
}, token)
.then([&newConfiguration, token](MTConnectorSDK::SyncNowResultCodes syncReturnCode)
{
return MTConnectorSDK::UnlockPackageTask(newConfiguration.packageUID, false, showUI, token);
}, token);
std::cout << "Package processing in background" << std::endl;
const concurrency::task_status taskStatus = t.wait();
std::cout << "Finished processing in background being canceled? " <<
std::boolalpha << (concurrency::task_status::canceled == taskStatus) << std::endl;
// Ensure package unlocked - since we cancelled the async task
MTConnectorSDK::UnlockPackage(newConfiguration.packageUID, false, showUI);
}
void DoAsyncLeavingScope(const MTConnectorSDK::PackageConfiguration& newConfiguration)
{
// Testing how asynchronous background behaves regarding scope leaving
// mainly proving that task destruction does not wait for the process to be done
// (which is one of the drawbacks in std::future/std::async)
bool done = false;
{
std::cout << "Preparing multiple Sync processing in background" << std::endl;
auto t = MTConnectorSDK::SyncNowTask(newConfiguration.packageUID, showUI)
.then([&done](MTConnectorSDK::SyncNowResultCodes syncReturnCode)
{
std::cout << "Done processing" << std::endl;
done = true;
});
std::cout << "Leave task scope" << std::endl;
}
std::cout << "Out of scope processing in background" << std::endl;
while (!done)
{
std::cout << "Still waiting for task to be done" << std::endl;
std::this_thread::sleep_for(1s);
}
std::cout << "Done waiting" << std::endl;
}
void SubscribeToEvents()
{
std::list<MTConnectorSDK::LogEvent> eventIds;
// Register for MineTrust Connector events, logging the events which we receive
auto registration = MTConnectorSDK::RegisterEvents([](const MTConnectorSDK::MTConnectorEvent& connectorEvent)
{
std::cout << connectorEvent.time
<< ": Event (" << magic_enum::enum_name(connectorEvent.eventId) << ") received: "
<< connectorEvent.message << std::endl;
}, eventIds);
// Optionally increase this timeout to see more events happening
std::this_thread::sleep_for(10s);
// When 'registration' goes out of scope, the destructor is called, which clears up the underlying
// event registration (can attach Mixed mode debugger to see this call all the way through to Dispose
// on the managed counterpart)
}
// Adds a unique 0-bytes file with the specified baseName prefix to the destination directory and
// returns the name of that file on disk
auto AddRandomFile(const std::string& baseName, const fs::path& destinationDir)
{
// Build a unique (-ish) file name by appending a pseudo-random suffix
auto rnd_suffix = random_string(6);
auto localFileName = baseName + "_" + rnd_suffix + ".txt";
auto localFilePath = destinationDir / localFileName;
// Create the file
std::ofstream outfile(localFilePath);
outfile.close();
// Return the file path
return localFilePath.string();
}
// Sets up a locally-enabled package configuration based on the specified name and server URL, and initialises it
// with some 'test' contents (just empty files)
auto SetUpTestPackage(const std::string& baseName, const std::string& serverUrl, int numberTestFiles = 10)
{
// Build a unique (-ish) display name by appending a pseudo-random suffix
auto rnd_suffix = random_string(6);
auto displayName = baseName + "_" + rnd_suffix;
// Construct the package configuration
packageConfiguration.displayName = displayName;
packageConfiguration.enabled = true;
packageConfiguration.localRoot = R"(C:\temp\test\)" + rnd_suffix;
packageConfiguration.serverUrl = serverUrl;
// Load the package that we created (so we have its packageUID)
packageConfiguration = MTConnectorSDK::FindPackageConfigurations("$.DisplayName", displayName)[0];
// Sync the package (ensures local root, so do this before creating any dummy data)
MTConnectorSDK::SyncNow(packageConfiguration.packageUID);
// Create some 'test' contents in the package
for (int i = 0; i < numberTestFiles; ++i)
{
AddRandomFile(std::format("dummy {} ({})", i, rnd_suffix), packageConfiguration.localRoot);
}
if (numberTestFiles > 0)
{
// Sync the package again with the test files
MTConnectorSDK::SyncNow(packageConfiguration.packageUID);
}
// Return the package configuration
return packageConfiguration;
}
}
int main()
{
try
{
// Display installation status and service state
std::cout << "Connector is installed: " << std::boolalpha << MTConnectorSDK::IsMineTrustConnectorInstalled() << std::endl;
std::cout << "Connector is running: " << std::boolalpha << MTConnectorSDK::IsMineTrustConnectorRunning() << std::endl;
// ServerRegistration replaces the "package of all packages" server-configuration model as the identity
// anchor for a registered MineTrust server. Unlike PackageConfiguration, it carries no Include/Exclude/
// LocalRoot - it is an identity anchor only (ServerUrl, ServiceUID, RegisteredAt, optional DisplayName).
auto serverRegistrations = MTConnectorSDK::ListServerRegistrations();
if (serverRegistrations.empty())
{
throw std::runtime_error("No server registrations found - register a MineTrust server first");
}
const MTConnectorSDK::ServerRegistration& serverRegistration = serverRegistrations[0];
std::cout << "Server registration: " << serverRegistration.serverUrl
<< " (ServiceUID: " << serverRegistration.serviceUID << ")" << std::endl;
// GetServerRegistrationForServerURL / GetServerRegistrationForServiceUID are the single-instance
// counterparts of ListServerRegistrations(). Both follow the bool-return / out-parameter convention
// already established by LoadPackageConfiguration, rather than returning a nullable value
MTConnectorSDK::ServerRegistration lookedUpRegistration;
if (MTConnectorSDK::GetServerRegistrationForServerURL(serverRegistration.serverUrl, lookedUpRegistration))
{
std::cout << "Looked up registration by URL, ServiceUID: " << lookedUpRegistration.serviceUID << std::endl;
}
// GetAvailablePackages replaces enumerating "packages on disk" for discovery purposes. It is populated
// entirely from the local SQLite tag cache - without a network round-trip, and without requiring every
// package in the tenant to have first been synced to disk - and includes packages which are not (yet)
// locally enabled on this client (see PackageSummary::locallyEnabled)
for (const MTConnectorSDK::PackageSummary& packageSummary : MTConnectorSDK::GetAvailablePackages(serverRegistration))
{
std::cout << "Available package: " << packageSummary.packageUID
<< " (" << packageSummary.displayName << ")"
<< " - locally enabled: " << std::boolalpha << packageSummary.locallyEnabled << std::endl;
}
// Query local packages that are Enabled
for (const MTConnectorSDK::PackageConfiguration& packageConfiguration : MTConnectorSDK::FindPackageConfigurations("$.Enabled", "True"))
{
std::cout << "Package is enabled: " << packageConfiguration.packageUID << std::endl;
}
// Look up available tags using the local tag cache
for (const MTConnectorSDK::LocalTagRecord& tagRecord : MTConnectorSDK::FindTags("Map*", "", "", true))
{
std::cout << "Tag name: " << tagRecord.name << " / Tag value: " << tagRecord.value << std::endl;
// Example of how to locate corresponding records via tag manager
for (const MTConnectorSDK::LocalFileRecord& fileRecord : MTConnectorSDK::FindFiles(tagRecord.name, tagRecord.value))
{
std::cout << "File record file found: " << fileRecord.name << std::endl;
}
}
// Look up tagged files using the local tag cache
for (const MTConnectorSDK::LocalFileRecord& fileRecord : MTConnectorSDK::FindFiles("MapType", "LevelMap"))
{
std::cout << "Level map file found: " << fileRecord.name << std::endl;
}
// Look up tagged files using the local tag cache - specifically filtering on .ar this time
for (const MTConnectorSDK::LocalFileRecord& fileRecord : MTConnectorSDK::FindFiles("MapType", "LevelMap", "*.ar", ""))
{
std::cout << "AR Level map file found: " << fileRecord.name << std::endl;
}
// Compare against the results of EvaluateFilter
for (const MTConnectorSDK::LocalFileRecord& filterResult : MTConnectorSDK::EvaluateFilter("MapType = LevelMap"))
{
std::cout << "Located level map file via filter evaluation: " << filterResult.name << std::endl;
}
// Show all packages which are accessible
for (const std::string& packageUID : MTConnectorSDK::GetAccessiblePackages())
{
std::cout << "Package is accessible: " << packageUID << std::endl;
}
// Show all packages which are inaccessible
for (const std::string& packageUID : MTConnectorSDK::GetInaccessiblePackages())
{
std::cout << "Package is inaccessible: " << packageUID << std::endl;
}
// Show all packages which have been deleted
for (const std::string& packageUID : MTConnectorSDK::GetDeletedPackages())
{
std::cout << "Package has been deleted: " << packageUID << std::endl;
}
// Show all packages which have been archived
for (const std::string& packageUID : MTConnectorSDK::GetArchivedPackages())
{
std::cout << "Package has been archived: " << packageUID << std::endl;
}
// Set up a local test package
auto testPackage1 = SetUpTestPackage("Cpp/CLI Package Test 1", serverRegistration.serverUrl);
// Examples of package sync using synchronous and asynchronous operations
DoSync(testPackage1);
DoAsync(testPackage1);
DoAsyncLeavingScope(testPackage1);
SubscribeToEvents();
// Enumerate local contents of the test package configuration
for (const std::string& localFile : MTConnectorSDK::EnumerateLocalContents(testPackage1.packageUID))
{
std::cout << "Test package has local file: " << localFile << std::endl;
}
// Print some package metrics for the test package
auto packageMetrics = MTConnectorSDK::LoadPackageMetrics(testPackage1.packageUID, serverRegistration.serviceUID);
std::cout << "Test package total files: " << packageMetrics.totalFiles << std::endl;
std::cout << "Test package local files: " << packageMetrics.localFiles << std::endl;
std::cout << "Test package percentage downloaded: " << packageMetrics.percentageDownloaded << std::endl;
auto testEmptyPackage = [&testPackage1](auto&& emptyThePackageFn)
{
std::cout << "Has " << MTConnectorSDK::EnumerateLocalContents(testPackage1.packageUID).size() << " files in scope" << std::endl;
auto packageCopy = testPackage1;
emptyThePackageFn(packageCopy);
MTConnectorSDK::SyncNow(testPackage1.packageUID);
std::cout << "After being emptied, has " << MTConnectorSDK::EnumerateLocalContents(testPackage1.packageUID).size() << " files in scope" << std::endl;
// Check package can be reloaded without problem afterwards
MTConnectorSDK::LoadPackageConfiguration(testPackage1.packageUID, reloaded);
// restore initial package state
MTConnectorSDK::SyncNow(testPackage1.packageUID);
};
testEmptyPackage([](MTConnectorSDK::PackageConfiguration& packageConfig)
{
std::cout << "Emptying the package (add empty include) " << std::endl;
packageConfig.include.push_back({});
});
testEmptyPackage([](MTConnectorSDK::PackageConfiguration& packageConfig)
{
std::cout << "Emptying the package (exclude everything) " << std::endl;
packageConfig.exclude.push_back("*");
});
// Create a file in the package and tag it as 'published'
auto testFile1a = AddRandomFile("testFile1a", testPackage1.localRoot);
MTConnectorSDK::SyncNow(testPackage1.packageUID);
MTConnectorSDK::TagFile(testPackage1, testFile1a, "Status", "Published");
// Save (and sync) the package configuration
MTConnectorSDK::SyncNow(testPackage1.packageUID, showUI);
// Now enumerate local files by tag (we should see results include the file we just created)
for (const std::string& localFile : MTConnectorSDK::EnumerateLocalFilesByTag(testPackage1.packageUID, "Status", "Published"))
{
std::cout << "Located the following file by tag in the test package: " << localFile << std::endl;
}
// A more sophisticated example of the above is to 'IncludeInPackage' and pass an inclusion configuration
// delegate
auto testFile1b = AddRandomFile("testFile1b", testPackage1.localRoot);
MTConnectorSDK::SyncNow(testPackage1.packageUID);
{
include.tags.insert(std::make_pair("Status", "Published"));
});
// Save (and sync) the package configuration
MTConnectorSDK::SyncNow(testPackage1.packageUID, showUI);
// Now enumerate local files by tag (we should see both our test files returned)
for (const std::string& localFile : MTConnectorSDK::EnumerateLocalFilesByTag(testPackage1.packageUID, "Status", "Published"))
{
std::cout << "Located the following file by tag in the test package: " << localFile << std::endl;
}
// Now disable the first package and set up a second one (also disable the local root so
// MT Connector has no clue about the original provenance of the files)
testPackage1.enabled = false;
testPackage1.localRoot = std::string();
// Set up the second package, and create some files
auto testPackage2 = SetUpTestPackage("Cpp/CLI Package Test 2", serverRegistration.serverUrl);
auto testFile2a = AddRandomFile("testFile2a", testPackage2.localRoot);
auto testFile2b = AddRandomFile("testFile2b", testPackage2.localRoot);
// Sync the package
MTConnectorSDK::SyncNow(testPackage2.packageUID);
// Set up the second round of test files with 'Status' = 'Draft' instead
MTConnectorSDK::TagFile(testPackage2, testFile2a, "Status", "Draft");
MTConnectorSDK::TagFile(testPackage2, testFile2b, "Status", "Draft");
// Attach another tag to the second file (to distinguish it later on)
MTConnectorSDK::TagFile(testPackage2, testFile2b, "LocalOnly", "True");
// Attach another tag that is transient in nature
MTConnectorSDK::TagFile(testPackage2, testFile2a, "Hidden", "True");
// Save (and sync) the package configuration
MTConnectorSDK::SyncNow(testPackage2.packageUID, showUI);
// Remove the transient tag from the file
MTConnectorSDK::RemoveTag(testPackage2, testFile2a, "Hidden");
// Save (and sync) the package configuration
MTConnectorSDK::SyncNow(testPackage2.packageUID, showUI);
// Now re-locate the 'Status' = 'Published' files which we set up from the first package
auto publishedFiles = MTConnectorSDK::FindFiles("Status", "Published");
// Call the 'ReceiveFiles' API to import the located files into this package
MTConnectorSDK::ReceiveFiles(publishedFiles, [&](const std::vector<MTConnectorSDK::LocalFileRecord>& receivedFiles)
{
for (const MTConnectorSDK::LocalFileRecord& receivedFile : receivedFiles)
{
std::cout << "Received file: " << receivedFile.localFilePath << std::endl;
const fs::path tempDir(testPackage2.localRoot);
if (!fs::exists(tempDir / receivedFile.name))
{
fs::copy_file(receivedFile.localFilePath, tempDir / receivedFile.name);
}
}
}).wait();
// We can use our local 'Status' = 'Draft' files to illustrate sending files back to the original package
auto draftFiles = MTConnectorSDK::FindFiles("Status", "Draft");
// Can also call the 'SendFiles' API to send the files somewhere else
MTConnectorSDK::SendFiles(draftFiles, [&](const MTConnectorSDK::LocalFileRecord& localFileRecord)
{
// Lambda just has to select a destination package
for (const MTConnectorSDK::LocalTagRecord& tagRecord : localFileRecord.tags)
{
if (tagRecord.name == "LocalOnly" && tagRecord.value == "True")
{
// To skip a file from being sent - can just return a 'default' package configuration instance
}
}
// To send a file to a particular package, just return that package instance from the lambda
std::cout << "Sending file to package: " << localFileRecord.localFilePath << std::endl;
return testPackage1;
}).wait();
// Consume the configuration schema for a Product
for (const std::string& permittedValue : MTConnectorSDK::GetPermittedValues("Studio Mapper/Mine"))
{
std::cout << "Permitted value: " << permittedValue << std::endl;
}
}
catch (const std::runtime_error& e)
{
// Exceptions from .NET are wrapped as std::runtime_error and raised into the CLR
std::cout << "Sample app encountered an error: " << e.what() << std::endl;
}
return 0;
}
MT_CONNECTOR_EXPORT std::vector< ServerRegistration > ListServerRegistrations()
MT_CONNECTOR_EXPORT std::vector< PackageSummary > GetAvailablePackages(const ServerRegistration &registration)
MT_CONNECTOR_EXPORT std::vector< std::string > GetArchivedPackages(const std::string &serviceUID="")
MT_CONNECTOR_EXPORT bool IsMineTrustConnectorRunning()
LockPackageResultCodes
Definition MTConnectorSDK.h:816
MT_CONNECTOR_EXPORT RegistrationWrapper RegisterEvents(std::function< void(const MTConnectorEvent &)> callback, const std::list< LogEvent > &eventIDs={})
MT_CONNECTOR_EXPORT std::vector< std::string > EnumerateLocalFilesByTag(const std::string &packageUID, const std::string &tagName, const std::string &tagValue)
MT_CONNECTOR_EXPORT PackageMetrics LoadPackageMetrics(const std::string &packageUID, const std::string &serviceUID="")
MT_CONNECTOR_EXPORT void TagFile(PackageConfiguration &instance, const std::string &localFilePath, const std::string &tagName, const std::string &tagValue)
MT_CONNECTOR_EXPORT std::vector< std::string > GetDeletedPackages(const std::string &serviceUID="")
MT_CONNECTOR_EXPORT concurrency::task< LockPackageResultCodes > LockPackageTask(const std::string &packageUID, const int &ownerProcess=0, bool showUI=false, concurrency::cancellation_token cancel=concurrency::cancellation_token::none())
MT_CONNECTOR_EXPORT void IncludeInPackage(PackageConfiguration &instance, const std::string &localFilePath, std::function< void(IncludeConfiguration &)> configureInclusion)
MT_CONNECTOR_EXPORT SyncNowResultCodes SyncNow(const std::string &packageUID, bool showUI=false)
SyncNowResultCodes
Definition MTConnectorSDK.h:908
MT_CONNECTOR_EXPORT std::vector< std::string > GetInaccessiblePackages(const std::string &serviceUID="")
MT_CONNECTOR_EXPORT std::vector< std::string > EnumerateLocalContents(const std::string &packageUID)
MT_CONNECTOR_EXPORT bool LoadPackageConfiguration(const std::string &packageUID, PackageConfiguration &instance)
MT_CONNECTOR_EXPORT concurrency::task< void > ReceiveFiles(const std::vector< LocalFileRecord > &selection, std::function< void(const std::vector< LocalFileRecord > &)> filesReceivedCallback, concurrency::cancellation_token cancel=concurrency::cancellation_token::none())
MT_CONNECTOR_EXPORT std::vector< std::string > GetPermittedValues(const std::string &configPath)
MT_CONNECTOR_EXPORT concurrency::task< UnlockPackageResultCodes > UnlockPackageTask(const std::string &packageUID, bool discardChanges=false, bool showUI=false, concurrency::cancellation_token cancel=concurrency::cancellation_token::none())
MT_CONNECTOR_EXPORT concurrency::task< void > SendFiles(const std::vector< LocalFileRecord > &selection, std::function< PackageConfiguration(const LocalFileRecord &)> packageSelectionCallback, concurrency::cancellation_token cancel=concurrency::cancellation_token::none())
MT_CONNECTOR_EXPORT bool IsMineTrustConnectorInstalled()
MT_CONNECTOR_EXPORT std::vector< LocalTagRecord > FindTags(const std::string &tagNameFilter="", const std::string &tagValueFilter="", const std::string &serviceUID="", bool fuzzyMatch=false)
MT_CONNECTOR_EXPORT concurrency::task< SyncNowResultCodes > SyncNowTask(const std::string &packageUID, bool showUI=false, concurrency::cancellation_token cancel=concurrency::cancellation_token::none())
MT_CONNECTOR_EXPORT UnlockPackageResultCodes UnlockPackage(const std::string &packageUID, bool discardChanges=false, bool showUI=false)
MT_CONNECTOR_EXPORT std::vector< LocalFileRecord > EvaluateFilter(const std::string &filter, const std::string &packageUID="", const std::string &serviceUID="")
MT_CONNECTOR_EXPORT LockPackageResultCodes LockPackage(const std::string &packageUID, const int &ownerProcess=0, bool showUI=false)
MT_CONNECTOR_EXPORT std::vector< LocalFileRecord > FindFiles(const std::string &tagName, const std::string &tagValue, const std::string &serviceUID="", bool fuzzyMatch=false)
MT_CONNECTOR_EXPORT bool SavePackageConfiguration(const PackageConfiguration &instance)
MT_CONNECTOR_EXPORT bool GetServerRegistrationForServerURL(const std::string &serverUrl, ServerRegistration &instance)
MT_CONNECTOR_EXPORT void RemoveTag(PackageConfiguration &instance, const std::string &localFilePath, const std::string &tagName)
@ AllFilesUpToDate
Definition MTConnectorSDK.h:37
@ SyncCompleted
Definition MTConnectorSDK.h:45
@ GeneralSyncError
Definition MTConnectorSDK.h:207
@ SyncStarted
Definition MTConnectorSDK.h:41
MT_CONNECTOR_EXPORT std::vector< std::string > GetAccessiblePackages(const std::string &serviceUID="")
MT_CONNECTOR_EXPORT std::vector< PackageConfiguration > FindPackageConfigurations(const std::string &jsonPath, const std::string &testValue, bool negate=false)
Definition MTConnectorSDK.h:303
std::unordered_map< std::string, std::string > tags
Definition MTConnectorSDK.h:319
Definition MTConnectorSDK.h:519
std::vector< LocalTagRecord > tags
Definition MTConnectorSDK.h:531
std::string localFilePath
Definition MTConnectorSDK.h:539
Definition MTConnectorSDK.h:494
std::string name
Definition MTConnectorSDK.h:498
std::string value
Definition MTConnectorSDK.h:502
Definition MTConnectorSDK.h:550
LogEvent eventId
Definition MTConnectorSDK.h:555
std::string message
Definition MTConnectorSDK.h:563
std::chrono::system_clock::time_point time
Definition MTConnectorSDK.h:559
Definition MTConnectorSDK.h:332
std::string displayName
Definition MTConnectorSDK.h:353
std::string serverUrl
Definition MTConnectorSDK.h:361
std::vector< IncludeConfiguration > include
Definition MTConnectorSDK.h:380
static MT_CONNECTOR_EXPORT const std::string SHARED_PACKAGE_VISIBILITY
Definition MTConnectorSDK.h:336
std::string localRoot
Definition MTConnectorSDK.h:365
bool enabled
Definition MTConnectorSDK.h:349
std::vector< std::string > exclude
Definition MTConnectorSDK.h:385
std::string visibility
Definition MTConnectorSDK.h:404
std::string packageUID
Definition MTConnectorSDK.h:357
Definition MTConnectorSDK.h:629
Definition MTConnectorSDK.h:596
std::string serviceUID
Definition MTConnectorSDK.h:604
std::string serverUrl
Definition MTConnectorSDK.h:600