Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions cpp/include/gg/ipc/client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ class ConfigurationUpdateCallback {
) = 0;
};

class ConnectionStatusCallback {
public:
virtual ~ConnectionStatusCallback() noexcept = default;
virtual void operator()(bool connected, Subscription &handle) = 0;
};

class Client {
private:
constexpr Client() noexcept = default;
Expand Down Expand Up @@ -177,6 +183,16 @@ class Client {
Subscription *handle = nullptr
) noexcept;

/// Subscribe to IoT Core MQTT connection status changes.
/// The callback receives the current connection status immediately after
/// subscribing, then on each subsequent CONNECTED/DISCONNECTED transition.
/// No accessControl authorization policy is required for this operation.
/// See:
/// <https://docs.aws.amazon.com/greengrass/v2/developerguide/ipc-iot-core-mqtt.html>
std::error_code subscribe_to_iot_core_connection_status(
ConnectionStatusCallback &callback, Subscription *handle = nullptr
) noexcept;

/// Update the state of this component.
/// Reports component state to the Greengrass nucleus.
/// See:
Expand Down
40 changes: 40 additions & 0 deletions cpp/samples/subscribe_to_iot_core_connection_status.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

// Example: Subscribe to IoT Core MQTT connection status changes

#include <gg/ipc/client.hpp>
#include <unistd.h>
#include <iostream>

class ConnectionStatusHandler : public gg::ipc::ConnectionStatusCallback {
void operator()(bool connected, gg::ipc::Subscription &handle) override {
(void) handle;
std::cout << "IoT Core connection status changed: "
<< (connected ? "CONNECTED" : "DISCONNECTED") << "\n";
}
};

int main() {
auto &client = gg::ipc::Client::get();

auto error = client.connect();
if (error) {
std::cerr << "Failed to establish IPC connection.\n";
exit(-1);
}

static ConnectionStatusHandler handler;
error = client.subscribe_to_iot_core_connection_status(handler);
if (error) {
std::cerr << "Failed to subscribe to IoT Core connection status.\n";
exit(-1);
}

std::cout << "Successfully subscribed to IoT Core connection status.\n";

// Keep the main thread alive, or the process will exit.
while (1) {
sleep(10);
}
}
26 changes: 26 additions & 0 deletions cpp/samples/subscribe_to_iot_core_connection_status.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"RecipeFormatVersion": "2020-01-25",
"ComponentName": "com.example.IoTCoreConnectionStatusCpp",
"ComponentVersion": "1.0.0",
"ComponentDescription": "A component that subscribes to IoT Core MQTT connection status changes. No accessControl authorization policy is required for this operation.",
"ComponentPublisher": "Amazon",
"Manifests": [
{
"Platform": {
"os": "linux",
"runtime": "*"
},
"Lifecycle": {
"run": "{artifacts:path}/sample_cpp_subscribe_to_iot_core_connection_status"
},
"Artifacts": [
{
"URI": "s3://amzn-s3-demo-bucket/artifacts/com.example.IoTCoreConnectionStatusCpp/1.0.0/sample_cpp_subscribe_to_iot_core_connection_status",
"Permission": {
"Execute": "OWNER"
}
}
]
}
]
}
60 changes: 60 additions & 0 deletions cpp/src/ipc/subscribe_to_iot_core_connection_status.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// aws-greengrass-component-sdk - Lightweight AWS IoT Greengrass SDK
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

#include <gg/error.hpp>
#include <gg/ipc/client.hpp>
#include <gg/ipc/subscription.hpp>
#include <exception>
#include <functional>
#include <iostream>
#include <source_location>
#include <system_error>

extern "C" {
#include <gg/ipc/client.h>
}

namespace gg::ipc {
extern "C" {
namespace {
void subscribe_to_iot_core_connection_status_callback(
void *ctx, bool connected, GgIpcSubscriptionHandle handle
) noexcept try {
Subscription locked { handle };
std::invoke(
*static_cast<ConnectionStatusCallback *>(ctx), connected, locked
);
(void) locked.release();
} catch (const std::exception &e) {
std::cerr << "Exception caught in "
<< std::source_location {}.function_name() << '\n'
<< e.what() << '\n';
} catch (...) {
std::cerr << "Exception caught in "
<< std::source_location {}.function_name() << '\n';
}
}
}

// singleton interface class.
// NOLINTBEGIN(readability-convert-member-functions-to-static)

std::error_code Client::subscribe_to_iot_core_connection_status(
ConnectionStatusCallback &callback, Subscription *handle
) noexcept {
GgIpcSubscriptionHandle raw_handle;
GgError ret = ggipc_subscribe_to_iot_core_connection_status(
subscribe_to_iot_core_connection_status_callback,
&callback,
(handle != nullptr) ? &raw_handle : nullptr
);
if ((handle != nullptr) && (ret == GG_ERR_OK)) {
handle->reset(raw_handle);
}
return ret;
}

// NOLINTEND(readability-convert-member-functions-to-static)

}
26 changes: 26 additions & 0 deletions rust/examples/subscribe_to_iot_core_connection_status.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"RecipeFormatVersion": "2020-01-25",
"ComponentName": "com.example.IoTCoreConnectionStatusRust",
"ComponentVersion": "1.0.0",
"ComponentDescription": "A component that subscribes to IoT Core MQTT connection status changes. No accessControl authorization policy is required for this operation.",
"ComponentPublisher": "Amazon",
"Manifests": [
{
"Platform": {
"os": "linux",
"runtime": "*"
},
"Lifecycle": {
"run": "{artifacts:path}/subscribe_to_iot_core_connection_status"
},
"Artifacts": [
{
"URI": "s3://amzn-s3-demo-bucket/artifacts/com.example.IoTCoreConnectionStatusRust/1.0.0/subscribe_to_iot_core_connection_status",
"Permission": {
"Execute": "OWNER"
}
}
]
}
]
}
32 changes: 32 additions & 0 deletions rust/examples/subscribe_to_iot_core_connection_status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

