Agora Edge Apps SDK
Overview
The Agora Edge Apps SDK is engineered to serve as the foundational layer for the development and deployment of edge applications. With a focus on facilitating key functionalities, the SDK accommodates:
Edge Data Processing - Optimized for low-latency computations, the SDK allows for data manipulation at the edge, minimizing the exigencies of data transport and enabling real-time operational decision-making.
Device Communication - Configured to permit bi-directional data flow between a multitude of devices and the edge platform, the SDK supports various hardware communication interfaces, including but not limited to Ethernet and Serial ports.
Broker-Driven Interoperability - Incorporating a standardized message broker architecture at the edge, the SDK fosters a unified environment that significantly eases the integration challenges posed by heterogeneous edge applications.
By providing a robust and standardized framework, the Agora Edge Apps SDK aims to accelerate the development cycle, while ensuring system integrity and scalability. It serves as a pivotal element for developers tasked with the assembly of reliable and cohesive edge computing solutions.
The SDK provides the minimal set of features that allows an application to be developed in either .NET or Python while providing a consistent, documented set of features for developers to use.
Core Functionalities of the Agora Edge Apps SDK
The Agora Edge Apps SDK encompasses a suite of core functionalities essential for the development and management of applications on edge devices. These functionalities are engineered to support complex operational requirements and facilitate seamless inter-application and cloud communication:
Application Configuration: This module provides a robust configuration management system that enables dynamic application settings at runtime. It leverages a hierarchy of configuration sources, including defaults, environmental variables, command-line arguments, and external configuration files. This system supports operational agility by allowing runtime adjustments and prioritized configuration overlays.
Messaging Framework: Central to the SDK is an advanced messaging infrastructure designed to enable efficient and reliable communication between edge applications and between edge and cloud systems. This framework utilizes MQTT for message brokering, supporting both data and command propagation across distributed components with high reliability and low latency.
File Transfer Management: This functionality integrates comprehensive file handling capabilities to manage data transfers between the cloud and edge devices. It includes mechanisms for both inbound and outbound data flows, ensuring data consistency and security during file transfer operations. The system supports scheduled and triggered transfers, accommodating a variety of operational scenarios.
Digital Twins: The SDK supports the concept of Digital Twins by synchronizing properties representing digital twins between the Cloud and the Edge. This ensures that the digital representation of physical devices remains accurate and up-to-date, reflecting the current state of edge devices in real-time.
For a guided introduction to use the SDK, refer to the Tutorials (SDK Tutorial).
Extra components for working with the Agora and the SDK are also included or available during app development or when the app is deployed on a gateway:
- Development Tools
- CSVReader - Reads CSV files and publishes data messages.
- CSVWriter - Listens for data messages and publishes tags specified in configuration file to csv file.
- Mosquitto - Publicly available MQTT Broker useful during development providing an MQTT server for transferring messages between local applications.
- Core Components
- Agora Edge App Broker - The broker used on the gateway providing application level authentication/authorization as well as message routing.
- OpsAgent - The AgoraOps Agent Primarily used to push alternate configurations from Cloud to Edge App/s running on gateway.
- FileManager - AEA File Manager handles file transfer for AEA applications to/from the cloud as well hosts files for sharing at the edge.
- Passthrough handler - The application makes it easy to send message through Cloud by converting the message into a special type of message that allows messages to go through, untouched, to a webhook registered to Cloud.
- Redis Server - A continuous backend data store for Edge API Gateway Module which is a webapp hosted on gateway.
- AEA Unit Converter - Application designed to facilitate the conversion of tag data from various unit systems to a canonical system based on International System of Units (SI Base)
- Edge Web Services
- Edge Application Gateway (EAG)
- Edge Device Communication Modules
- Modbus Module (HBM)
- Enron Modbus Module
- OPC UA Client
- OPC UA Server
- WITS Receiver
- WITSML Client
- WITSML Server
- Northbound Adapters
- Agora Cloud Adapter
- Agora On-prem Adapter
- Avalon Adapter
Nomenclature for Variables and Tags Involving Units
Importance of Clarity in Unit Specification
When defining variables and tag names that include units, it's crucial to ensure the context and unit of the value are immediately clear. Ambiguities in units can lead to significant debugging challenges, particularly during live application troubleshooting, resulting in costly errors.
Historical Approach and Its Challenges
Agora initially adopted a laissez-faire approach regarding unit specification on tags and variables, assuming SI units for unspecified units. This assumption led to issues when integrating legacy code or when developers presumed SI units by default, causing failures upon deployment.
Current Best Practices
- External Developers: It's recommended to explicitly include units in tags or variable names.
- Internal Development and Operations: Explicit unit inclusion is mandatory.
Unit Specification
Agora endorses the SI unit system for inter-application data exchange, emphasizing clarity over strict naming conventions. For example, the unit of time can be denoted as 's', 'sec', or 'seconds'. We do prefer the use of the following for SI Base Units (bold) as well as these suggested alternatives for units:
- Time - seconds (s or sec), minutes (min), hour (hr)
- Distance - meter (m), feet (ft)
- Area - square meters (m2), square feet (ft2), square inches (in2)
- Volume - cubic meters (m3), Gallons (gal), Liters (L), barrels of oil (bbl)
- Mass - kilogram (kg), pounds mass (lbm)
- Temperature - Celsius (degC), Farenheight (degF), Kelvin (degK)
- Current - ampere (A)
- Voltage - Volts (V)
- Resistance - Ohms (ohms)
- Inductance - Henry (H)
- Capacitance - Farad (F)
- Energy, Work, Heat - Joule (J)
- Force - Newton (N), pounds (lbs)
- Pressure, Stress - Pascal (Pa), pounds per square inch (psi)
- Magnetic Flux - Weber (Wb)
- Magnetic Flux Density - Tesla (T)
- Plane Angle - radians (rad), degrees (deg)
To represent percent and decimal portions:
- Percent (pct), e.g. 50.0 = 50%
- Decimal percent (dec), e.g. 0.50 = 50%
To represent cycles/revolutions/frequency:
- Frequency / Cycles per second - Hertz (Hz)
- Rotations per minute (rpm)
- Cycles per minutes (cpm)
Example Tag Name (Camel Case):
- Good -
WellheadPressure_psi
- Wellhead Pressure with clear indication of pounds per square inch (psi). - Good -
BPOS_m
- Block position with clear indication of meters (m). - Bad -
WellheadPressure
- Wellhead Pressure assumed to be pascals (Pa), but confusing and error prone.
Example C# Variable Names (Camel Case):
- Good -
WellheadPressure_psi
- Class variable representing Wellhead Pressure with clear indication of pounds per square inch (psi). - Good -
wellheadPressure_Pa
- Local variable (first letter is lower case) representing Wellhead Pressure with clear indication of Pascals (Pa). - Bad -
WellheadPressure
- Class variable representing Wellhead Pressure assumed to be pascals (Pa), but confusing and error prone.
Example Python Variable Names (defined by PSE):
- Good -
self.wellhead_pressure_psi
- Class variable representing Wellhead Pressure with clear indication of pounds per square inch (psi). - Good -
wellhead_pressure_Pa
- Local variable representing Wellhead Pressure with clear indiciation of pascals (Pa). - Bad -
self.wellhead_pressure
- Class variable representing Wellhead Pressure assumed to be pascals (Pa), but confusing and error prone.
Handling Raw Values with Ambiguous Units
In scenarios where equipment or external systems provide values in configurable units, it's vital to indicate ambiguity by including '_raw' in the tag name, signaling that the value requires special handling.
Example Conversion in Code:
volume_variable_m3 = float('nan')
if Tag['UnitSystem'] == 'METRIC':
# No conversion needed if tag is in cubic meters
volume_variable_m3 = Tag['VolumeVariable_raw']
else:
# Conversion required if tag is in cubic feet
volume_variable_m3 = Tag['VolumeVariable_raw'] * 0.02831685
Additionally, Agora also adopts the following to reprecent percent and decimal portions.
- percent (pct), e.g. 50.0 = 50%
- decimal percent (dec), e.g. 0.50 = 50%
Important
To minimize potential errors and confusion, explicitly specify units in variable and tag names, and provide clear documentation on the unit system used. This practice facilitates easier maintenance, debugging, and integration of code, especially in complex or legacy systems.
Timestamps
To ensure consistency and ease of communication between apps, all timestamps in the Agora ecosystem should be expressed as the number of milliseconds since January 1, 1970 (UTC), represented as a double. Developers can utilize the AgoraTimeStamp function within the SDKs to obtain this value. Unless otherwise specified in the variable name, this should be the default format for timestamps in Agora Edge Apps.
Note
For Python, it is discouraged to try to use the default Python libraries to get unix epoch because it will return the shifted number of seconds relative to the local time zone. The AgoraTimeStamp within the SDK always returns the number of milliseconds relative to an exact point in time, which is not subject to a local timezone.
Deployment
When deployed, Agora Edge Apps are executed within containers to make sure the protection of both the application and the rest of the edge system from potential damage. As part of a deployment, each application is provided several shared volumes that allow it to be configured remotely and to exchange files with the cloud and local users.
To distribute containers to the edge, container registries are utilized. For commercial deployments, containers are placed in the Agora private container registry. Each container in this registry undergoes security scanning by AgoraOps personnel to make sure that any security or quality issues are resolved before inclusion. Additionally, all Agora Edge Application containers must be well-documented and comply with the configuration and file input/output methodology.
On the gateway, Agora Edge Applications are deployed as a group and communicate through the use of messages via a message broker.
Similarly, gateways are deployed across the world and communicate with Agora Cloud or Agora On-Prem using messaging. Gateways DO NOT communicate with one-another via the MQTT IoT Hub.
Gateways communicate to other systems at the edge using physical connections, which are allocated to specific containers.
For example, a container may be provided access to a serial port to communicate with a WITS Server or allocated a TCP port
to provide a Modbus Slave endpoint. This allows clear and secure communication between the gateway and other systems at the
edge, and ensures that each container is only able to access the resources it needs to function.
Configuration
The Agora Edge Apps SDKs provide a common method of sharing configuration across an application and with AgoraOps. Configurations are built at runtime using several sources, in the following order:
- Defaults - In Memory Config provided by app and SDK defaults.
AEA.json
- Internal configuration file, a.k.a. Primary Configuration.- Environment variables prepended with "AEA__"
- Command-line arguments
./config/AEA.json
- Alternate Configuration (optional) - Allows remote configuration by AgoraOps. During development, one typically does not use this../config/keys/{Key}
- Key-Per-File: if {Key} file is specified, its value is the file contents (optional) - Allows remote configuration by AgoraOps. During development, one typically does not use this.- Overrides - Overridden configuration values programmatically set by the application itself.
Note
Since environment variables and files (for Key-Per-File) cannot contain ':', a double underline '__' is used instead, ex. AEA2__LogLevel
for key-file name, and AEA__AEA2__LogLevel
for Environment Variable.
The AEA.json
files and Key-Per-File are monitored for changes (see Runtime Configuration Changes at run-time.
Alternate Configuration and Key-Per-File
The purpose of the alternate configuration file is to allow the module to be configured once while running as a container via shared volume. To set the alternate configuration via docker use a command similar to the following:
docker -v {shared config folder}:/{AppDir}
where the shared config folder contains:
{application.exe folder}/
config/AEA.json <-- the alternate configuration
config/keys/{Key-Per-File}
Within the config/keys
folder, the file and file contents are converted to key-value pairs within the configuration.
For example, the file AEA2__LogLevel
is the configuration setting 'key', and the files contents would be the configuration value.
Example: Contents of AEA2__LogLevel
file to specify Debug level of logging:
Debug
Caution
You cannot specify a hierarchy of settings nor arrays using Key-Per-File.
Well known Configuration Settings
Below are the set of well known Configuration Settings provided by default:
- Name - The Name of the App. This setting is used to set the MQTT Client Name for identifying the client to the Agora Edge Apps Broker. This is used for routing purposes. Default: Entry Assembly Name. Although "optional", it is recommended to specify the "Name" setting in the
AEA.json
configuration file. - DEVICE_ID - Device/Machine Id
- GROUP_ID - Group Id of the Gateway
- GATEWAY_ID - Gateway Id
Configurable Logging Settings
- AEA2:LogLevel (string) - [optional] One of ("Trace", "Debug", "Info" (default), "Off"). This setting is not case sensitive and provides the capability to specify the minimum level of log statements included in the log. This setting affects all Logging Targets contained by the Logger.
Configurable BusClient Settings
- AEA2:BusClient:Server (string): Server name hosting the MQTT Broker. Default:
localhost
. - AEA2:BusClient:Port (uint): Server Port for MQTT broker hosting the MQTT Broker. Default:
707
. - AEA2:BusClient:DeviceId (int): Default Device Id to use when publishing
DataOut
messages. Use this instead of setting Device Id programmatically to make application more configurable. - AEA2:BusClient:Subscriptions (string array): Array of string topics to subscribe. Routing is required to receive messages. Default:
<empty>
. Common topics include:DataIn
- Subscribes toDataIn
topic containing Io Data Reports.RequestIn
- Subscribes toRequestIn
topic containing Request Messages.
File Manager Settings
- AEA2:FileManager:SharedVolumeFolder (string): The folder used to interact with the FileManager.
Messaging
The BusClient and the Message Broker
The Agora Edge Apps SDKs include BusClients which are utilized to interact with an MQTT Message Broker. A BusClient is responsible for sending and receiving messages through the Broker.
When an application is deployed on the gateway, it will use the Agora Edge Apps Message Broker. Unlike a typical MQTT Broker, the Agora Edge Apps Message Broker manages the routing of messages between applications. For instance, AppA
might generate DataOut
messages which the operations team would like sent to AppB
. Using the routing capability, the Broker can be configured to route /AppA/DataOut TO /AppB/DataIn
, allowing for seamless communication and data transfer between different parts of the system.
Within the SDKs, the below topics are used by the BusClients:
- DataOut/In - Used for passing Data Messages (
IoDataReportMsg
). - RequestOut/In - Used for requesting other application services (
RequestMsg
). - EventOut/In - Used for publishing events/alerts (
EventMsg
).
Generic Topics are routed similarly, i.e. publishing of a custom topic is done using SendMessage. Ex. When sending a message with the topic "T1", the topic is converted in the broker to '/{Name}/T1' and routed to other topics using the routes specified in the broker's routes.json
configuration file.
Data and Request Messages
The BusClient sends Data Messages contained within IoDataReportMsg
, Request Messages contained within RequestMsg
, and custom messages (byte[]). Additionally, when data is received, the BusClient stores the messages in the BusClient Message Queues. Applications are expected to check and dequeue messages periodically. Currently, there is no mechanism provided that signals the app that a message has arrived.
Header
Within Data and Request messages, there is a 'header' element which contains information that allows the receiver to determine:
- SrcModule (string) - The Name of the originating sender module as specified in the configuration setting "Name".
- MessageID (Int32) - An integer used to uniquely identify the message.
- MessageType (string) - The type of message
- ConfigVersion (Int32) - The subtype of message (used for versioning the message schema)
- Timestamp (Int64) - Milliseconds since Jan. 1, 1970 (UTC) - AgoraTimeStamp
IoDataReportMsg
The IoDataReportMsg
is used for accessing DataIn
and creating DataOut
messages.
The following is an example of JSON Data Message that is generated by serializing an IoDataReportMsg:
Note
It is not required for the developer to serialize or deserialize the Data and Request messages. The SDK does this for you.
{
"header": {
"SrcModule": "Sender",
"MessageType": "IODataReport",
"ConfigVersion": 1,
"MessageID": 21556310,
"TimeStamp": 1674686831036
},
"device": [
{
"id": "18",
"tags": {
"HKLD_m": {
"value": 10,
"quality_code": 0,
"timestamp": 1674686831036
}
}
}
]
}
RequestMsg
Request messages allow applications to pass requests to other applications and to provide responses (as needed). A Request message allows a string command to be send, as well as a set of parameters as string key/value pairs. A correlation id header.MessageID is provided on both messages to allow the request sender to correlate a Request with a possible Response (also sent as a Request message), as required.
To use Request Messages, subscribe to RequestIn
. When Requests arrive, they are placed into the BusClient Message Queue as RequestMsg
.
The following are example JSON Request and Response Messages:
Example of Request Message
{
"header": {
"SrcModule": "Sender",
"MessageType": "RequestName",
"ConfigVersion": 1,
"MessageID": 21556311,
"TimeStamp": 1674686832593,
},
"payload": {
"Arg1": "Value1",
"Arg2": "Value2"
}
}
Example of Response Message for above Request
Note
MessageID is the same so that the request/response can be correlated.
{
"header": {
"SrcModule": "Receiver",
"MessageType": "RequestName_RSP",
"ConfigVersion": 1,
"MessageID": 21556311,
"TimeStamp": 1674686832649,
},
"payload": {
"RetVal1": "Value1",
"RetVal2": "Value2"
},
"response": {
"status": "SUCCESS"
}
}
EventMsg
In addition to Data and Request messages, the BusClient provides Event messages that allow applications to send and receive alerts/events.
To listen to Event Messages, subscribe to EventIn
topic. When Event arrive, they are placed into the BusClient's Event Message Queue as EventMsg
.
The following is an example JSON Event Message that is generated by serializing an EventMsg:
Example of Event Message
{
"EventId": 164544168,
"GroupId": "safoco",
"GatewayId": "b3s",
"SlaveId": 963,
"ControllerId": "714db0ee-a549-48e9-acda-a4c5b05787a7",
"Start_tm": 343434234.0,
"End_tm": 565654645.0,
"DetectedStart_tm": 10101010.0,
"DetectedEnd_tm": 20202020.0,
"Sent_tm": 34343456656.0,
"Created_tm": 979797979.0,
"Detected_tm": 868686868.0,
"mediaDataRef": [
{
"Type": "Image",
"Id": 164544168,
"ZoneId": "4",
"CameraId": "5",
"MotTrackerId": null,
"EdgeFilename": "arq-4-5-31192-1-Rigzone2_0.mpeg",
"MotEdgeFilename": "",
"MIMEType": "mpeg",
"AltText": "Entry Detection - Zone 4 - Camera 5 @ 1688985616525.5986",
"RawData": "",
"DetectedStart_tm": 0.0,
"DetectedEnd_tm": 0.0
}
],
"workFlow": {
"Type": "Rigzone2.0",
"Properties": {
"area1": "1",
"area2": "0"
}
},
"Version": "1.0.27"
}
File Transfers
A deployment of Agora Edge Apps provides the AEA_FileManager Application by default. This application is used to transfer files between the Cloud and the Applications on the Gateway.
To transfer files from your application to the Cloud, write files to the {fileManagerSharedVolume}/fileOut
folder.
Files beginning with '~' are ignored to allow writing files in place before sending.
Files received from the cloud will appear in the {fileManagerSharedVolume}/fileIn
folder.
For consistency, use the following configuration setting to set the Shared Volume Folder.
- AEA2:FileManager:SharedVolumeFolder (string): The folder used to interact with the FileManager.
On the Cloud, files can be transferred using the Nimbus File Manager Service API described at https://swagger.p4d.nimbus.slb-ds.com/swagger. An approved account is required to access the Cloud API documentation.
Digital Twins
The Agora Edge Apps SDK integrates the concept of Digital Twins, facilitating dynamic and continuous synchronization of properties representing digital twins between the Cloud and Edge environments. This synchronization ensures that the digital representation of physical devices remains accurate and up-to-date, reflecting the current state of edge devices in real-time.
Intended Usage:
Domain-Specific Workflows: Twin Properties are specifically designed to support domain-specific workflows rather than operational workflows such as application configuration. This focus ensures that the properties are used to enhance the core functionalities of the digital twin within its specific operational context.
Exclusion from Telemetry: Twin Properties are not intended for use in telemetry data handling. Their use is reserved for managing state information between the cloud and edge components, not for transmitting continuous or medium to high volume data streams.
Twin Property Groups:
Definition and Association: Twin Property Groups are named collections of properties defined in the cloud and associated with specific gateways within the Agora Ecosystem. These groups organize properties that collectively represent a digital twin.
Accessibility: All applications operating on a given gateway have access to the Twin Property Groups. This accessibility ensures that multiple applications can collaborate on maintaining and utilizing the digital twin, enhancing the robustness and functionality of both edge and cloud components.
Categories of Twin Properties:
Twin Properties within these groups are categorized into two types to facilitate effective synchronization and management:
Desired Twin Properties: These properties represent the values that the cloud component of a digital twin desires to set on the edge device. Desired properties dictate the intended state or configuration as determined by cloud-based analytics and management systems.
Reported Twin Properties: These are the actual values as reported by the edge device. They reflect the current state or outcomes from the edge, providing feedback to the cloud about the device’s operational status and any deviations from the desired configurations.
Synchronization Mechanism:
Process Integration: Synchronization between the desired and reported properties is managed by processes operating both at the edge and in the cloud. This dual-process integration ensures that any changes in the cloud's desired state are promptly reflected on the edge, and any state changes at the edge are accurately reported back to the cloud.
Consistency and Reliability: By maintaining a consistent and reliable flow of property values in both directions, the Agora Edge Apps SDK ensures that the digital twins accurately mirror their physical counterparts, facilitating improved monitoring, management, and operational efficiency.