diff --git a/samples/fleet_provisioning/fleet_provisioning/main.cpp b/samples/fleet_provisioning/fleet_provisioning/main.cpp index 1dbcd0ea2..f4f5fdfd5 100644 --- a/samples/fleet_provisioning/fleet_provisioning/main.cpp +++ b/samples/fleet_provisioning/fleet_provisioning/main.cpp @@ -2,6 +2,14 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ + +/** + * A sample application demonstrating usage of AWS IoT Fleet provisioning. + * + * It's easier to follow a synchronous workflow, when events happen one after another. For that reason, this sample + * performs all actions, like connecting to a server or registering a thing, in synchronous manner. + */ + #include #include @@ -26,14 +34,23 @@ using namespace Aws::Crt; using namespace Aws::Iotidentity; -static std::string getFileData(std::string const &fileName) +static String getFileData(const String &fileName) { - std::ifstream ifs(fileName); + std::ifstream ifs(fileName.c_str()); std::string str; getline(ifs, str, (char)ifs.eof()); - return str; + return str.c_str(); } +/** + * Auxiliary structure for holding data used by MQTT connection. + */ +struct ConnectionContext +{ + std::promise connectionCompletedPromise; + std::promise connectionClosedPromise; +}; + /** * Auxiliary structure for holding data used when creating a certificate. */ @@ -46,6 +63,85 @@ struct CreateCertificateContext String token; }; +/** + * Auxiliary structure for holding data used when registering a thing. + */ +struct RegisterThingContext +{ + std::promise pubAckPromise; + std::promise acceptedSubAckPromise; + std::promise rejectedSubAckPromise; + std::promise thingCreatedPromise; +}; + +/** + * Create MQTT3 connection. + */ +std::shared_ptr createConnection(const Utils::cmdData &cmdData, ConnectionContext &ctx) +{ + /** + * In a real world application you probably don't want to enforce synchronous behavior + * but this is a sample console application, so we'll just do that with a promise. + */ + + // Invoked when a MQTT connect has completed or failed + auto onConnectionCompleted = [&ctx](Mqtt::MqttConnection &, int errorCode, Mqtt::ReturnCode returnCode, bool) { + if (errorCode) + { + fprintf(stdout, "Connection failed with error %s\n", ErrorDebugString(errorCode)); + ctx.connectionCompletedPromise.set_value(false); + } + else + { + fprintf(stdout, "Connection completed with return code %d\n", returnCode); + ctx.connectionCompletedPromise.set_value(true); + } + }; + + // Invoked when a disconnect has been completed + auto onDisconnect = [&ctx](Mqtt::MqttConnection & /*conn*/) { + { + fprintf(stdout, "Disconnect completed\n"); + ctx.connectionClosedPromise.set_value(); + } + }; + + // Create the MQTT builder and populate it with data from cmdData. + auto clientConfigBuilder = + Aws::Iot::MqttClientConnectionConfigBuilder(cmdData.input_cert.c_str(), cmdData.input_key.c_str()); + clientConfigBuilder.WithEndpoint(cmdData.input_endpoint); + if (cmdData.input_ca != "") + { + clientConfigBuilder.WithCertificateAuthority(cmdData.input_ca.c_str()); + } + + // Create the MQTT connection from the MQTT builder + auto clientConfig = clientConfigBuilder.Build(); + if (!clientConfig) + { + fprintf( + stderr, + "Client Configuration initialization failed with error %s\n", + Aws::Crt::ErrorDebugString(clientConfig.LastError())); + exit(-1); + } + Aws::Iot::MqttClient client = Aws::Iot::MqttClient(); + auto connection = client.NewConnection(clientConfig); + if (!*connection) + { + fprintf( + stderr, + "MQTT Connection Creation failed with error %s\n", + Aws::Crt::ErrorDebugString(connection->LastError())); + exit(-1); + } + + connection->OnConnectionCompleted = std::move(onConnectionCompleted); + connection->OnDisconnect = std::move(onDisconnect); + + return connection; +} + /** * Keys-and-Certificate workflow. * @@ -53,7 +149,7 @@ struct CreateCertificateContext * callbacks must be alive for the whole duration of the identityClient's lifetime. An instance of * CreateCertificateContext is used to store variables used by the callbacks. */ -void useKeysAndCertificate(IotIdentityClient &identityClient, CreateCertificateContext &ctx) +void createKeysAndCertificate(IotIdentityClient &identityClient, CreateCertificateContext &ctx) { auto onKeysPublishPubAck = [&ctx](int ioErr) { if (ioErr != AWS_OP_SUCCESS) @@ -143,7 +239,7 @@ void useKeysAndCertificate(IotIdentityClient &identityClient, CreateCertificateC * callbacks must be alive for the whole duration of the identityClient's lifetime. An instance of * CreateCertificateContext is used to store variables used by the callbacks. */ -void useCsr(IotIdentityClient &identityClient, CreateCertificateContext &ctx, const String &csrFile) +void createCertificateFromCsr(IotIdentityClient &identityClient, CreateCertificateContext &ctx, const String &csrFile) { auto onCsrPublishPubAck = [&ctx](int ioErr) { if (ioErr != AWS_OP_SUCCESS) @@ -229,119 +325,22 @@ void useCsr(IotIdentityClient &identityClient, CreateCertificateContext &ctx, co ctx.tokenReceivedPromise.get_future().wait(); } -int main(int argc, char *argv[]) +/** + * Provision an AWS IoT thing using a pre-defined template. + */ +void registerThing( + IotIdentityClient &identityClient, + RegisterThingContext &ctx, + const Utils::cmdData &cmdData, + const String &token) { - /************************ Setup ****************************/ - - // Do the global initialization for the API - ApiHandle apiHandle; - // Variables for the sample - String csrFile; - RegisterThingResponse registerThingResponse; - - /** - * cmdData is the arguments/input from the command line placed into a single struct for - * use in this sample. This handles all of the command line parsing, validating, etc. - * See the Utils/CommandLineUtils for more information. - */ - Utils::cmdData cmdData = Utils::parseSampleInputFleetProvisioning(argc, argv, &apiHandle); - - if (cmdData.input_csrPath != "") - { - csrFile = getFileData(cmdData.input_csrPath.c_str()).c_str(); - } - - /** - * In a real world application you probably don't want to enforce synchronous behavior - * but this is a sample console application, so we'll just do that with a promise. - */ - std::promise connectionCompletedPromise; - std::promise connectionClosedPromise; - - // Invoked when a MQTT connect has completed or failed - auto onConnectionCompleted = [&](Mqtt::MqttConnection &, int errorCode, Mqtt::ReturnCode returnCode, bool) { - if (errorCode) - { - fprintf(stdout, "Connection failed with error %s\n", ErrorDebugString(errorCode)); - connectionCompletedPromise.set_value(false); - } - else - { - fprintf(stdout, "Connection completed with return code %d\n", returnCode); - connectionCompletedPromise.set_value(true); - } - }; - - // Invoked when a disconnect has been completed - auto onDisconnect = [&](Mqtt::MqttConnection & /*conn*/) { - { - fprintf(stdout, "Disconnect completed\n"); - connectionClosedPromise.set_value(); - } - }; - - // Create the MQTT builder and populate it with data from cmdData. - auto clientConfigBuilder = - Aws::Iot::MqttClientConnectionConfigBuilder(cmdData.input_cert.c_str(), cmdData.input_key.c_str()); - clientConfigBuilder.WithEndpoint(cmdData.input_endpoint); - if (cmdData.input_ca != "") - { - clientConfigBuilder.WithCertificateAuthority(cmdData.input_ca.c_str()); - } - - // Create the MQTT connection from the MQTT builder - auto clientConfig = clientConfigBuilder.Build(); - if (!clientConfig) - { - fprintf( - stderr, - "Client Configuration initialization failed with error %s\n", - Aws::Crt::ErrorDebugString(clientConfig.LastError())); - exit(-1); - } - Aws::Iot::MqttClient client = Aws::Iot::MqttClient(); - auto connection = client.NewConnection(clientConfig); - if (!*connection) - { - fprintf( - stderr, - "MQTT Connection Creation failed with error %s\n", - Aws::Crt::ErrorDebugString(connection->LastError())); - exit(-1); - } - - connection->OnConnectionCompleted = std::move(onConnectionCompleted); - connection->OnDisconnect = std::move(onDisconnect); - - /************************ Run the sample ****************************/ - - fprintf(stdout, "Connecting...\n"); - if (!connection->Connect(cmdData.input_clientId.c_str(), true, 0)) - { - fprintf(stderr, "MQTT Connection failed with error %s\n", ErrorDebugString(connection->LastError())); - exit(-1); - } - - if (!connectionCompletedPromise.get_future().get()) - { - return -1; - } - - IotIdentityClient identityClient(connection); - - std::promise registerPublishPubAckCompletedPromise; - std::promise registerAcceptedSubAckCompletedPromise; - std::promise registerRejectedSubAckCompletedPromise; - std::promise registerAcceptedCompletedPromise; - auto onRegisterAcceptedSubAck = [&](int ioErr) { if (ioErr != AWS_OP_SUCCESS) { fprintf(stderr, "Error subscribing to RegisterThing accepted: %s\n", ErrorDebugString(ioErr)); exit(-1); } - - registerAcceptedSubAckCompletedPromise.set_value(); + ctx.acceptedSubAckPromise.set_value(); }; auto onRegisterRejectedSubAck = [&](int ioErr) { @@ -350,14 +349,14 @@ int main(int argc, char *argv[]) fprintf(stderr, "Error subscribing to RegisterThing rejected: %s\n", ErrorDebugString(ioErr)); exit(-1); } - registerRejectedSubAckCompletedPromise.set_value(); + ctx.rejectedSubAckPromise.set_value(); }; auto onRegisterAccepted = [&](RegisterThingResponse *response, int ioErr) { if (ioErr == AWS_OP_SUCCESS) { fprintf(stdout, "RegisterThingResponse ThingName: %s.\n", response->ThingName->c_str()); - registerAcceptedCompletedPromise.set_value(); + ctx.thingCreatedPromise.set_value(); } else { @@ -389,22 +388,9 @@ int main(int argc, char *argv[]) fprintf(stderr, "Error publishing to RegisterThing: %s\n", ErrorDebugString(ioErr)); exit(-1); } - - registerPublishPubAckCompletedPromise.set_value(); + ctx.pubAckPromise.set_value(); }; - // Create certificate. - CreateCertificateContext certificateContext; - if (csrFile.empty()) - { - useKeysAndCertificate(identityClient, certificateContext); - } - else - { - useCsr(identityClient, certificateContext, csrFile); - } - - // After certificate is obtained, it's time to register a thing. fprintf(stdout, "Subscribing to RegisterThing Accepted and Rejected topics\n"); RegisterThingSubscriptionRequest registerSubscriptionRequest; registerSubscriptionRequest.TemplateName = cmdData.input_templateName; @@ -416,8 +402,8 @@ int main(int argc, char *argv[]) registerSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterRejected, onRegisterRejectedSubAck); // Wait for the subscriptions to the accept and reject RegisterThing topics to be established. - registerAcceptedSubAckCompletedPromise.get_future().wait(); - registerRejectedSubAckCompletedPromise.get_future().wait(); + ctx.acceptedSubAckPromise.get_future().wait(); + ctx.rejectedSubAckPromise.get_future().wait(); fprintf(stdout, "Publishing to RegisterThing topic\n"); RegisterThingRequest registerThingRequest; @@ -436,18 +422,69 @@ int main(int argc, char *argv[]) registerThingRequest.Parameters = params; // NOTE: In a real application creating multiple certificates you'll probably need to protect token var with // a critical section. This sample makes only one request for a certificate, so no data race is possible. - registerThingRequest.CertificateOwnershipToken = certificateContext.token; + registerThingRequest.CertificateOwnershipToken = token; identityClient.PublishRegisterThing(registerThingRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterPublishPubAck); - registerPublishPubAckCompletedPromise.get_future().wait(); + ctx.pubAckPromise.get_future().wait(); // Wait for registering a thing to succeed. - registerAcceptedCompletedPromise.get_future().wait(); + ctx.thingCreatedPromise.get_future().wait(); +} + +int main(int argc, char *argv[]) +{ + /************************ Setup ****************************/ + + // Do the global initialization for the API + ApiHandle apiHandle; + + /** + * cmdData is the arguments/input from the command line placed into a single struct for + * use in this sample. This handles all of the command line parsing, validating, etc. + * See the Utils/CommandLineUtils for more information. + */ + Utils::cmdData cmdData = Utils::parseSampleInputFleetProvisioning(argc, argv, &apiHandle); + + ConnectionContext connectionContext; + auto connection = createConnection(cmdData, connectionContext); + + /************************ Run the sample ****************************/ + + fprintf(stdout, "Connecting...\n"); + if (!connection->Connect(cmdData.input_clientId.c_str(), true, 0)) + { + fprintf(stderr, "MQTT Connection failed with error %s\n", ErrorDebugString(connection->LastError())); + exit(-1); + } + + if (!connectionContext.connectionCompletedPromise.get_future().get()) + { + exit(-1); + } + + // Create fleet provisioning client. + IotIdentityClient identityClient(connection); + + // Create certificate. + CreateCertificateContext certificateContext; + if (cmdData.input_csrPath != "") + { + auto csrFile = getFileData(cmdData.input_csrPath); + createCertificateFromCsr(identityClient, certificateContext, csrFile); + } + else + { + createKeysAndCertificate(identityClient, certificateContext); + } + + // After certificate is obtained, it's time to register a thing. + RegisterThingContext registerThingContext; + registerThing(identityClient, registerThingContext, cmdData, certificateContext.token); // Disconnect if (connection->Disconnect()) { - connectionClosedPromise.get_future().wait(); + connectionContext.connectionClosedPromise.get_future().wait(); } return 0; diff --git a/samples/fleet_provisioning/mqtt5_fleet_provisioning/main.cpp b/samples/fleet_provisioning/mqtt5_fleet_provisioning/main.cpp index 21ed958a8..c84b8b3b9 100644 --- a/samples/fleet_provisioning/mqtt5_fleet_provisioning/main.cpp +++ b/samples/fleet_provisioning/mqtt5_fleet_provisioning/main.cpp @@ -2,6 +2,14 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ + +/** + * A sample application demonstrating usage of AWS IoT Fleet provisioning with MQTT5 client. + * + * It's easier to follow a synchronous workflow, when events happen one after another. For that reason, this sample + * performs all actions, like connecting to a server or registering a thing, in synchronous manner. + */ + #include #include #include @@ -27,14 +35,25 @@ using namespace Aws::Crt; using namespace Aws::Iotidentity; -static std::string getFileData(std::string const &fileName) +static String getFileData(const String &fileName) { - std::ifstream ifs(fileName); + std::ifstream ifs(fileName.c_str()); std::string str; getline(ifs, str, (char)ifs.eof()); - return str; + return str.c_str(); } +/** + * Auxiliary structure for holding data used by MQTT connection. + */ +struct Mqtt5ClientContext +{ + std::promise connectionPromise; + std::promise stoppedPromise; + std::promise disconnectPromise; + std::promise subscribeSuccess; +}; + /** * Auxiliary structure for holding data used when creating a certificate. */ @@ -47,6 +66,74 @@ struct CreateCertificateContext String token; }; +/** + * Auxiliary structure for holding data used when registering a thing. + */ +struct RegisterThingContext +{ + std::promise pubAckPromise; + std::promise acceptedSubAckPromise; + std::promise rejectedSubAckPromise; + std::promise thingCreatedPromise; +}; + +/** + * Create MQTT5 client. + */ +std::shared_ptr createMqtt5Client(Mqtt5ClientContext &ctx, const Utils::cmdData &cmdData) +{ + // Create the MQTT5 builder and populate it with data from cmdData. + Aws::Iot::Mqtt5ClientBuilder *builder = Aws::Iot::Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithMtlsFromPath( + cmdData.input_endpoint, cmdData.input_cert.c_str(), cmdData.input_key.c_str()); + + // Check if the builder setup correctly. + if (builder == nullptr) + { + printf( + "Failed to setup mqtt5 client builder with error code %d: %s", LastError(), ErrorDebugString(LastError())); + exit(-1); + } + + // Setup connection options + std::shared_ptr connectOptions = std::make_shared(); + connectOptions->WithClientId(cmdData.input_clientId); + builder->WithConnectOptions(connectOptions); + if (cmdData.input_port != 0) + { + builder->WithPort(static_cast(cmdData.input_port)); + } + + // Setup lifecycle callbacks + builder->WithClientConnectionSuccessCallback([&ctx](const Mqtt5::OnConnectionSuccessEventData &eventData) { + fprintf( + stdout, + "Mqtt5 Client connection succeed, clientid: %s.\n", + eventData.negotiatedSettings->getClientId().c_str()); + ctx.connectionPromise.set_value(true); + }); + builder->WithClientConnectionFailureCallback([&ctx](const Mqtt5::OnConnectionFailureEventData &eventData) { + fprintf(stdout, "Mqtt5 Client connection failed with error: %s.\n", aws_error_debug_str(eventData.errorCode)); + ctx.connectionPromise.set_value(false); + }); + builder->WithClientStoppedCallback([&ctx](const Mqtt5::OnStoppedEventData &) { + fprintf(stdout, "Mqtt5 Client stopped.\n"); + ctx.stoppedPromise.set_value(); + }); + builder->WithClientAttemptingConnectCallback([](const Mqtt5::OnAttemptingConnectEventData &) { + fprintf(stdout, "Mqtt5 Client attempting connection...\n"); + }); + builder->WithClientDisconnectionCallback([&ctx](const Mqtt5::OnDisconnectionEventData &eventData) { + fprintf(stdout, "Mqtt5 Client disconnection with reason: %s.\n", aws_error_debug_str(eventData.errorCode)); + ctx.disconnectPromise.set_value(); + }); + + // Create Mqtt5Client + std::shared_ptr client = builder->Build(); + delete builder; + + return client; +} + /** * Keys-and-Certificate workflow. * @@ -54,7 +141,7 @@ struct CreateCertificateContext * callbacks must be alive for the whole duration of the identityClient's lifetime. An instance of * CreateCertificateContext is used to store variables used by the callbacks. */ -void useKeysAndCertificate(IotIdentityClient &identityClient, CreateCertificateContext &ctx) +void createKeysAndCertificate(IotIdentityClient &identityClient, CreateCertificateContext &ctx) { auto onKeysPublishPubAck = [&ctx](int ioErr) { if (ioErr != AWS_OP_SUCCESS) @@ -144,7 +231,7 @@ void useKeysAndCertificate(IotIdentityClient &identityClient, CreateCertificateC * callbacks must be alive for the whole duration of the identityClient's lifetime. An instance of * CreateCertificateContext is used to store variables used by the callbacks. */ -void useCsr(IotIdentityClient &identityClient, CreateCertificateContext &ctx, const String &csrFile) +void createCertificateFromCsr(IotIdentityClient &identityClient, CreateCertificateContext &ctx, const String &csrFile) { auto onCsrPublishPubAck = [&ctx](int ioErr) { if (ioErr != AWS_OP_SUCCESS) @@ -230,111 +317,22 @@ void useCsr(IotIdentityClient &identityClient, CreateCertificateContext &ctx, co ctx.tokenReceivedPromise.get_future().wait(); } -int main(int argc, char *argv[]) +/** + * Provision an AWS IoT thing using a pre-defined template. + */ +void registerThing( + IotIdentityClient &identityClient, + RegisterThingContext &ctx, + const Utils::cmdData &cmdData, + const String &token) { - /************************ Setup ****************************/ - - // Do the global initialization for the API - ApiHandle apiHandle; - // Variables for the sample - String csrFile; - RegisterThingResponse registerThingResponse; - - /** - * cmdData is the arguments/input from the command line placed into a single struct for - * use in this sample. This handles all of the command line parsing, validating, etc. - * See the Utils/CommandLineUtils for more information. - */ - Utils::cmdData cmdData = Utils::parseSampleInputFleetProvisioning(argc, argv, &apiHandle); - - if (cmdData.input_csrPath != "") - { - csrFile = getFileData(cmdData.input_csrPath.c_str()).c_str(); - } - - // Create the MQTT5 builder and populate it with data from cmdData. - Aws::Iot::Mqtt5ClientBuilder *builder = Aws::Iot::Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithMtlsFromPath( - cmdData.input_endpoint, cmdData.input_cert.c_str(), cmdData.input_key.c_str()); - - // Check if the builder setup correctly. - if (builder == nullptr) - { - printf( - "Failed to setup mqtt5 client builder with error code %d: %s", LastError(), ErrorDebugString(LastError())); - return -1; - } - - // Setup connection options - std::shared_ptr connectOptions = std::make_shared(); - connectOptions->WithClientId(cmdData.input_clientId); - builder->WithConnectOptions(connectOptions); - if (cmdData.input_port != 0) - { - builder->WithPort(static_cast(cmdData.input_port)); - } - - std::promise connectionPromise; - std::promise stoppedPromise; - std::promise disconnectPromise; - std::promise subscribeSuccess; - - // Setup lifecycle callbacks - builder->WithClientConnectionSuccessCallback( - [&connectionPromise](const Mqtt5::OnConnectionSuccessEventData &eventData) { - fprintf( - stdout, - "Mqtt5 Client connection succeed, clientid: %s.\n", - eventData.negotiatedSettings->getClientId().c_str()); - connectionPromise.set_value(true); - }); - builder->WithClientConnectionFailureCallback([&connectionPromise]( - const Mqtt5::OnConnectionFailureEventData &eventData) { - fprintf(stdout, "Mqtt5 Client connection failed with error: %s.\n", aws_error_debug_str(eventData.errorCode)); - connectionPromise.set_value(false); - }); - builder->WithClientStoppedCallback([&stoppedPromise](const Mqtt5::OnStoppedEventData &) { - fprintf(stdout, "Mqtt5 Client stopped.\n"); - stoppedPromise.set_value(); - }); - builder->WithClientAttemptingConnectCallback([](const Mqtt5::OnAttemptingConnectEventData &) { - fprintf(stdout, "Mqtt5 Client attempting connection...\n"); - }); - builder->WithClientDisconnectionCallback([&disconnectPromise](const Mqtt5::OnDisconnectionEventData &eventData) { - fprintf(stdout, "Mqtt5 Client disconnection with reason: %s.\n", aws_error_debug_str(eventData.errorCode)); - disconnectPromise.set_value(); - }); - - // Create Mqtt5Client - std::shared_ptr client = builder->Build(); - delete builder; - /************************ Run the sample ****************************/ - - fprintf(stdout, "Connecting...\n"); - if (!client->Start()) - { - fprintf(stderr, "MQTT5 Connection failed to start"); - exit(-1); - } - - if (!connectionPromise.get_future().get()) - { - return -1; - } - IotIdentityClient identityClient(client); - - std::promise registerPublishPubAckCompletedPromise; - std::promise registerAcceptedSubAckCompletedPromise; - std::promise registerRejectedSubAckCompletedPromise; - std::promise registerAcceptedCompletedPromise; - auto onRegisterAcceptedSubAck = [&](int ioErr) { if (ioErr != AWS_OP_SUCCESS) { fprintf(stderr, "Error subscribing to RegisterThing accepted: %s\n", ErrorDebugString(ioErr)); exit(-1); } - - registerAcceptedSubAckCompletedPromise.set_value(); + ctx.acceptedSubAckPromise.set_value(); }; auto onRegisterRejectedSubAck = [&](int ioErr) { @@ -343,14 +341,14 @@ int main(int argc, char *argv[]) fprintf(stderr, "Error subscribing to RegisterThing rejected: %s\n", ErrorDebugString(ioErr)); exit(-1); } - registerRejectedSubAckCompletedPromise.set_value(); + ctx.rejectedSubAckPromise.set_value(); }; auto onRegisterAccepted = [&](RegisterThingResponse *response, int ioErr) { if (ioErr == AWS_OP_SUCCESS) { fprintf(stdout, "RegisterThingResponse ThingName: %s.\n", response->ThingName->c_str()); - registerAcceptedCompletedPromise.set_value(); + ctx.thingCreatedPromise.set_value(); } else { @@ -382,22 +380,9 @@ int main(int argc, char *argv[]) fprintf(stderr, "Error publishing to RegisterThing: %s\n", ErrorDebugString(ioErr)); exit(-1); } - - registerPublishPubAckCompletedPromise.set_value(); + ctx.pubAckPromise.set_value(); }; - // Create certificate. - CreateCertificateContext certificateContext; - if (csrFile.empty()) - { - useKeysAndCertificate(identityClient, certificateContext); - } - else - { - useCsr(identityClient, certificateContext, csrFile); - } - - // After certificate is obtained, it's time to register a thing. fprintf(stdout, "Subscribing to RegisterThing Accepted and Rejected topics\n"); RegisterThingSubscriptionRequest registerSubscriptionRequest; registerSubscriptionRequest.TemplateName = cmdData.input_templateName; @@ -409,8 +394,8 @@ int main(int argc, char *argv[]) registerSubscriptionRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterRejected, onRegisterRejectedSubAck); // Wait for the subscriptions to the accept and reject RegisterThing topics to be established. - registerAcceptedSubAckCompletedPromise.get_future().wait(); - registerRejectedSubAckCompletedPromise.get_future().wait(); + ctx.acceptedSubAckPromise.get_future().wait(); + ctx.rejectedSubAckPromise.get_future().wait(); fprintf(stdout, "Publishing to RegisterThing topic\n"); RegisterThingRequest registerThingRequest; @@ -429,18 +414,69 @@ int main(int argc, char *argv[]) registerThingRequest.Parameters = params; // NOTE: In a real application creating multiple certificates you'll probably need to protect token var with // a critical section. This sample makes only one request for a certificate, so no data race is possible. - registerThingRequest.CertificateOwnershipToken = certificateContext.token; + registerThingRequest.CertificateOwnershipToken = token; identityClient.PublishRegisterThing(registerThingRequest, AWS_MQTT_QOS_AT_LEAST_ONCE, onRegisterPublishPubAck); - registerPublishPubAckCompletedPromise.get_future().wait(); + ctx.pubAckPromise.get_future().wait(); // Wait for registering a thing to succeed. - registerAcceptedCompletedPromise.get_future().wait(); + ctx.thingCreatedPromise.get_future().wait(); +} + +int main(int argc, char *argv[]) +{ + /************************ Setup ****************************/ + + // Do the global initialization for the API + ApiHandle apiHandle; + + /** + * cmdData is the arguments/input from the command line placed into a single struct for + * use in this sample. This handles all of the command line parsing, validating, etc. + * See the Utils/CommandLineUtils for more information. + */ + Utils::cmdData cmdData = Utils::parseSampleInputFleetProvisioning(argc, argv, &apiHandle); + + Mqtt5ClientContext mqtt5ClientContext; + auto client = createMqtt5Client(mqtt5ClientContext, cmdData); + + /************************ Run the sample ****************************/ + + fprintf(stdout, "Connecting...\n"); + if (!client->Start()) + { + fprintf(stderr, "MQTT5 Connection failed to start"); + exit(-1); + } + + if (!mqtt5ClientContext.connectionPromise.get_future().get()) + { + return -1; + } + + // Create fleet provisioning client. + IotIdentityClient identityClient(client); + + // Create certificate. + CreateCertificateContext certificateContext; + if (cmdData.input_csrPath != "") + { + auto csrFile = getFileData(cmdData.input_csrPath); + createCertificateFromCsr(identityClient, certificateContext, csrFile); + } + else + { + createKeysAndCertificate(identityClient, certificateContext); + } + + // After certificate is obtained, it's time to register a thing. + RegisterThingContext registerThingContext; + registerThing(identityClient, registerThingContext, cmdData, certificateContext.token); // Disconnect if (client->Stop()) { - stoppedPromise.get_future().wait(); + mqtt5ClientContext.stoppedPromise.get_future().wait(); } return 0;