// Example: Subscribe to IoT Core MQTT connection status changes

use gg_sdk::Sdk;
use std::{thread, time::Duration};

fn main() {
let sdk = Sdk::init();
sdk.connect().expect("Failed to establish IPC connection");

let callback = |connected: bool| {
let status = if connected {
"CONNECTED"
} else {
"DISCONNECTED"
};
println!("IoT Core connection status changed: {status}");
};

let _sub = sdk
.subscribe_to_iot_core_connection_status(&callback)
.expect("Failed to subscribe to IoT Core connection status");

println!("Successfully subscribed to IoT Core connection status.");

// Keep the main thread alive, or the process will exit.
loop {
thread::sleep(Duration::from_secs(10));
}
}
42 changes: 42 additions & 0 deletions rust/src/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,48 @@ impl Sdk {
})
}

/// Subscribe to IoT Core MQTT connection status changes.
///
/// The callback receives `true` when the nucleus is connected to AWS IoT
/// Core and `false` when disconnected. It is called with the current
/// connection status immediately after subscribing, then on each
/// subsequent CONNECTED/DISCONNECTED transition.
///
/// No accessControl authorization policy is required for this operation.
///
/// # Errors
/// Returns error if subscription fails.
pub fn subscribe_to_iot_core_connection_status<'a, F: Fn(bool)>(
&self,
callback: &'a F,
) -> Result<Subscription<'a, F>> {
extern "C" fn trampoline<F: Fn(bool)>(
ctx: *mut ffi::c_void,
connected: bool,
_handle: c::GgIpcSubscriptionHandle,
) {
let cb = unsafe { &*ctx.cast::<F>() };
cb(connected);
}

let ctx = ptr::from_ref(callback);
let mut handle = c::GgIpcSubscriptionHandle { val: 0 };

Result::from(unsafe {
c::ggipc_subscribe_to_iot_core_connection_status(
Some(trampoline::<F>),
ctx.cast::<ffi::c_void>().cast_mut(),
&raw mut handle,
)
})?;

debug_assert!(handle.val != 0);
Ok(Subscription {
handle,
phantom: PhantomData,
})
}

/// Update component state.
///
/// Reports component state to the Greengrass nucleus.
Expand Down
53 changes: 53 additions & 0 deletions samples/subscribe_to_iot_core_connection_status.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

// Example: Subscribe to IoT Core MQTT connection status changes

#include <gg/error.h>
#include <gg/ipc/client.h>
#include <gg/sdk.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

static void on_connection_status(
void *ctx, bool connected, GgIpcSubscriptionHandle handle
) {
(void) ctx;
(void) handle;

printf(
"IoT Core connection status changed: %s\n",
connected ? "CONNECTED" : "DISCONNECTED"
);
}

int main(void) {
gg_sdk_init();

GgError err = ggipc_connect();
if (err != GG_ERR_OK) {
fprintf(stderr, "Failed to establish IPC connection.\n");
exit(-1);
}

GgIpcSubscriptionHandle handle;
err = ggipc_subscribe_to_iot_core_connection_status(
on_connection_status, NULL, &handle
);
if (err != GG_ERR_OK) {
fprintf(stderr, "Failed to subscribe to IoT Core connection status.\n");
exit(-1);
}

printf("Successfully subscribed to IoT Core connection status.\n");

// Keep the main thread alive, or the process will exit.
while (1) {
sleep(10);
}

// To stop subscribing, close the subscription handle.
ggipc_close_subscription(handle);
}
26 changes: 26 additions & 0 deletions samples/subscribe_to_iot_core_connection_status.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"RecipeFormatVersion": "2020-01-25",
"ComponentName": "com.example.IoTCoreConnectionStatusC",
"ComponentVersion": "1.0.0",
"ComponentDescription": "A component that subscribes to IoT Core MQTT connection status changes. No accessControl authorization policy is required for this operation.",
"ComponentPublisher": "Amazon",
"Manifests": [
{
"Platform": {
"os": "linux",
"runtime": "*"
},
"Lifecycle": {
"run": "{artifacts:path}/sample_subscribe_to_iot_core_connection_status"
},
"Artifacts": [
{
"URI": "s3://amzn-s3-demo-bucket/artifacts/com.example.IoTCoreConnectionStatusC/1.0.0/sample_subscribe_to_iot_core_connection_status",
"Permission": {
"Execute": "OWNER"
}
}
]
}
]
}
Loading