Skip to main content

Expansion

In the previous step, we created a single metric whose value remains constant after being set. While functional, it's not particularly exciting or suitable for fully utilizing your plugin's potential.

In this section, we will adapt and expand our plugin to handle multiple metrics and update them periodically.


Refactor

To improve the structure of our plugin, we will:

  1. Refactor: Separate the metric creation and registration process from the logic responsible for updating their values.
  2. Enhance: Replace the placeholder metric with more meaningful ones and add two additional metrics for a richer feature set.

Below is the refactored code:

Plugin.Example/Plugin.cs
using System.Runtime.InteropServices;
using MoBro.Plugin.SDK;
using MoBro.Plugin.SDK.Builders;
using MoBro.Plugin.SDK.Enums;
using MoBro.Plugin.SDK.Services;

namespace Plugin.Example;

public class Plugin : IMoBroPlugin
{
private readonly IMoBroService _mobro;

public Plugin(IMoBroService mobro)
{
_mobro = mobro;
}

public void Init()
{
// Create and register all metrics
CreateAndRegisterMetrics();

// Update the values of all metrics
UpdateMetricValues();
}

private void CreateAndRegisterMetrics()
{
// Dynamic metric: e.g., CPU usage, temperature, etc.
var cpuUsage = MoBroItem
.CreateMetric()
.WithId("cpu_usage")
.WithLabel("CPU Usage")
.OfType(CoreMetricType.Usage)
.OfCategory(CoreCategory.Cpu)
.OfNoGroup()
.Build();

var memoryInUse = MoBroItem
.CreateMetric()
.WithId("ram_in_use")
.WithLabel("Memory In Use")
.OfType(CoreMetricType.Data)
.OfCategory(CoreCategory.Ram)
.OfNoGroup()
.Build();

// Static metric: e.g., Operating System details, CPU model name
var osName = MoBroItem
.CreateMetric()
.WithId("os_name")
.WithLabel("Operating System")
.OfType(CoreMetricType.Text)
.OfCategory(CoreCategory.System)
.OfNoGroup()
.AsStaticValue()
.Build();

// Register the metrics with MoBro
_mobro.Register(cpuUsage);
_mobro.Register(memoryInUse);
_mobro.Register(osName);
}

private void UpdateMetricValues()
{
_mobro.UpdateMetricValue("os_name", RuntimeInformation.OSDescription);
_mobro.UpdateMetricValue("cpu_usage", 0); // Placeholder value
_mobro.UpdateMetricValue("ram_in_use", 1000); // Placeholder value
}
}

Key Changes

  1. We now define and register three meaningful metrics: CPU Usage, Memory In Use, and Operating System. These metrics are better suited for real-world use cases.
  2. The metric types and categories have been updated accordingly (refer to the Metric Reference for additional details).
  3. Instead of initializing metrics in the constructor, we moved the logic to an Init function. This function is automatically invoked exactly once after the plugin instance has been created (refer to In-depth: IMoBroPlugin for more information).
tip

Although creating and updating metrics directly in the constructor would work, it is better practice to keep plugin initialization and business logic (e.g., metric creation and updates) separate. This approach improves the readability and maintainability of your code.

Expected Output

The refactored plugin provides the metrics CPU Usage, Memory In Use, and Operating System, producing output like this:

PS C:\dev\mobro-data-plugins\Plugin.Example> dotnet run
01:17:22.500 [INF] Creating new plugin instance
01:17:22.523 [INF] Invoking 'init' function on plugin
01:17:22.557 [DBG] Registered Metric: cpu_usage
01:17:22.558 [DBG] Registered Metric: ram_in_use
01:17:22.559 [DBG] Registered Metric: os_name
01:17:22.564 [DBG] Value of metric os_name updated to: Microsoft Windows 10.0.19045
01:17:22.567 [DBG] Value of metric cpu_usage updated to: 0
01:17:22.569 [DBG] Value of metric ram_in_use updated to: 1000

We're now setting the values for all our metrics.
However, these values currently remain static after their initial assignment. Let's make them dynamic!


Scheduling Updates

While setting the value for the 'Operating System' metric once is sufficient (as this value does not change), we need to continuously update the 'CPU Usage' and 'Memory Usage' metrics to ensure they always reflect the most current values.

