Block type: dashboardGauge
Displays a bound signal as a needle on a circular scale.
| Module |
nflow_blocks
|
| Library | Dashboard blocks |
| Type |
dashboardGauge
|
| Label | Gauge |
Description
The Gauge block shows the value of the bound signal as a needle sweeping a full circular scale between the configured limits. Scale colors highlight value ranges.
Ports
Input(s)
This block declares no input ports.
Output(s)
This block declares no output ports.
Parameters
| Parameter | Default value |
|---|---|
LabelPosition
|
Top |
Binding
|
|
ShowInitialText
|
on |
ScaleColors
|
[] |
Limits
|
[0, -1, 100] |
FontColor
|
[0, 0, 0] |
Opacity
|
1 |
ScaleDirection
|
Clockwise |
Inspector Keys
These serialized keys are exposed by the block inspector.
LabelPosition
Binding
ShowInitialText
ScaleColors
Limits
FontColor
Opacity
ScaleDirection
Block Characteristics
| Block type | dashboardGauge |
| Family | Dashboard blocks |
| Rendered size | 125 x 140 |
| Phases | INIT, AFTER_STEP |
| Direct feedthrough | see Algorithms |
| Internal state or history | yes |
| Signal data type | double numeric values |
Algorithms
Extended Capabilities
This page describes the native runtime behavior observed in the module C++ sources. Declared phases indicate when the simulation engine calls the block.
Code generation: supported for C and Rust.
Implementation Sources
modules/nflow_blocks/libraries/dashboard/library.json{
"id": "builtin.dashboard",
"title": "Dashboard",
"version": "1.0.0",
"format": "nflow-2",
"metadata": {
"author": "Allan CORNET",
"created": "2026-08-07",
"tool": "Nelson nflow"
},
"comment": "Interactive controls and signal displays",
"license": "LGPL-3.0",
"builtin": true,
"blocks": [
{
"type": "dashboardScope",
"label": "Dashboard Scope",
"icon": "dashboardScope.svg",
"phases": [
"INIT",
"AFTER_STEP"
],
"width": 230,
"height": 165,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Hide",
"Binding": null,
"ShowInitialText": "on",
"TimeSpan": "auto",
"LegendPosition": "Top",
"ScaleAtStop": "on",
"UpdateMode": "Wrap",
"NormalizeYAxis": "off",
"TicksPosition": "Outside",
"TickLabels": "All",
"Grid": "All",
"Border": "on",
"Markers": "off",
"FontColor": [
0,
0,
0
],
"YLimits": [
-3,
3
],
"Colors": []
},
"render": {
"type": "math",
"formula": "Dashboard Scope"
}
},
{
"type": "dashboardDisplay",
"label": "Display",
"icon": "dashboardDisplay.svg",
"phases": [
"INIT",
"AFTER_STEP"
],
"width": 180,
"height": 40,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Hide",
"Binding": null,
"ShowInitialText": "on",
"Format": "short",
"Alignment": "Center",
"Opacity": "1",
"Layout": "Preserve dimensions",
"FormatString": "%d",
"GridColor": [
0.502,
0.502,
0.502
],
"ShowGrid": "on"
},
"render": {
"type": "math",
"formula": "Display"
}
},
{
"type": "dashboardEdit",
"label": "Edit",
"icon": "dashboardEdit.svg",
"phases": [],
"width": 150,
"height": 30,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Hide",
"Binding": null,
"ShowInitialText": "on",
"Alignment": "Center",
"Opacity": "1"
},
"render": {
"type": "math",
"formula": "Edit"
}
},
{
"type": "dashboardGauge",
"label": "Gauge",
"icon": "dashboardGauge.svg",
"phases": [
"INIT",
"AFTER_STEP"
],
"width": 125,
"height": 140,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Top",
"Binding": null,
"ShowInitialText": "on",
"ScaleColors": [],
"Limits": [
0,
-1,
100
],
"FontColor": [
0,
0,
0
],
"Opacity": "1",
"ScaleDirection": "Clockwise"
},
"render": {
"type": "math",
"formula": "Gauge"
}
},
{
"type": "dashboardHalfGauge",
"label": "Half Gauge",
"icon": "dashboardHalfGauge.svg",
"phases": [
"INIT",
"AFTER_STEP"
],
"width": 156,
"height": 108,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Top",
"Binding": null,
"ShowInitialText": "on",
"ScaleColors": [],
"Limits": [
0,
-1,
100
],
"FontColor": [
0,
0,
0
],
"Opacity": "1",
"ScaleDirection": "Clockwise"
},
"render": {
"type": "math",
"formula": "Half Gauge"
}
},
{
"type": "dashboardKnob",
"label": "Knob",
"icon": "dashboardKnob.svg",
"phases": [],
"width": 110,
"height": 115,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Top",
"Binding": null,
"ShowInitialText": "on",
"ScaleType": "Linear",
"Limits": [
0,
-1,
100
]
},
"render": {
"type": "math",
"formula": "Knob"
}
},
{
"type": "dashboardLamp",
"label": "Lamp",
"icon": "dashboardLamp.svg",
"phases": [
"INIT",
"AFTER_STEP"
],
"width": 65,
"height": 60,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Hide",
"Binding": null,
"ShowInitialText": "on",
"ColorDefault": [
0.7529411764705882,
0.7529411764705882,
0.7529411764705882
],
"StateColors": [
{
"Value": 0,
"Color": [
0.392156862745098,
0.8313725490196079,
0.07450980392156863
]
}
],
"Opacity": "1"
},
"render": {
"type": "math",
"formula": "Lamp"
}
},
{
"type": "dashboardLinearGauge",
"label": "Linear Gauge",
"icon": "dashboardLinearGauge.svg",
"phases": [
"INIT",
"AFTER_STEP"
],
"width": 210,
"height": 90,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Top",
"Binding": null,
"ShowInitialText": "on",
"ScaleColors": [],
"Limits": [
0,
-1,
100
],
"FontColor": [
0,
0,
0
],
"Opacity": "1",
"ScaleDirection": "Clockwise"
},
"render": {
"type": "math",
"formula": "Linear Gauge"
}
},
{
"type": "dashboardMultiStateImage",
"label": "MultiStateImage",
"icon": "dashboardMultiStateImage.svg",
"phases": [
"INIT",
"AFTER_STEP"
],
"width": 190,
"height": 180,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Top",
"Binding": null,
"ShowInitialText": "on",
"States": [
{
"State": 0,
"Size": [
0,
0
],
"Image": "",
"Thumbnail": ""
}
],
"DefaultImage": {
"Size": [
0,
0
],
"Image": "",
"Thumbnail": ""
},
"ScaleMode": "Fill with fixed aspect ratio"
},
"render": {
"type": "math",
"formula": "MultiStateImage"
}
},
{
"type": "dashboardPushButton",
"label": "Push Button",
"icon": "dashboardPushButton.svg",
"phases": [],
"width": 91,
"height": 44,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Hide",
"Binding": null,
"ShowInitialText": "on",
"ButtonText": "Button",
"OnValue": "1",
"Opacity": "1",
"ButtonType": "Momentary",
"Icon": "None",
"CustomIcon": "",
"IconAlignment": "Left",
"IconOnColor": [
0,
0.392156862745098,
0
],
"IconOffColor": [
0,
1,
0
],
"IconColor": "Off"
},
"render": {
"type": "math",
"formula": "Push Button"
}
},
{
"type": "dashboardQuarterGauge",
"label": "Quarter Gauge",
"icon": "dashboardQuarterGauge.svg",
"phases": [
"INIT",
"AFTER_STEP"
],
"width": 140,
"height": 160,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Top",
"Binding": null,
"ShowInitialText": "on",
"ScaleColors": [],
"Limits": [
0,
-1,
100
],
"FontColor": [
0,
0,
0
],
"Opacity": "1",
"ScaleDirection": "Clockwise"
},
"render": {
"type": "math",
"formula": "Quarter Gauge"
}
},
{
"type": "dashboardRadioButton",
"label": "Radio Button",
"icon": "dashboardRadioButton.svg",
"phases": [],
"width": 135,
"height": 115,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Top",
"Binding": null,
"ShowInitialText": "on",
"ButtonGroupName": "Group",
"States": [
{
"Value": 0,
"Label": "Label1"
},
{
"Value": 1,
"Label": "Label2"
},
{
"Value": 2,
"Label": "Label3"
}
],
"UseEnumeratedDataType": "off",
"EnumeratedDataType": "",
"Opacity": "1"
},
"render": {
"type": "math",
"formula": "Radio Button"
}
},
{
"type": "dashboardRockerSwitch",
"label": "Rocker Switch",
"icon": "dashboardRockerSwitch.svg",
"phases": [],
"width": 55,
"height": 100,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Top",
"Binding": null,
"ShowInitialText": "on",
"States": [
{
"Value": 0,
"Label": "Off"
},
{
"Value": 1,
"Label": "On"
}
]
},
"render": {
"type": "math",
"formula": "Rocker Switch"
}
},
{
"type": "dashboardRotarySwitch",
"label": "Rotary Switch",
"icon": "dashboardRotarySwitch.svg",
"phases": [],
"width": 125,
"height": 100,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Top",
"Binding": null,
"ShowInitialText": "on",
"States": [
{
"Value": 0,
"Label": "Off"
},
{
"Value": 1,
"Label": "Low"
},
{
"Value": 2,
"Label": "Medium"
},
{
"Value": 3,
"Label": "High"
}
],
"UseEnumeratedDataType": "off",
"EnumeratedDataType": ""
},
"render": {
"type": "math",
"formula": "Rotary Switch"
}
},
{
"type": "dashboardSlider",
"label": "Slider",
"icon": "dashboardSlider.svg",
"phases": [],
"width": 200,
"height": 90,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Top",
"Binding": null,
"ShowInitialText": "on",
"ScaleType": "Linear",
"Limits": [
0,
-1,
100
]
},
"render": {
"type": "math",
"formula": "Slider"
}
},
{
"type": "dashboardSliderSwitch",
"label": "Slider Switch",
"icon": "dashboardSliderSwitch.svg",
"phases": [],
"width": 120,
"height": 45,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Top",
"Binding": null,
"ShowInitialText": "on",
"States": [
{
"Value": 0,
"Label": "Off"
},
{
"Value": 1,
"Label": "On"
}
]
},
"render": {
"type": "math",
"formula": "Slider Switch"
}
},
{
"type": "dashboardToggleSwitch",
"label": "Toggle Switch",
"icon": "dashboardToggleSwitch.svg",
"phases": [],
"width": 55,
"height": 100,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Top",
"Binding": null,
"ShowInitialText": "on",
"States": [
{
"Value": 0,
"Label": "Off"
},
{
"Value": 1,
"Label": "On"
}
]
},
"render": {
"type": "math",
"formula": "Toggle Switch"
}
},
{
"type": "dashboardCallbackButton",
"label": "Callback Button",
"icon": "dashboardCallbackButton.svg",
"phases": [],
"width": 110,
"height": 35,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Top",
"Binding": null,
"ShowInitialText": "off",
"ButtonText": "Callback Button",
"ButtonType": "Momentary",
"ClickFcn": "",
"OnValue": 1,
"PressDelay": "500",
"PressFcn": "",
"RepeatInterval": "0",
"AutoActivate": true,
"fixedAspectRatio": "off"
},
"render": {
"type": "math",
"formula": "Callback Button"
}
},
{
"type": "dashboardCheckBox",
"label": "Check Box",
"icon": "dashboardCheckBox.svg",
"phases": [],
"width": 135,
"height": 30,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Hide",
"Binding": null,
"ShowInitialText": "on",
"Label": "Label",
"Values": [
0,
1
],
"Opacity": "1"
},
"render": {
"type": "math",
"formula": "Check Box"
}
},
{
"type": "dashboardComboBox",
"label": "Combo Box",
"icon": "dashboardComboBox.svg",
"phases": [],
"width": 160,
"height": 25,
"inputs": [],
"outputs": [],
"defaultParams": {
"LabelPosition": "Hide",
"Binding": null,
"ShowInitialText": "on",
"States": [
{
"Value": 0,
"Label": "Label1"
},
{
"Value": 1,
"Label": "Label2"
},
{
"Value": 2,
"Label": "Label3"
}
],
"UseEnumeratedDataType": "off",
"EnumeratedDataType": "",
"Opacity": "1"
},
"render": {
"type": "math",
"formula": "Combo Box"
}
}
]
}
modules/nflow_blocks/src/cpp/DashboardHandlers.cpp//=============================================================================
// Copyright (c) 2026-present Allan CORNET (Nelson)
//=============================================================================
// This file is part of Nelson.
//=============================================================================
// LICENCE_BLOCK_BEGIN
// SPDX-License-Identifier: LGPL-3.0-or-later
// LICENCE_BLOCK_END
//=============================================================================
#include "BlockRegistry.hpp"
#include "SimEngineTypes.hpp"
#include <algorithm>
//=============================================================================
namespace Nelson::NFlow {
//=============================================================================
static bool
dashboardPath(const nlohmann::json& binding, std::vector<std::string>& path)
{
path.clear();
if (!binding.is_object() || !binding.contains("blockPath")) {
return false;
}
const auto& value = binding["blockPath"];
if (value.is_string()) {
const std::string id = value.get<std::string>();
if (!id.empty()) {
path.push_back(id);
}
} else if (value.is_array()) {
for (const auto& item : value) {
if (item.is_string() && !item.get<std::string>().empty()) {
path.push_back(item.get<std::string>());
}
}
}
return !path.empty();
}
//=============================================================================
static bool
readSubsystemOutput(const BlockState::SubsystemState& subsystem,
const std::vector<std::string>& path, size_t pathIndex, int port, std::vector<double>& values,
std::vector<double>& valuesImag, std::vector<long long>& valuesI64, int& exactType)
{
if (pathIndex >= path.size()) {
return false;
}
auto found = subsystem.nameToId.find(path[pathIndex]);
if (found == subsystem.nameToId.end()) {
return false;
}
const BlockId nid = found->second;
if (nid < 0 || nid >= static_cast<BlockId>(subsystem.blocks.size())
|| nid >= static_cast<BlockId>(subsystem.blockStateVec.size())) {
return false;
}
if (pathIndex + 1 < path.size()) {
const auto& nested = subsystem.blockStateVec[nid].subsystem;
return nested
&& readSubsystemOutput(
*nested, path, pathIndex + 1, port, values, valuesImag, valuesI64, exactType);
}
const Block& source = subsystem.blocks[nid];
if (port < 0 || port >= source.outputs) {
return false;
}
int offset = -1;
int width = 1;
exactType = static_cast<int>(SigType::Double);
if (!subsystem.portBase.empty() && nid < static_cast<BlockId>(subsystem.portBase.size())) {
const int gpi = subsystem.portBase[nid] + port;
if (gpi >= 0 && gpi < static_cast<int>(subsystem.sigs.size())) {
const PortSig& sig = subsystem.sigs[gpi];
offset = sig.offset;
width = std::max(1, sig.width());
exactType = static_cast<int>(sig.type);
if (sig.isComplex && offset >= 0
&& offset + width <= static_cast<int>(subsystem.outputsImagVec.size())) {
valuesImag.assign(subsystem.outputsImagVec.begin() + offset,
subsystem.outputsImagVec.begin() + offset + width);
}
if (portUsesI64(sig) && offset >= 0
&& offset + width <= static_cast<int>(subsystem.outputsI64Vec.size())) {
valuesI64.assign(subsystem.outputsI64Vec.begin() + offset,
subsystem.outputsI64Vec.begin() + offset + width);
}
}
}
if (offset < 0 && nid < static_cast<BlockId>(subsystem.outputBase.size())) {
offset = subsystem.outputBase[nid] + port;
}
if (offset < 0 || offset + width > static_cast<int>(subsystem.outputsVec.size())) {
return false;
}
values.assign(
subsystem.outputsVec.begin() + offset, subsystem.outputsVec.begin() + offset + width);
return true;
}
//=============================================================================
static bool
readDashboardSignal(SimCtx& ctx, const nlohmann::json& binding, std::vector<double>& values,
std::vector<double>& valuesImag, std::vector<long long>& valuesI64, int& exactType)
{
if (!binding.is_object() || binding.value("kind", std::string()) != "signal") {
return false;
}
std::vector<std::string> path;
if (!dashboardPath(binding, path)) {
return false;
}
auto found = ctx.nameToId.find(path.front());
if (found == ctx.nameToId.end()) {
return false;
}
const BlockId nid = found->second;
const int port = std::max(1, binding.value("outputPortIndex", 1)) - 1;
if (nid < 0 || nid >= static_cast<BlockId>(ctx.blocks.size())
|| port >= ctx.blocks[nid].outputs) {
return false;
}
if (path.size() > 1) {
const auto& subsystem = ctx.blockState[nid].subsystem;
return subsystem
&& readSubsystemOutput(
*subsystem, path, 1, port, values, valuesImag, valuesI64, exactType);
}
const int offset = slotOf(ctx, nid, port);
const int width = outputWidth(ctx, nid, port);
if (offset < 0 || width <= 0 || offset + width > static_cast<int>(ctx.outputs.size())) {
return false;
}
values.assign(ctx.outputs.begin() + offset, ctx.outputs.begin() + offset + width);
const PortSig* signal = portSigOf(ctx, nid, port);
exactType = static_cast<int>(signal ? signal->type : SigType::Double);
if (signal && signal->isComplex && ctx.outputsImag
&& offset + width <= static_cast<int>(ctx.outputsImag->size())) {
valuesImag.assign(
ctx.outputsImag->begin() + offset, ctx.outputsImag->begin() + offset + width);
}
if (signal && portUsesI64(*signal) && ctx.outputsI64
&& offset + width <= static_cast<int>(ctx.outputsI64->size())) {
valuesI64.assign(
ctx.outputsI64->begin() + offset, ctx.outputsI64->begin() + offset + width);
}
return true;
}
//=============================================================================
static bool
handleDashboardObserver(SimCtx& ctx, const Block& block, Phase phase)
{
BlockState& state = getState(ctx, block.nid);
if (phase == Phase::INIT) {
state.scalar = 0.0;
state.scalar2 = 0.0;
state.displayValues.clear();
state.displayValuesImag.clear();
state.displayValuesI64.clear();
state.scopeSeries.clear();
state.scopeSeriesImag.clear();
state.scopeSeriesI64.clear();
state.xSeries.clear();
state.scopeExactType = static_cast<int>(SigType::Double);
state.dashboardTouched = false;
state.dashboardError.clear();
return false;
}
if (phase != Phase::AFTER_STEP) {
return false;
}
const nlohmann::json binding = block.params.value("Binding", nlohmann::json());
std::vector<double> values;
std::vector<double> valuesImag;
std::vector<long long> valuesI64;
int exactType = static_cast<int>(SigType::Double);
if (!readDashboardSignal(ctx, binding, values, valuesImag, valuesI64, exactType)) {
state.scalar2 = 0.0;
return false;
}
state.scalar2 = 1.0;
state.scopeExactType = exactType;
state.scalar = values.empty() ? 0.0 : values.front();
state.displayValues = values;
state.displayValuesImag = valuesImag;
state.displayValuesI64 = valuesI64;
state.dashboardTouched = true;
if (block.type == "dashboardScope") {
if (state.scopeSeries.size() != values.size()) {
state.scopeSeries.assign(values.size(), std::vector<double> {});
}
if (!valuesImag.empty() && state.scopeSeriesImag.size() != valuesImag.size()) {
state.scopeSeriesImag.assign(valuesImag.size(), std::vector<double> {});
}
if (!valuesI64.empty() && state.scopeSeriesI64.size() != valuesI64.size()) {
state.scopeSeriesI64.assign(valuesI64.size(), std::vector<long long> {});
}
for (size_t i = 0; i < values.size(); ++i) {
state.scopeSeries[i].push_back(values[i]);
}
for (size_t i = 0; i < valuesImag.size(); ++i) {
state.scopeSeriesImag[i].push_back(valuesImag[i]);
}
for (size_t i = 0; i < valuesI64.size(); ++i) {
state.scopeSeriesI64[i].push_back(valuesI64[i]);
}
state.xSeries.push_back(ctx.t);
}
return false;
}
//=============================================================================
static bool
handleDashboardControl(SimCtx&, const Block&, Phase)
{
return false;
}
//=============================================================================
void
registerDashboardHandlers(BlockRegistry& registry)
{
const char* observers[] = { "dashboardScope", "dashboardDisplay", "dashboardGauge",
"dashboardHalfGauge", "dashboardQuarterGauge", "dashboardLinearGauge", "dashboardLamp",
"dashboardMultiStateImage" };
for (const char* type : observers) {
registry.registerHandler(
type, handleDashboardObserver, { Phase::INIT, Phase::AFTER_STEP }, "builtin.dashboard");
}
const char* controls[] = { "dashboardEdit", "dashboardKnob", "dashboardSlider",
"dashboardPushButton", "dashboardRotarySwitch", "dashboardRadioButton", "dashboardComboBox",
"dashboardCheckBox", "dashboardRockerSwitch", "dashboardSliderSwitch",
"dashboardToggleSwitch", "dashboardCallbackButton" };
BlockMetadata metadata;
metadata.interactive = true;
for (const char* type : controls) {
registry.registerHandler(type, handleDashboardControl, {}, "builtin.dashboard", metadata);
}
}
//=============================================================================
} // namespace Nelson::NFlow
//=============================================================================
| Version | Description |
|---|---|
| 1.0.0 | initial version |