Skip to main content

General

Writing User Functions

Extend platform logic by writing custom C# scripts that run directly on the Edge Gateway.

While the Rule Engine handles standard automation tasks, some scenarios require complex calculation, state management, or custom protocol parsing. For these cases, Proxus allows you to deploy compiled C# Functions directly to the Edge Gateway.

Deployment & Scope

Unlike Devices which are bound to a single Gateway, Functions can be deployed to multiple Gateways simultaneously.

  • Specific Assignment: You can select specific Edge Gateways to run the function.
  • Default (SYSTEM): When creating a new function, the SYSTEM gateway is selected automatically by default. You can add more gateways or change this assignment as needed.

Function Architecture

User functions are independent C# classes that inherit from the Proxus.SDK.BaseFunctions.FunctionBase class. They run inside a sandboxed environment on the Gateway, ensuring that a crashing script does not take down the entire system.

Basic Structure

Every function must override the OnMessageReceive method. This method is triggered asynchronously for every data packet passing through the Gateway.

using System;
using System.Threading.Tasks;
using Proxus.SDK.BaseFunctions;
using Proxus.Common.Messages;

public class MyCustomLogic : FunctionBase
{
    // Called once when the function starts
    protected override void OnStarted()
    {
        LogInformation("MyCustomLogic function started.");
        // Optional: Set up a recurring task
        ExecuteScheduledTask(TimeSpan.FromSeconds(30), () =>
        {
            LogInformation("Periodic check running...");
        });
    }

    // Called for every telemetry packet
    protected override void OnMessageReceive(FunctionContext ctx)
    {
        // 1. Check if the message is TransportData
        if (ctx.Message is TransportData data)
        {
            // 2. Filter: Only process specific topics or devices
            if (ctx.Topic != "Gen_01")
            {
                return;
            }

            // 3. Extract Value (Helper method in your logic or manual parsing)
            // Note: TransportData payload is a list of key-value pairs
            var tempMetric = data.Payload.FirstOrDefault(p => p.Key == "Temperature");

            if (tempMetric != null && double.TryParse(tempMetric.Value, out double temp))
            {
                // 4. Logic: Calculate efficiency
                double efficiency = (temp / 120.0) * 100;

                // 5. Action: Save result back to pipeline
                if (efficiency < 50)
                {
                    LogWarning($"Low Efficiency Detected: {efficiency:F2}%");

                    // Create new data packet
                    var resultData = new TransportData();
                    resultData.MetaData.Add(new MetaData { Key = "DeviceName", Value = "Gen_01_Calc" });
                    resultData.Payload.Add(new Payload
                    {
                        Key = "Efficiency",
                        Value = efficiency.ToString(),
                        Type = "Double"
                    });

                    // Send back to system
                    Save(resultData);
                }
            }
        }
    }
}

SDK Capabilities

The FunctionBase class provides powerful methods to interact with the runtime.

Logging

Use strongly-typed logging methods. These logs appear in the centralized System Logs.

MethodDescription
LogInformation(string)Standard info log.
LogWarning(string)Warning log (yellow).
LogError(string)Error log (red).
LogDebug(string)Debug log (visible only when debug mode is enabled).

Data Operations

MethodDescription
Save(TransportData)Injects a new data point into the system pipeline or saves directly to DB.
SendNotification(channel, msg)Triggers a notification via a configured channel (e.g., "Email", "Slack").
UpdateDeviceStatus(id, status)Updates the status of a specific device in the system registry.

Advanced Features

1. Caching & State

Functions are stateful. You can use the built-in thread-safe Cache dictionary or class-level variables to maintain state between messages.

// Enable automatic cache cleanup (removes entries older than 1 hour)
EnableCacheWithExpirationTime = TimeSpan.FromHours(1);

// Add/Update cache
Cache.AddOrUpdate(DateTime.UtcNow, data, (k, v) => data);

2. Scheduling

You can schedule background tasks without blocking the main message loop.

// Run every 10 seconds
ExecuteScheduledTask(TimeSpan.FromSeconds(10), () =>
{
// Perform periodic calculation or cleanup
});

3. MQTT Client

The SDK includes a managed MQTT client for communicating with external brokers directly from your script.

// Publish to an external broker
PublishMqttMessage("external/topic", "payload");

// Subscribe to external topic (handled in OnMessageReceive)
SubscribeToMqttTopic("external/command");

Best Practices

  • Non-Blocking: OnMessageReceive should be fast. For long-running operations, use Task.Run or scheduled tasks.
  • Exception Handling: Unhandled exceptions are caught by the runtime, but frequent crashes may cause the function to be automatically undeployed (Threshold: 10 exceptions).
  • Dependencies: Stick to standard .NET libraries (System, System.Linq, System.Text.Json). External NuGet packages must be pre-installed on the Gateway.