The simplest way to achieve this is by using a scheduler to periodically invoke an update function for our dynamic metrics every few seconds.

Since this is a common use case, the SDK provides a built-in scheduler. To use it, we simply inject the IMoBroScheduler service into the constructor. (For more details, refer to Reference: Scheduler).

Instead of calling the UpdateMetricValues function only once in the Init method, we will schedule it to run at regular intervals. Additionally, since the 'Operating System' metric does not need periodic updates, we will handle it in a separate function.

Plugin.Example/Plugin.cs
public class Plugin : IMoBroPlugin
{
private readonly IMoBroService _mobro;
private readonly IMoBroScheduler _scheduler;
private readonly Random _random;

public Plugin(IMoBroService mobro, IMoBroScheduler scheduler)
{
_mobro = mobro;
_scheduler = scheduler;
_random = new Random();
}

public void Init()
{
// create and register all metrics
CreateAndRegisterMetrics();

// set the value for the static metrics
SetStaticMetricValues();

// schedule a recurring task to update the dynamic metrics
_scheduler.Interval(UpdateDynamicMetrics, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(5));
}

[...]

private void SetStaticMetricValues()
{
// since this value won't change over time, it's sufficient to set it once
_mobro.UpdateMetricValue("os_name", RuntimeInformation.OSDescription);
}

private void UpdateDynamicMetrics()
{
// update the changing dynamic metrics
// normally we would retrieve the new value first by e.g. reading a sensor or calling an external API
_mobro.UpdateMetricValue("cpu_usage", _random.NextDouble() * 100);
_mobro.UpdateMetricValue("ram_in_use", _random.NextDouble() * 1_000_000_000);
}

[...]
}
tip

If your plugin provides metrics whose values are guaranteed not to change until the next system reboot, always create them using .AsStaticValue() and set/update the value only once.

In this case, our SetStaticMetricValues function will be called only once during plugin initialization, as it is sufficient to set the value just once (since it will not change).

Meanwhile, our UpdateDynamicMetrics function will be called first after an initial delay of 5 seconds and then periodically every 2 seconds, as long as the plugin is running (or until it is paused).

To simulate changing metric values, we are returning a random value for now. In a real-world scenario, you would typically retrieve the actual sensor value at this point and return the data.

Metric Value Requirements

  • CPU Usage Metric: Since this metric is of type CoreMetricType.Usage, you must return a percentage value in the range of 0 to 100.
  • Memory Metric: For memory metrics of type CoreMetricType.Data, you must return the data value in bytes.

For more details on metric types, refer to the Reference: Metrics documentation.

Expected Output

Running the plugin now should produce the following results:

PS C:\dev\mobro-data-plugins\Plugin.Example> dotnet run
01:24:34.364 [INF] Creating new plugin instance
01:24:34.383 [INF] Invoking 'init' function on plugin
01:24:34.413 [DBG] Registered Metric: cpu_usage
01:24:34.415 [DBG] Registered Metric: ram_in_use
01:24:34.416 [DBG] Registered Metric: os_name
01:24:34.419 [DBG] Value of metric os_name updated to: Microsoft Windows 10.0.19045
01:24:34.424 [DBG] Scheduling 'interval' job for interval 00:00:02 with a delay of 00:00:05
01:24:34.527 [DBG] Starting scheduler
01:24:39.550 [DBG] Value of metric cpu_usage updated to: 39.63311068798713
01:24:39.551 [DBG] Value of metric ram_in_use updated to: 892389951.7236712
01:24:41.536 [DBG] Value of metric cpu_usage updated to: 2.415082483011388
01:24:41.537 [DBG] Value of metric ram_in_use updated to: 123213804.21178019
01:24:43.535 [DBG] Value of metric cpu_usage updated to: 67.0902048404516
01:24:43.536 [DBG] Value of metric ram_in_use updated to: 220452476.57903522

Success!
Our metric values are finally updating dynamically.


Settings

Currently, the metric values are updated every two seconds.
While this default interval works well, different users may have different preferences. For instance, some users might prefer shorter update intervals for real-time precision, whereas others may favor longer intervals to minimize performance overhead caused by frequent polling of sensors.

