diff --git a/core/tests/CMakeLists.txt b/core/tests/CMakeLists.txt index 459e9ce1..0c0f95d8 100644 --- a/core/tests/CMakeLists.txt +++ b/core/tests/CMakeLists.txt @@ -12,6 +12,7 @@ add_subdirectory(gwmessage_ut) add_subdirectory(message_q_ut) add_subdirectory(dynamic_loader_ut) add_subdirectory(module_loader_ut) + if(${enable_java_binding}) add_subdirectory(java_loader_ut) endif() @@ -34,5 +35,6 @@ endif() if(${run_e2e_tests}) add_subdirectory(gateway_e2e) + add_subdirectory(performance_e2e) endif() diff --git a/core/tests/performance_e2e/CMakeLists.txt b/core/tests/performance_e2e/CMakeLists.txt new file mode 100644 index 00000000..00cc9a35 --- /dev/null +++ b/core/tests/performance_e2e/CMakeLists.txt @@ -0,0 +1,91 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required(VERSION 2.8.12) + +include_directories(./inc) +include_directories(${GW_INC}) + +set(simulator_sources + ./src/simulator.cpp +) + +set(simulator_headers + ./inc/simulator.h +) + + +#this builds the simulator module +add_library(simulator MODULE ${simulator_sources} ${simulator_headers}) +target_link_libraries(simulator gateway) + +add_library(simulator_static STATIC ${simulator_sources} ${simulator_headers}) +target_compile_definitions(simulator_static PRIVATE BUILD_MODULE_TYPE_STATIC) +target_link_libraries(simulator_static gateway) + +linkSharedUtil(simulator) +linkSharedUtil(simulator_static) + +add_module_to_solution(simulator) + +if(install_modules) + install(TARGETS simulator LIBRARY DESTINATION "${LIB_INSTALL_DIR}/modules") +endif() + + +set(metrics_sources + ./src/metrics.cpp +) + +set(metrics_headers + ./inc/metrics.h +) + + +#this builds the metrics module +add_library(metrics MODULE ${metrics_sources} ${metrics_headers}) +target_link_libraries(metrics gateway) + +add_library(metrics_static STATIC ${metrics_sources} ${metrics_headers}) +target_compile_definitions(metrics_static PRIVATE BUILD_MODULE_TYPE_STATIC) +target_link_libraries(metrics_static gateway) + +linkSharedUtil(metrics) +linkSharedUtil(metrics_static) + +add_module_to_solution(metrics) + +if(install_modules) + install(TARGETS metrics LIBRARY DESTINATION "${LIB_INSTALL_DIR}/modules") +endif() + + +set(performance_e2e_sources + ./src/main.cpp +) +if(WIN32) + set(performance_e2e_sources + ${performance_e2e_sources} + ./src/performance_win.json + ) + set_source_files_properties(./src/performance_win.json PROPERTIES HEADER_FILE_ONLY ON) +else() + set(performance_e2e_sources + ${performance_e2e_sources} + ./src/performance_lin.json + ) + set_source_files_properties(./src/performance_lin.json PROPERTIES HEADER_FILE_ONLY ON) +endif() + +add_executable(performance_e2e ${performance_e2e_sources}) + +add_dependencies(performance_e2e simulator metrics) + +target_link_libraries(performance_e2e gateway nanomsg) +linkSharedUtil(performance_e2e) +install_broker(performance_e2e ${CMAKE_CURRENT_BINARY_DIR}/$(Configuration) ) +copy_gateway_dll(performance_e2e ${CMAKE_CURRENT_BINARY_DIR}/$(Configuration) ) + +set_target_properties(performance_e2e + PROPERTIES + FOLDER "tests/E2ETests") diff --git a/core/tests/performance_e2e/README.md b/core/tests/performance_e2e/README.md index 65f64e76..7bba5af2 100644 --- a/core/tests/performance_e2e/README.md +++ b/core/tests/performance_e2e/README.md @@ -291,7 +291,7 @@ The information the metrics module produces is: | Maximum latency | Time (microseconds) | Maximum message latency | | Devices Discovered | Count | Number of deviceId names received in message. | -The metrics module also produces this information for each deviceId recognized: +The metrics module also produces this information for each deviceId recognized. | Metric | Measure | Description | | ------------------------ | ------------------- | ----------- | diff --git a/core/tests/performance_e2e/inc/metrics.h b/core/tests/performance_e2e/inc/metrics.h new file mode 100644 index 00000000..32015bab --- /dev/null +++ b/core/tests/performance_e2e/inc/metrics.h @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef METRICS_H +#define METRICS_H + +#include "module.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +MODULE_EXPORT const MODULE_API* MODULE_STATIC_GETAPI(METRICS_MODULE)(MODULE_API_VERSION gateway_api_version); + +#ifdef __cplusplus +} +#endif + +#endif /*METRICS_H*/ diff --git a/core/tests/performance_e2e/inc/simulator.h b/core/tests/performance_e2e/inc/simulator.h new file mode 100644 index 00000000..c4497967 --- /dev/null +++ b/core/tests/performance_e2e/inc/simulator.h @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SIMULATOR_H +#define SIMULATOR_H + +#include "module.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct SIMULATOR_MODULE_CONFIG_TAG +{ + char * device_id; + size_t message_delay; + size_t properties_count; + size_t properties_size; + size_t message_size; +} SIMULATOR_MODULE_CONFIG; + + +MODULE_EXPORT const MODULE_API* MODULE_STATIC_GETAPI(SIMULATOR_MODULE)(MODULE_API_VERSION gateway_api_version); + +#ifdef __cplusplus +} +#endif + +#endif /*SIMULATOR_H*/ diff --git a/core/tests/performance_e2e/src/main.cpp b/core/tests/performance_e2e/src/main.cpp new file mode 100644 index 00000000..3342ae71 --- /dev/null +++ b/core/tests/performance_e2e/src/main.cpp @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#include + +#include "gateway.h" +#include "azure_c_shared_utility/threadapi.h" +#include + +int main(int argc, char** argv) +{ + int sleep_in_ms = 5000; + GATEWAY_HANDLE gateway; + if (argc != 2 && argc != 3) + { + std::cout + << "usage: performance_sample configFile [duration]" << std::endl + << "where configFile is the name of the file that contains the gateway configuration" << std::endl + << "where duration is the length of time in seconds for the test to run" << std::endl; + } + else + { + if (argc==3) + { + sleep_in_ms = std::stoi(argv[2]) * 1000; + } + + if ((gateway = Gateway_CreateFromJson(argv[1])) == NULL) + { + std::cout << "failed to create the gateway from JSON" << std::endl; + } + else + { + + std::cout << "gateway successfully created from JSON" << std::endl; + std::cout << "gateway shall run for " << sleep_in_ms/1000 << " seconds" << std::endl; + ThreadAPI_Sleep(sleep_in_ms); + + Gateway_Destroy(gateway); + } + } + return 0; +} diff --git a/core/tests/performance_e2e/src/metrics.cpp b/core/tests/performance_e2e/src/metrics.cpp new file mode 100644 index 00000000..359244b1 --- /dev/null +++ b/core/tests/performance_e2e/src/metrics.cpp @@ -0,0 +1,270 @@ + +#include +#include +#include +#include +#include + +#include + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/threadapi.h" +#include "azure_c_shared_utility/map.h" +#include "message.h" +#include "module.h" + +#include "simulator.h" + +using HrClock = std::chrono::high_resolution_clock; +using MicroSeconds = std::chrono::microseconds; +using HrTime = std::chrono::time_point; +using Counter = long long; + +template +struct SimpleAccumulator +{ + typedef ValueT result_type; + + ValueT accumulation; + ValueT max; + Counter accumulation_count; + + SimpleAccumulator() : accumulation(0), max(0), accumulation_count(0) + { + } + + result_type getSum() + { + return this->accumulation; + } + Counter getCount() + { + return this->accumulation_count; + } + result_type getMean() + { + ValueT mean(0); + if (accumulation_count != 0) + { + mean = accumulation / accumulation_count; + } + return mean; + } + void add(ValueT v) + { + this->accumulation += v; + this->accumulation_count++; + if (v > max) + { + max = v; + } + } +}; + +struct METRICS_PER_DEVICE +{ + Counter messages_received; + Counter seqence_number; + Counter out_of_sequence_messages; + Counter messages_lost; + METRICS_PER_DEVICE() : messages_received(0), seqence_number(0), out_of_sequence_messages(0), messages_lost(0) + { + } +} ; + +using PerDeviceMap = std::map; + +typedef struct METRICS_MODULE_HANDLE_TAG +{ + BROKER_HANDLE broker; + bool started; + HrTime start_time; + Counter all_messages_received; + Counter non_conforming_messages; + SimpleAccumulator latency; + PerDeviceMap *per_device_metrics; +} METRICS_MODULE_HANDLE; + + +static void* MetricsModule_ParseConfigurationFromJson(const char* configuration) +{ + (void)configuration; + return NULL; +} + +static void MetricsModule_FreeConfiguration(void* configuration) +{ + (void)configuration; +} + +static MODULE_HANDLE MetricsModule_Create(BROKER_HANDLE broker, const void* configuration) +{ + METRICS_MODULE_HANDLE * module; + + if (broker == NULL) + { + LogError(" had a null input. broker: [%p], configuration: [%p]", broker, configuration); + module = NULL; + } + else + { + module = (METRICS_MODULE_HANDLE*)malloc(sizeof(METRICS_MODULE_HANDLE)); + if (module == NULL) + { + LogError("Could not allocate memory for module handle"); + } + else + { + HrTime init_time; + Counter init_count(0); + SimpleAccumulator init_accumulator; + + module->broker = broker; + module->started = false; + module->start_time = init_time; + module->all_messages_received = init_count; + module->non_conforming_messages = init_count; + module->latency = init_accumulator; + module->per_device_metrics = new PerDeviceMap(); + } + } + return (MODULE_HANDLE)module; +} + +static void MetricsModule_Start(MODULE_HANDLE moduleHandle) +{ + if (moduleHandle != NULL) + { + METRICS_MODULE_HANDLE * module = (METRICS_MODULE_HANDLE *)moduleHandle; + module->start_time = std::chrono::time_point_cast(HrClock::now()); + module->started = true; + } +} + +static void MetricsModule_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle) +{ + if (moduleHandle != NULL && messageHandle != NULL) + { + HrTime received_time = std::chrono::time_point_cast(HrClock::now()); + METRICS_MODULE_HANDLE * module = (METRICS_MODULE_HANDLE *)moduleHandle; + module->all_messages_received++; + + CONSTMAP_HANDLE message_properties = Message_GetProperties(messageHandle); + if (message_properties == NULL) + { + module->non_conforming_messages++; + } + else + { + const char * timestamp_property = ConstMap_GetValue(message_properties, "timestamp"); + const char * seq_num_property = ConstMap_GetValue(message_properties, "sequence number"); + const char * deviceId_property = ConstMap_GetValue(message_properties, "deviceId"); + if ((timestamp_property == NULL) || (seq_num_property == NULL) || (deviceId_property == NULL)) + { + module->non_conforming_messages++; + } + else + { + try + { + MicroSeconds timestamp_duration(std::stoll(timestamp_property)); + HrTime timestamp(timestamp_duration); + MicroSeconds current_latency = received_time - timestamp; + module->latency.add(current_latency); + + if (deviceId_property == NULL) + { + module->non_conforming_messages++; + } + else + { + std::string deviceId(deviceId_property); + METRICS_PER_DEVICE& per_device = (*module->per_device_metrics)[deviceId]; + per_device.messages_received++; + per_device.seqence_number++; + + Counter sequence_number(std::stoll(seq_num_property)); + if (sequence_number != per_device.seqence_number) + { + per_device.out_of_sequence_messages++; + if (sequence_number > per_device.seqence_number) + { + per_device.messages_lost += (sequence_number - per_device.seqence_number); + } + per_device.seqence_number = sequence_number; + } + } + } + catch (std::exception & e) + { + LogError("non-conforming message: exception caught: %s", e.what()); + module->non_conforming_messages++; + } + } + + ConstMap_Destroy(message_properties); + } + } +} + +static void MetricsModule_Destroy(MODULE_HANDLE moduleHandle) +{ + if (moduleHandle == NULL) + { + LogError("Destroying a NULL module"); + } + else + { + METRICS_MODULE_HANDLE * module = (METRICS_MODULE_HANDLE *)moduleHandle; + if (module->started) + { + HrTime destroy_time = std::chrono::time_point_cast(HrClock::now()); + MicroSeconds duration = destroy_time - module->start_time; + std::cout + << "Module Metrics:" << std::endl + << "---------------" << std::endl + << "Duration (ms): " << duration.count() / 1000 << std::endl + << "Messages received: " << module->all_messages_received << std::endl + << "Non-Conforming Messages: " << module->non_conforming_messages << std::endl + << "Message Latency (average microseconds): " << module->latency.getMean().count() << std::endl + << "Message Latency (max microseconds): " << module->latency.max.count() << std::endl + << "Devices Discovered: " << module->per_device_metrics->size() << std::endl; + for (PerDeviceMap::iterator d = module->per_device_metrics->begin(); + d != module->per_device_metrics->end(); + d++) + { + std::cout + << "Device: " << (*d).first << std::endl + << "Message count: " << (*d).second.messages_received << std::endl + << "Out of Sequence Count: " << (*d).second.out_of_sequence_messages << std::endl + << "Messages Lost: " << (*d).second.messages_lost << std::endl; + } + } + free(module); + } +} + + + +static const MODULE_API_1 METRICS_APIS_all = +{ + {MODULE_API_VERSION_1}, + + MetricsModule_ParseConfigurationFromJson, + MetricsModule_FreeConfiguration, + MetricsModule_Create, + MetricsModule_Destroy, + MetricsModule_Receive, + MetricsModule_Start +}; + +#ifdef BUILD_MODULE_TYPE_STATIC +MODULE_EXPORT const MODULE_API* MODULE_STATIC_GETAPI(METRICS_MODULE)(MODULE_API_VERSION gateway_api_version) +#else +MODULE_EXPORT const MODULE_API* Module_GetApi(MODULE_API_VERSION gateway_api_version) +#endif +{ + (void)gateway_api_version; + return reinterpret_cast< const MODULE_API *>(&METRICS_APIS_all); +} diff --git a/core/tests/performance_e2e/src/performance_lin.json b/core/tests/performance_e2e/src/performance_lin.json new file mode 100644 index 00000000..3f267d4f --- /dev/null +++ b/core/tests/performance_e2e/src/performance_lin.json @@ -0,0 +1,33 @@ +{ + "modules": [ + { + "name": "metrics1", + "loader": { + "name": "native", + "entrypoint": { + "module.path": "libmetrics.so" + } + }, + "args": null + }, + { + "name": "simulator1", + "loader": { + "name": "native", + "entrypoint": { + "module.path": "libsimulator.so" + } + }, + "args": { + "deviceId": "device1", + "message.delay": 0 + } + } + ], + "links": [ + { + "source": "simulator1", + "sink": "metrics1" + } + ] +} diff --git a/core/tests/performance_e2e/src/performance_win.json b/core/tests/performance_e2e/src/performance_win.json new file mode 100644 index 00000000..2412451e --- /dev/null +++ b/core/tests/performance_e2e/src/performance_win.json @@ -0,0 +1,33 @@ +{ + "modules": [ + { + "name": "metrics1", + "loader": { + "name": "native", + "entrypoint": { + "module.path": "Debug\\metrics.dll" + } + }, + "args": null + }, + { + "name": "simulator1", + "loader": { + "name": "native", + "entrypoint": { + "module.path": "Debug\\simulator.dll" + } + }, + "args": { + "deviceId": "device1", + "message.delay": 0 + } + } + ], + "links": [ + { + "source": "simulator1", + "sink": "metrics1" + } + ] +} diff --git a/core/tests/performance_e2e/src/simulator.cpp b/core/tests/performance_e2e/src/simulator.cpp new file mode 100644 index 00000000..763a7e60 --- /dev/null +++ b/core/tests/performance_e2e/src/simulator.cpp @@ -0,0 +1,408 @@ + +#include +#include +#include +#include +#include +#include + +#include + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/xlogging.h" +#include "azure_c_shared_utility/threadapi.h" +#include "azure_c_shared_utility/map.h" +#include "message.h" +#include "module.h" + +#include "simulator.h" + + +typedef struct SIMULATOR_MODULE_HANDLE_TAG +{ + BROKER_HANDLE broker; + char * device_id; + size_t message_delay; + size_t properties_count; + size_t properties_size; + size_t message_size; + char * psuedo_random_buffer; + bool thread_flag; + THREAD_HANDLE main_thread; +} SIMULATOR_MODULE_HANDLE; + + +static void* SimulatorModule_ParseConfigurationFromJson(const char* configuration) +{ + SIMULATOR_MODULE_CONFIG * result; + if (configuration == NULL) + { + LogError("Simulator module expects configuration"); + result = NULL; + } + else + { + JSON_Value* json = json_parse_string((const char*)configuration); + if (json == NULL) + { + LogError("unable to json_parse_string"); + result = NULL; + } + else + { + JSON_Object* obj = json_value_get_object(json); + if (obj == NULL) + { + LogError("unable to json_value_get_object"); + result = NULL; + } + else + { + const char* deviceIdValue = json_object_get_string(obj, "deviceId"); + if (deviceIdValue == NULL) + { + LogError("deviceId is a required field in configuration"); + result = NULL; + } + else + { + result = (SIMULATOR_MODULE_CONFIG *)malloc(sizeof(SIMULATOR_MODULE_CONFIG)); + if (result == NULL) + { + LogError("Could not allocated Module data"); + } + else + { + if (mallocAndStrcpy_s(&(result->device_id), deviceIdValue) != 0) + { + LogError("could not allocate memory for deviceID string"); + free(result); + result = NULL; + } + else + { + result->message_delay = 0; + result->message_size = 256; + result->properties_count = 2; + result->properties_size = 16; + + if (json_object_has_value_of_type(obj, "message.delay", JSONNumber)) + { + result->message_delay = static_cast(json_object_get_number(obj, "message.delay")); + } + double message_size_value = json_object_get_number(obj, "message.size"); + if (message_size_value > 0) + { + result->message_size = static_cast(message_size_value); + } + double properties_count_value = json_object_get_number(obj, "properties.count"); + if (properties_count_value > 0) + { + result->properties_count = static_cast(properties_count_value); + } + double properties_size_value = json_object_get_number(obj, "properties.size"); + if (properties_size_value > 0) + { + result->properties_size = static_cast(properties_size_value); + } + } + } + } + } + json_value_free(json); + } + } + return result; +} + +static void SimulatorModule_FreeConfiguration(void* configuration) +{ + if (configuration != NULL) + { + SIMULATOR_MODULE_CONFIG * conf = (SIMULATOR_MODULE_CONFIG*)configuration; + free(conf->device_id); + free(conf); + } +} + +static MODULE_HANDLE SimulatorModule_Create(BROKER_HANDLE broker, const void* configuration) +{ + SIMULATOR_MODULE_HANDLE * module; + + if ((broker == NULL) || + (configuration == NULL)) + { + LogError("Simulator had a null input. broker: [%p], configuration: [%p]", broker, configuration); + module = NULL; + } + else + { + SIMULATOR_MODULE_CONFIG * conf = (SIMULATOR_MODULE_CONFIG *)configuration; + module = (SIMULATOR_MODULE_HANDLE*)malloc(sizeof(SIMULATOR_MODULE_HANDLE)); + if (module == NULL) + { + LogError("Could not allocate memory for module handle"); + } + else + { + if (mallocAndStrcpy_s(&(module->device_id), conf->device_id) != 0) + { + LogError("could not allocate memory for deviceID string"); + free(module); + module = NULL; + } + else + { + module->broker = broker; + module->message_delay = conf->message_delay; + module->message_size = conf->message_size; + module->properties_count = conf->properties_count; + module->properties_size = conf->properties_size; + size_t max_buffer = std::max(module->properties_size, module->message_size); + module->psuedo_random_buffer = (char*)malloc(max_buffer + 1); + if (module->psuedo_random_buffer == NULL) + { + LogError("could not allocate memory for deviceID string"); + free(module->device_id); + free(module); + module = NULL; + } + else + { + std::default_random_engine generator; + std::uniform_int_distribution distribution(' ', 'z'); + for (size_t i = 0; i < max_buffer; i++) + { + (module->psuedo_random_buffer)[i] = distribution(generator); + } + (module->psuedo_random_buffer)[max_buffer] = '\0'; + } + } + } + } + return (MODULE_HANDLE)module; +} + +static int SimulatorModule_create_message(SIMULATOR_MODULE_HANDLE * module, MESSAGE_CONFIG* message) +{ + int thread_result; + MAP_HANDLE property_map = Map_Create(NULL); + if (property_map == NULL) + { + LogError("Allocation of properties map failed."); + thread_result = -__LINE__; + } + else + { + std::string property_string(module->psuedo_random_buffer, module->properties_size); + std::string property_count_string = std::to_string(module->properties_count); + + if (property_string.empty() || property_count_string.empty()) + { + LogError("Allocation of properties string failed."); + Map_Destroy(property_map); + thread_result = -__LINE__; + } + else + { + if (Map_Add(property_map, "deviceId", module->device_id) != MAP_OK) + { + Map_Destroy(property_map); + thread_result = -__LINE__; + } + else if (Map_Add(property_map, "property count", property_count_string.c_str()) != MAP_OK) + { + Map_Destroy(property_map); + thread_result = -__LINE__; + } + else + { + thread_result = 0; + for (size_t p = 0; p < module->properties_count; p++) + { + std::ostringstream property_n; + property_n << "property" << p; + if (Map_Add(property_map, property_n.str().c_str(), property_string.c_str())) + { + thread_result = -__LINE__; + break; + } + } + if (thread_result != 0) + { + Map_Destroy(property_map); + thread_result = -__LINE__; + } + else + { + message->source = (const unsigned char*)malloc(module->message_size); + if (message->source == NULL) + { + LogError("unable to allocate message buffer"); + Map_Destroy(property_map); + thread_result = -__LINE__; + } + else + { + message->sourceProperties = property_map; + message->size = module->message_size; + if (module->message_size == 0) + { + message->source = NULL; + } + else + { + memcpy((void*)message->source, module->psuedo_random_buffer, message->size - 1); + ((char*)(message->source))[message->size - 1] = '\0'; + } + thread_result = 0; + } + + } + } + } + } + return thread_result; +} + +static int SimulatorModule_thread(void * context) +{ + int thread_result; + SIMULATOR_MODULE_HANDLE * module = (SIMULATOR_MODULE_HANDLE *)context; + MESSAGE_CONFIG message_to_send; + + thread_result = SimulatorModule_create_message(module, &message_to_send); + if (thread_result != 0) + { + LogError("unable to continue with simulation"); + } + else + { + using HrClock = std::chrono::high_resolution_clock; + using MicroSeconds = std::chrono::microseconds; + long long time_to_wait = module->message_delay * 1000; + + size_t messages_produced = 0; + thread_result = 0; + while (module->thread_flag) + { + std::chrono::time_point t1 = std::chrono::time_point_cast(HrClock::now()); + auto t1_as_int = t1.time_since_epoch().count(); + std::string t1_as_string = std::to_string(t1_as_int); + messages_produced++; + std::string messages_produced_as_string = std::to_string(messages_produced); + if (Map_AddOrUpdate(message_to_send.sourceProperties, "timestamp", t1_as_string.c_str()) != MAP_OK) + { + LogError("Unable to update timestamp in message"); + module->thread_flag = false; + thread_result = -__LINE__; + break; + } + else if (Map_AddOrUpdate(message_to_send.sourceProperties, "sequence number", messages_produced_as_string.c_str()) != MAP_OK) + { + LogError("Unable to update sequence number in message"); + module->thread_flag = false; + thread_result = -__LINE__; + break; + } + else + { + MESSAGE_HANDLE next_message = Message_Create(&message_to_send); + if (next_message == NULL) + { + LogError("Unable to create next message"); + module->thread_flag = false; + thread_result = -__LINE__; + break; + } + else + { + if (Broker_Publish(module->broker, module, next_message) != BROKER_OK) + { + LogError("Unable to publish message"); + module->thread_flag = false; + thread_result = -__LINE__; + break; + } + else + { + Message_Destroy(next_message); + std::chrono::time_point t2 = std::chrono::time_point_cast(HrClock::now()); + auto time_to_publish = t2.time_since_epoch().count() - t1_as_int; + if (time_to_publish < time_to_wait) + { + unsigned int remaining_time = static_cast((time_to_wait - time_to_publish)/1000); + ThreadAPI_Sleep(remaining_time); + } + } + } + } + } + } + return thread_result; +} + +static void SimulatorModule_Start(MODULE_HANDLE moduleHandle) +{ + if (moduleHandle != NULL) + { + SIMULATOR_MODULE_HANDLE * module = (SIMULATOR_MODULE_HANDLE *)moduleHandle; + + module->thread_flag = true; + if (ThreadAPI_Create(&(module->main_thread), SimulatorModule_thread, module) != 0) + { + LogError("Thread Creation failed"); + module->main_thread = NULL; + } + } +} + +static void SimulatorModule_Destroy(MODULE_HANDLE moduleHandle) +{ + if (moduleHandle == NULL) + { + LogError("Destroying a NULL module"); + } + else + { + SIMULATOR_MODULE_HANDLE * module = (SIMULATOR_MODULE_HANDLE *)moduleHandle; + module->thread_flag = false; + int thread_result; + (void)ThreadAPI_Join(module->main_thread, &thread_result); + if (thread_result != 0) + { + LogInfo("Thread ended with non-zero result: %d", thread_result); + } + free(module->device_id); + free(module->psuedo_random_buffer); + free(module); + } +} + +static void SimulatorModule_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle) +{ + (void)moduleHandle; + (void)messageHandle; +} + +static const MODULE_API_1 SIMULATOR_APIS_all = +{ + {MODULE_API_VERSION_1}, + + SimulatorModule_ParseConfigurationFromJson, + SimulatorModule_FreeConfiguration, + SimulatorModule_Create, + SimulatorModule_Destroy, + SimulatorModule_Receive, + SimulatorModule_Start +}; + +#ifdef BUILD_MODULE_TYPE_STATIC +MODULE_EXPORT const MODULE_API* MODULE_STATIC_GETAPI(SIMULATOR_MODULE)(MODULE_API_VERSION gateway_api_version) +#else +MODULE_EXPORT const MODULE_API* Module_GetApi(MODULE_API_VERSION gateway_api_version) +#endif +{ + (void)gateway_api_version; + return reinterpret_cast< const MODULE_API *>(&SIMULATOR_APIS_all); +} diff --git a/samples/performance/Overview.png b/samples/performance/Overview.png deleted file mode 100644 index 54f53e9d..00000000 Binary files a/samples/performance/Overview.png and /dev/null differ diff --git a/samples/performance/Overview.vsdx b/samples/performance/Overview.vsdx deleted file mode 100644 index 239d2eb4..00000000 Binary files a/samples/performance/Overview.vsdx and /dev/null differ diff --git a/samples/performance/default_sample.png b/samples/performance/default_sample.png deleted file mode 100644 index 0f8004aa..00000000 Binary files a/samples/performance/default_sample.png and /dev/null differ diff --git a/samples/performance/default_sample.vsdx b/samples/performance/default_sample.vsdx deleted file mode 100644 index f1c73714..00000000 Binary files a/samples/performance/default_sample.vsdx and /dev/null differ