Instead of enforcing a fixed interval, we can make the update frequency configurable. This allows users to choose an interval that suits their needs while balancing up-to-date metrics with system performance considerations.

Adding a Configurable Setting

To make the interval customizable, we will add a setting to our plugin by modifying the settings array inside the mobro_plugin_config.json file as follows:

Plugin.Example/mobro_plugin_config.json
{
"settings": [
{
"type": "number",
"name": "update_frequency",
"label": "Update Frequency",
"description": "Frequency at which metric values are updated",
"required": true,
"defaultValue": 2,
"min": 1
}
]
}

Explanation:

  • We added a setting of type number to control the update frequency.
  • It's marked as required, ensuring the user provides a value.
  • A sensible default value of 2 seconds is provided.
  • The minimum allowed value is set to 1. While we could set a maximum value, it isn't essential in this case.
Best Practice

Always provide sensible default values for plugin settings whenever possible. This ensures the plugin can function immediately after installation without forcing users to configure settings first.


What Happens Next?

Once this setting is added:

  • MoBro will automatically handle exposing the setting to users, validating the input, and persisting it.
  • With this, users can easily customize their update intervals via the plugin settings.

Updating the Scheduler

Next, we'll modify our plugin to adapt the scheduler's interval based on the user-defined setting. We will achieve this by:

  1. Injecting the IMoBroSettings service in the constructor.
  2. Retrieving the current value of the setting in the Init function.
Plugin.Example/Plugin.cs
public class Plugin : IMoBroPlugin
{
private readonly IMoBroService _mobro;
private readonly IMoBroScheduler _scheduler;
private readonly IMoBroSettings _settings;
private readonly Random _random;

public Plugin(IMoBroService mobro, IMoBroScheduler scheduler, IMoBroSettings settings)
{
_mobro = mobro;
_scheduler = scheduler;
_settings = settings;
_random = new Random();
}

public void Init()
{
// create and register all metrics
CreateAndRegisterMetrics();

// set the value for the static metrics
SetStaticMetricValues();

// get the value of the setting for the update frequency
var updateFrequencySetting = _settings.GetValue<int>("update_frequency");

// schedule a recurring task to update the dynamic metrics
_scheduler.Interval(UpdateDynamicMetrics, TimeSpan.FromSeconds(updateFrequencySetting), TimeSpan.FromSeconds(5));
}

[...]
}

To retrieve the value for a setting, simply call the GetValue method on the IMoBroSettings service and pass in the name defined in mobro_plugin_config.json.

For more details about the IMoBroSettings service, refer to Reference: Settings.

Since we are testing the plugin locally, there won't be any users to configure the settings. Therefore, we need to update the Program.cs file and provide a value for the setting manually:

Plugin.Example/Program.cs
using MoBro.Plugin.SDK;

// create and start the plugin to test it locally
var plugin = MoBroPluginBuilder
.Create<Plugin.Example.Plugin>()
.WithSetting("update_frequency", "1")
.Build();

// prevent the program from exiting immediately
Console.ReadLine();

When we run our plugin again, the metrics should now update every second.
This approach also provides a simple way to test different settings, so feel free to experiment with a variety of values.

Now, let's take a look at the output:

PS C:\dev\mobro-data-plugins\Plugin.Example> dotnet run
01:28:51.975 [INF] Creating new plugin instance
01:28:51.994 [INF] Invoking 'init' function on plugin
01:28:52.028 [DBG] Registered Metric: cpu_usage
01:28:52.030 [DBG] Registered Metric: ram_in_use
01:28:52.032 [DBG] Registered Metric: os_name
01:28:52.043 [DBG] Value of metric os_name updated to: Microsoft Windows 10.0.19045
01:28:52.048 [DBG] Scheduling 'interval' job for interval 00:00:01 with a delay of 00:00:05
01:28:52.170 [DBG] Starting scheduler
01:28:57.193 [DBG] Value of metric cpu_usage updated to: 18.314520086015506
01:28:57.194 [DBG] Value of metric ram_in_use updated to: 432407624.6621394
01:28:58.179 [DBG] Value of metric cpu_usage updated to: 74.36047971197121
01:28:58.181 [DBG] Value of metric ram_in_use updated to: 117872170.93206614

Success!
The metric update frequency can now be configured by the user.