@frangoteam/fuxa-min 1.2.11 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/auth/index.js +141 -3
- package/api/command/index.js +1 -1
- package/api/index.js +21 -2
- package/api/jwt-helper.js +3 -1
- package/api/resources/index.js +19 -2
- package/api/scripts/index.js +7 -3
- package/dist/3rdpartylicenses.txt +139 -7
- package/dist/assets/i18n/de.json +10 -0
- package/dist/assets/i18n/en.json +21 -3
- package/dist/assets/i18n/es.json +12 -0
- package/dist/assets/i18n/fr.json +11 -0
- package/dist/assets/i18n/ja.json +16 -6
- package/dist/assets/i18n/ko.json +12 -0
- package/dist/assets/i18n/pt.json +9 -2
- package/dist/assets/i18n/ru.json +11 -0
- package/dist/assets/i18n/sv.json +11 -1
- package/dist/assets/i18n/tr.json +8 -1
- package/dist/assets/i18n/ua.json +9 -2
- package/dist/assets/i18n/zh-cn.json +11 -0
- package/dist/assets/i18n/zh-tw.json +12 -1
- package/dist/assets/lib/svgeditor/fuxa-editor.min.js +17 -23
- package/dist/index.html +2 -2
- package/dist/main.72bdfed42c527918.js +329 -0
- package/dist/polyfills.d7de05f9af2fb559.js +1 -0
- package/dist/{runtime.8ef63094e52a66ba.js → runtime.9136a61a9b98f987.js} +1 -1
- package/dist/{scripts.40b60f02658462e4.js → scripts.d9e6ee984bf6f3b7.js} +1 -1
- package/dist/styles.545e37beb3e671ba.css +1 -0
- package/docs/openapi.yaml +3 -0
- package/integrations/node-red/index.js +4 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-daq.html +56 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-daq.js +8 -2
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag-change.html +56 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag-change.js +12 -12
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag-daq-settings.html +56 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag-daq-settings.js +14 -10
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag.html +69 -14
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag.js +44 -12
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag-daq-settings.html +56 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag-daq-settings.js +24 -20
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag.html +72 -13
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag.js +33 -7
- package/main.js +38 -17
- package/package.json +10 -5
- package/runtime/alarms/alarmstorage.js +45 -30
- package/runtime/apikeys/apiKeysStorage.js +34 -22
- package/runtime/devices/adsclient/index.js +1 -1
- package/runtime/devices/ethernetip/index.js +1 -1
- package/runtime/devices/gpio/index.js +1 -1
- package/runtime/devices/odbc/index.js +14 -9
- package/runtime/devices/s7/index.js +2 -2
- package/runtime/devices/template/index.js +14 -14
- package/runtime/notificator/notifystorage.js +34 -23
- package/runtime/project/index.js +16 -0
- package/runtime/project/prjstorage.js +78 -40
- package/runtime/scripts/index.js +6 -2
- package/runtime/storage/calculator.js +17 -13
- package/runtime/storage/daqstorage.js +28 -2
- package/runtime/storage/influxdb/index.js +8 -3
- package/runtime/storage/questdb/index.js +224 -0
- package/runtime/storage/sqlite/index.js +11 -8
- package/runtime/storage/tdengine/index.js +24 -9
- package/runtime/users/usrstorage.js +65 -61
- package/runtime/utils.js +5 -0
- package/settings.default.js +8 -2
- package/dist/main.92522279642ef880.js +0 -329
- package/dist/polyfills.c8e7db9850a3ad8b.js +0 -1
- package/dist/styles.03cc550382689976.css +0 -1
|
@@ -152,12 +152,11 @@ async function mountNodeRedIfInstalled({ app, server, settings, runtime, logger,
|
|
|
152
152
|
});
|
|
153
153
|
};
|
|
154
154
|
|
|
155
|
-
// Allow
|
|
155
|
+
// Allow only dashboard routes as public; require JWT or API key for admin/editor/flows when security is enabled
|
|
156
156
|
const allowDashboard = (req, res, next) => {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (url.includes('/dashboard') || url.includes('/socket.io')) return next();
|
|
157
|
+
// Public dashboard UI and its HTTP APIs (served from httpNodeRoot/ui.path).
|
|
158
|
+
// baseUrl comes from Express mount point and is not affected by query/path tricks.
|
|
159
|
+
if (req.baseUrl === '/dashboard') return next();
|
|
161
160
|
|
|
162
161
|
if (!settings.secureEnabled || settings.nodeRedAuthMode === 'legacy-open') {
|
|
163
162
|
return next();
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
color: '#a6bbcf',
|
|
5
5
|
defaults: {
|
|
6
6
|
name: {value:""},
|
|
7
|
-
tag: {value:"",
|
|
7
|
+
tag: {value:""}, // Keep: tag name for display and backward compatibility
|
|
8
|
+
tagId: {value:""}, // New: unique identifier (optional, for backward compatibility)
|
|
8
9
|
from: {value:""},
|
|
9
10
|
to: {value:""}
|
|
10
11
|
},
|
|
@@ -12,17 +13,65 @@
|
|
|
12
13
|
outputs:1,
|
|
13
14
|
icon: "white-globe.png",
|
|
14
15
|
label: function() {
|
|
15
|
-
|
|
16
|
+
if (this.name) {
|
|
17
|
+
return this.name;
|
|
18
|
+
}
|
|
19
|
+
// Display tag name or tagId
|
|
20
|
+
return this.tag || this.tagId || "get daq";
|
|
16
21
|
},
|
|
17
22
|
oneditprepare: function() {
|
|
23
|
+
var node = this;
|
|
24
|
+
var tagMap = {}; // Use tag.id as key to avoid tag.name duplication issues
|
|
25
|
+
|
|
18
26
|
$.getJSON('/nodered/fuxa/devices', function(data) {
|
|
19
27
|
var datalist = $('#fuxa-tags-daq');
|
|
20
28
|
datalist.empty();
|
|
29
|
+
|
|
21
30
|
data.forEach(function(device) {
|
|
22
31
|
device.tags.forEach(function(tag) {
|
|
23
|
-
|
|
32
|
+
var fullName = device.name + ' - ' + tag.name;
|
|
33
|
+
// datalist option value format: tag.name(tag.id)
|
|
34
|
+
var optionValue = tag.name + '(' + tag.id + ')';
|
|
35
|
+
datalist.append('<option value="' + optionValue + '">' + fullName + '</option>');
|
|
36
|
+
tagMap[tag.id] = {
|
|
37
|
+
name: tag.name,
|
|
38
|
+
deviceName: device.name,
|
|
39
|
+
fullName: fullName
|
|
40
|
+
};
|
|
24
41
|
});
|
|
25
42
|
});
|
|
43
|
+
|
|
44
|
+
// Listen for tagSelected input changes
|
|
45
|
+
$('#node-input-tagSelected').on('change', function() {
|
|
46
|
+
var selectedValue = $(this).val();
|
|
47
|
+
// Parse format: tag.name(tag.id)
|
|
48
|
+
var match = selectedValue.match(/^(.+)\(([^)]+)\)$/);
|
|
49
|
+
if (match) {
|
|
50
|
+
var tagName = match[1];
|
|
51
|
+
var tagId = match[2];
|
|
52
|
+
|
|
53
|
+
// Set the actual stored fields
|
|
54
|
+
$('#node-input-tag').val(tagName);
|
|
55
|
+
$('#node-input-tagId').val(tagId);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Initialize display (when editing existing node)
|
|
60
|
+
if (node.tagId && tagMap[node.tagId]) {
|
|
61
|
+
// Has tagId, construct tagSelected value
|
|
62
|
+
var tagName = node.tag || tagMap[node.tagId].name;
|
|
63
|
+
$('#node-input-tagSelected').val(tagName + '(' + node.tagId + ')');
|
|
64
|
+
} else if (node.tag && !node.tagId) {
|
|
65
|
+
// Old node: only has tag (tag.name), need to find corresponding tagId
|
|
66
|
+
// Note: if there are duplicate names, only the first match will be found
|
|
67
|
+
for (var tagId in tagMap) {
|
|
68
|
+
if (tagMap[tagId].name === node.tag) {
|
|
69
|
+
$('#node-input-tagSelected').val(node.tag + '(' + tagId + ')');
|
|
70
|
+
$('#node-input-tagId').val(tagId);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
26
75
|
});
|
|
27
76
|
|
|
28
77
|
// Convert timestamp format for datetime-local inputs
|
|
@@ -64,10 +113,12 @@
|
|
|
64
113
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
65
114
|
</div>
|
|
66
115
|
<div class="form-row">
|
|
67
|
-
<label for="node-input-
|
|
68
|
-
<input type="text" id="node-input-
|
|
116
|
+
<label for="node-input-tagSelected"><i class="fa fa-tag"></i> Tag</label>
|
|
117
|
+
<input type="text" id="node-input-tagSelected" list="fuxa-tags-daq" placeholder="Select Tag">
|
|
69
118
|
<datalist id="fuxa-tags-daq"></datalist>
|
|
70
119
|
</div>
|
|
120
|
+
<input type="hidden" id="node-input-tag">
|
|
121
|
+
<input type="hidden" id="node-input-tagId">
|
|
71
122
|
<div class="form-row">
|
|
72
123
|
<label for="node-input-from"><i class="fa fa-clock-o"></i> From (timestamp)</label>
|
|
73
124
|
<input type="datetime-local" id="node-input-from" placeholder="From timestamp (optional)">
|
|
@@ -6,7 +6,13 @@ module.exports = function(RED) {
|
|
|
6
6
|
|
|
7
7
|
this.on('input', async function(msg) {
|
|
8
8
|
try {
|
|
9
|
-
|
|
9
|
+
// Prefer config.tagId, fallback to config.tag for backward compatibility
|
|
10
|
+
var tagId = config.tagId;
|
|
11
|
+
if (!tagId && config.tag) {
|
|
12
|
+
// Backward compatibility: old nodes use tag.name, need to convert to tagId
|
|
13
|
+
tagId = fuxa.getTagId(config.tag, null);
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
if (tagId) {
|
|
11
17
|
var fromts = config.from || msg.from || Date.now() - 3600000; // default 1 hour ago
|
|
12
18
|
var tots = config.to || msg.to || Date.now();
|
|
@@ -14,7 +20,7 @@ module.exports = function(RED) {
|
|
|
14
20
|
msg.payload = data;
|
|
15
21
|
node.send(msg);
|
|
16
22
|
} else {
|
|
17
|
-
node.error('Tag not found: ' + config.tag, msg);
|
|
23
|
+
node.error('Tag not found: ' + (config.tag || config.tagId), msg);
|
|
18
24
|
}
|
|
19
25
|
} catch (err) {
|
|
20
26
|
node.error(err, msg);
|
|
@@ -4,23 +4,72 @@
|
|
|
4
4
|
color: '#a6bbcf',
|
|
5
5
|
defaults: {
|
|
6
6
|
name: {value:""},
|
|
7
|
-
tag: {value:"",
|
|
7
|
+
tag: {value:""}, // Keep: tag name for display and backward compatibility
|
|
8
|
+
tagId: {value:""} // New: unique identifier (optional, for backward compatibility)
|
|
8
9
|
},
|
|
9
10
|
inputs:0,
|
|
10
11
|
outputs:1,
|
|
11
12
|
icon: "white-globe.png",
|
|
12
13
|
label: function() {
|
|
13
|
-
|
|
14
|
+
if (this.name) {
|
|
15
|
+
return this.name;
|
|
16
|
+
}
|
|
17
|
+
// Display tag name or tagId
|
|
18
|
+
return this.tag || this.tagId || "get tag change";
|
|
14
19
|
},
|
|
15
20
|
oneditprepare: function() {
|
|
21
|
+
var node = this;
|
|
22
|
+
var tagMap = {}; // Use tag.id as key to avoid tag.name duplication issues
|
|
23
|
+
|
|
16
24
|
$.getJSON('/nodered/fuxa/devices', function(data) {
|
|
17
25
|
var datalist = $('#fuxa-change-tags');
|
|
18
26
|
datalist.empty();
|
|
27
|
+
|
|
19
28
|
data.forEach(function(device) {
|
|
20
29
|
device.tags.forEach(function(tag) {
|
|
21
|
-
|
|
30
|
+
var fullName = device.name + ' - ' + tag.name;
|
|
31
|
+
// datalist option value format: tag.name(tag.id)
|
|
32
|
+
var optionValue = tag.name + '(' + tag.id + ')';
|
|
33
|
+
datalist.append('<option value="' + optionValue + '">' + fullName + '</option>');
|
|
34
|
+
tagMap[tag.id] = {
|
|
35
|
+
name: tag.name,
|
|
36
|
+
deviceName: device.name,
|
|
37
|
+
fullName: fullName
|
|
38
|
+
};
|
|
22
39
|
});
|
|
23
40
|
});
|
|
41
|
+
|
|
42
|
+
// Listen for tagSelected input changes
|
|
43
|
+
$('#node-input-tagSelected').on('change', function() {
|
|
44
|
+
var selectedValue = $(this).val();
|
|
45
|
+
// Parse format: tag.name(tag.id)
|
|
46
|
+
var match = selectedValue.match(/^(.+)\(([^)]+)\)$/);
|
|
47
|
+
if (match) {
|
|
48
|
+
var tagName = match[1];
|
|
49
|
+
var tagId = match[2];
|
|
50
|
+
|
|
51
|
+
// Set the actual stored fields
|
|
52
|
+
$('#node-input-tag').val(tagName);
|
|
53
|
+
$('#node-input-tagId').val(tagId);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Initialize display (when editing existing node)
|
|
58
|
+
if (node.tagId && tagMap[node.tagId]) {
|
|
59
|
+
// Has tagId, construct tagSelected value
|
|
60
|
+
var tagName = node.tag || tagMap[node.tagId].name;
|
|
61
|
+
$('#node-input-tagSelected').val(tagName + '(' + node.tagId + ')');
|
|
62
|
+
} else if (node.tag && !node.tagId) {
|
|
63
|
+
// Old node: only has tag (tag.name), need to find corresponding tagId
|
|
64
|
+
// Note: if there are duplicate names, only the first match will be found
|
|
65
|
+
for (var tagId in tagMap) {
|
|
66
|
+
if (tagMap[tagId].name === node.tag) {
|
|
67
|
+
$('#node-input-tagSelected').val(node.tag + '(' + tagId + ')');
|
|
68
|
+
$('#node-input-tagId').val(tagId);
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
24
73
|
});
|
|
25
74
|
}
|
|
26
75
|
});
|
|
@@ -32,10 +81,12 @@
|
|
|
32
81
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
33
82
|
</div>
|
|
34
83
|
<div class="form-row">
|
|
35
|
-
<label for="node-input-
|
|
36
|
-
<input type="text" id="node-input-
|
|
84
|
+
<label for="node-input-tagSelected"><i class="fa fa-tag"></i> Tag</label>
|
|
85
|
+
<input type="text" id="node-input-tagSelected" list="fuxa-change-tags" placeholder="Select Tag">
|
|
37
86
|
<datalist id="fuxa-change-tags"></datalist>
|
|
38
87
|
</div>
|
|
88
|
+
<input type="hidden" id="node-input-tag">
|
|
89
|
+
<input type="hidden" id="node-input-tagId">
|
|
39
90
|
</script>
|
|
40
91
|
|
|
41
92
|
<script type="text/x-red" data-help-name="get-tag-change">
|
|
@@ -7,26 +7,26 @@ module.exports = function(RED) {
|
|
|
7
7
|
// Store previous values to detect actual changes
|
|
8
8
|
var previousValues = {};
|
|
9
9
|
|
|
10
|
+
// Prefer config.tagId, fallback to config.tag for backward compatibility
|
|
11
|
+
var configuredTagId = config.tagId;
|
|
12
|
+
if (!configuredTagId && config.tag) {
|
|
13
|
+
// Backward compatibility: old nodes use tag.name, need to convert to tagId
|
|
14
|
+
configuredTagId = fuxa.getTagId(config.tag, null);
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
// Initialize previous value for the configured tag
|
|
11
|
-
if (
|
|
12
|
-
var
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
var initialValue = fuxa.getTag(tagId);
|
|
16
|
-
if (initialValue !== undefined) {
|
|
17
|
-
previousValues[tagId] = initialValue;
|
|
18
|
-
}
|
|
18
|
+
if (configuredTagId) {
|
|
19
|
+
var initialValue = fuxa.getTag(configuredTagId);
|
|
20
|
+
if (initialValue !== undefined) {
|
|
21
|
+
previousValues[configuredTagId] = initialValue;
|
|
19
22
|
}
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
// Event listener for device value changes (raw events from devices)
|
|
23
26
|
var deviceEventListener = function(deviceEvent) {
|
|
24
27
|
try {
|
|
25
28
|
// deviceEvent format: { id: deviceId, values: { tagId: tagObject, ... } }
|
|
26
29
|
if (deviceEvent && deviceEvent.values) {
|
|
27
|
-
// Check if any of the changed values match our configured tag
|
|
28
|
-
var configuredTagId = fuxa.getTagId(config.tag, null);
|
|
29
|
-
|
|
30
30
|
if (configuredTagId && deviceEvent.values[configuredTagId]) {
|
|
31
31
|
var tagData = deviceEvent.values[configuredTagId];
|
|
32
32
|
|
|
@@ -4,23 +4,72 @@
|
|
|
4
4
|
color: '#a6bbcf',
|
|
5
5
|
defaults: {
|
|
6
6
|
name: {value:""},
|
|
7
|
-
tag: {value:"",
|
|
7
|
+
tag: {value:""}, // Keep: tag name for display and backward compatibility
|
|
8
|
+
tagId: {value:""} // New: unique identifier (optional, for backward compatibility)
|
|
8
9
|
},
|
|
9
10
|
inputs:1,
|
|
10
11
|
outputs:1,
|
|
11
12
|
icon: "white-globe.png",
|
|
12
13
|
label: function() {
|
|
13
|
-
|
|
14
|
+
if (this.name) {
|
|
15
|
+
return this.name;
|
|
16
|
+
}
|
|
17
|
+
// Display tag name or tagId
|
|
18
|
+
return this.tag || this.tagId || "get tag daq settings";
|
|
14
19
|
},
|
|
15
20
|
oneditprepare: function() {
|
|
21
|
+
var node = this;
|
|
22
|
+
var tagMap = {}; // Use tag.id as key to avoid tag.name duplication issues
|
|
23
|
+
|
|
16
24
|
$.getJSON('/nodered/fuxa/devices', function(data) {
|
|
17
25
|
var datalist = $('#fuxa-tags-daq-settings');
|
|
18
26
|
datalist.empty();
|
|
27
|
+
|
|
19
28
|
data.forEach(function(device) {
|
|
20
29
|
device.tags.forEach(function(tag) {
|
|
21
|
-
|
|
30
|
+
var fullName = device.name + ' - ' + tag.name;
|
|
31
|
+
// datalist option value format: tag.name(tag.id)
|
|
32
|
+
var optionValue = tag.name + '(' + tag.id + ')';
|
|
33
|
+
datalist.append('<option value="' + optionValue + '">' + fullName + '</option>');
|
|
34
|
+
tagMap[tag.id] = {
|
|
35
|
+
name: tag.name,
|
|
36
|
+
deviceName: device.name,
|
|
37
|
+
fullName: fullName
|
|
38
|
+
};
|
|
22
39
|
});
|
|
23
40
|
});
|
|
41
|
+
|
|
42
|
+
// Listen for tagSelected input changes
|
|
43
|
+
$('#node-input-tagSelected').on('change', function() {
|
|
44
|
+
var selectedValue = $(this).val();
|
|
45
|
+
// Parse format: tag.name(tag.id)
|
|
46
|
+
var match = selectedValue.match(/^(.+)\(([^)]+)\)$/);
|
|
47
|
+
if (match) {
|
|
48
|
+
var tagName = match[1];
|
|
49
|
+
var tagId = match[2];
|
|
50
|
+
|
|
51
|
+
// Set the actual stored fields
|
|
52
|
+
$('#node-input-tag').val(tagName);
|
|
53
|
+
$('#node-input-tagId').val(tagId);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Initialize display (when editing existing node)
|
|
58
|
+
if (node.tagId && tagMap[node.tagId]) {
|
|
59
|
+
// Has tagId, construct tagSelected value
|
|
60
|
+
var tagName = node.tag || tagMap[node.tagId].name;
|
|
61
|
+
$('#node-input-tagSelected').val(tagName + '(' + node.tagId + ')');
|
|
62
|
+
} else if (node.tag && !node.tagId) {
|
|
63
|
+
// Old node: only has tag (tag.name), need to find corresponding tagId
|
|
64
|
+
// Note: if there are duplicate names, only the first match will be found
|
|
65
|
+
for (var tagId in tagMap) {
|
|
66
|
+
if (tagMap[tagId].name === node.tag) {
|
|
67
|
+
$('#node-input-tagSelected').val(node.tag + '(' + tagId + ')');
|
|
68
|
+
$('#node-input-tagId').val(tagId);
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
24
73
|
});
|
|
25
74
|
}
|
|
26
75
|
});
|
|
@@ -32,10 +81,12 @@
|
|
|
32
81
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
33
82
|
</div>
|
|
34
83
|
<div class="form-row">
|
|
35
|
-
<label for="node-input-
|
|
36
|
-
<input type="text" id="node-input-
|
|
84
|
+
<label for="node-input-tagSelected"><i class="fa fa-tag"></i> Tag</label>
|
|
85
|
+
<input type="text" id="node-input-tagSelected" list="fuxa-tags-daq-settings" placeholder="Select Tag">
|
|
37
86
|
<datalist id="fuxa-tags-daq-settings"></datalist>
|
|
38
87
|
</div>
|
|
88
|
+
<input type="hidden" id="node-input-tag">
|
|
89
|
+
<input type="hidden" id="node-input-tagId">
|
|
39
90
|
</script>
|
|
40
91
|
|
|
41
92
|
<script type="text/x-red" data-help-name="get-tag-daq-settings">
|
|
@@ -6,18 +6,22 @@ module.exports = function(RED) {
|
|
|
6
6
|
|
|
7
7
|
this.on('input', async function(msg) {
|
|
8
8
|
try {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} else {
|
|
17
|
-
node.error('Tag not found: ' + tagName, msg);
|
|
9
|
+
// Prefer config.tagId, fallback to config.tag or msg.tag for backward compatibility
|
|
10
|
+
var tagId = config.tagId;
|
|
11
|
+
if (!tagId) {
|
|
12
|
+
var tagName = config.tag || msg.tag;
|
|
13
|
+
if (tagName) {
|
|
14
|
+
// Backward compatibility: old nodes use tag.name, need to convert to tagId
|
|
15
|
+
tagId = fuxa.getTagId(tagName, null);
|
|
18
16
|
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (tagId) {
|
|
20
|
+
var settings = await fuxa.getTagDaqSettings(tagId);
|
|
21
|
+
msg.payload = settings;
|
|
22
|
+
node.send(msg);
|
|
19
23
|
} else {
|
|
20
|
-
node.error('Tag
|
|
24
|
+
node.error('Tag not found: ' + (config.tag || msg.tag || config.tagId), msg);
|
|
21
25
|
}
|
|
22
26
|
} catch (err) {
|
|
23
27
|
node.error(err, msg);
|
|
@@ -1,27 +1,69 @@
|
|
|
1
1
|
<script type="text/javascript">
|
|
2
|
-
RED.nodes.registerType('get-tag',{
|
|
2
|
+
RED.nodes.registerType('get-tag', {
|
|
3
3
|
category: 'FUXA',
|
|
4
4
|
color: '#a6bbcf',
|
|
5
5
|
defaults: {
|
|
6
|
-
name: {value:""},
|
|
7
|
-
tag: {value:"",
|
|
6
|
+
name: {value: ""},
|
|
7
|
+
tag: {value: ""},
|
|
8
|
+
tagId: {value: ""}
|
|
8
9
|
},
|
|
9
|
-
inputs:1,
|
|
10
|
-
outputs:1,
|
|
10
|
+
inputs: 1,
|
|
11
|
+
outputs: 1,
|
|
11
12
|
icon: "white-globe.png",
|
|
12
13
|
label: function() {
|
|
13
|
-
|
|
14
|
+
if (this.name) {
|
|
15
|
+
return this.name;
|
|
16
|
+
}
|
|
17
|
+
return this.tag || this.tagId || "get tag";
|
|
14
18
|
},
|
|
15
19
|
oneditprepare: function() {
|
|
20
|
+
var node = this;
|
|
21
|
+
var tagMap = {};
|
|
22
|
+
|
|
16
23
|
$.getJSON('/nodered/fuxa/devices', function(data) {
|
|
17
24
|
var datalist = $('#fuxa-tags');
|
|
18
25
|
datalist.empty();
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
|
|
27
|
+
(data || []).forEach(function(device) {
|
|
28
|
+
(device.tags || []).forEach(function(tag) {
|
|
29
|
+
var fullName = device.name + ' - ' + tag.name;
|
|
30
|
+
var optionValue = tag.name + '(' + tag.id + ')';
|
|
31
|
+
datalist.append('<option value="' + optionValue + '">' + fullName + '</option>');
|
|
32
|
+
tagMap[tag.id] = {
|
|
33
|
+
name: tag.name,
|
|
34
|
+
deviceName: device.name,
|
|
35
|
+
fullName: fullName
|
|
36
|
+
};
|
|
22
37
|
});
|
|
23
38
|
});
|
|
39
|
+
|
|
40
|
+
$('#node-input-tagSelected').on('change', function() {
|
|
41
|
+
var selectedValue = $(this).val();
|
|
42
|
+
var match = selectedValue.match(/^(.+)\(([^)]+)\)$/);
|
|
43
|
+
if (match) {
|
|
44
|
+
$('#node-input-tag').val(match[1]);
|
|
45
|
+
$('#node-input-tagId').val(match[2]);
|
|
46
|
+
} else if (!selectedValue || !selectedValue.trim()) {
|
|
47
|
+
$('#node-input-tag').val('');
|
|
48
|
+
$('#node-input-tagId').val('');
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (node.tagId && tagMap[node.tagId]) {
|
|
53
|
+
var tagName = node.tag || tagMap[node.tagId].name;
|
|
54
|
+
$('#node-input-tagSelected').val(tagName + '(' + node.tagId + ')');
|
|
55
|
+
} else if (node.tag && !node.tagId) {
|
|
56
|
+
for (var id in tagMap) {
|
|
57
|
+
if (tagMap[id].name === node.tag) {
|
|
58
|
+
$('#node-input-tagSelected').val(node.tag + '(' + id + ')');
|
|
59
|
+
$('#node-input-tagId').val(id);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
24
64
|
});
|
|
65
|
+
|
|
66
|
+
$('#node-input-tagSelected').attr('placeholder', 'Optional - leave empty to use msg.topic');
|
|
25
67
|
}
|
|
26
68
|
});
|
|
27
69
|
</script>
|
|
@@ -32,13 +74,26 @@
|
|
|
32
74
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
33
75
|
</div>
|
|
34
76
|
<div class="form-row">
|
|
35
|
-
<label for="node-input-
|
|
36
|
-
<input type="text" id="node-input-
|
|
77
|
+
<label for="node-input-tagSelected"><i class="fa fa-tag"></i> Tag</label>
|
|
78
|
+
<input type="text" id="node-input-tagSelected" list="fuxa-tags" placeholder="Optional - leave empty to use msg.topic">
|
|
37
79
|
<datalist id="fuxa-tags"></datalist>
|
|
38
80
|
</div>
|
|
81
|
+
<input type="hidden" id="node-input-tag">
|
|
82
|
+
<input type="hidden" id="node-input-tagId">
|
|
83
|
+
<div class="form-row">
|
|
84
|
+
<div style="margin-left:105px;font-size:12px;color:#666;">
|
|
85
|
+
If <b>Tag</b> is empty, the node uses <code>msg.topic</code>.
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
39
88
|
</script>
|
|
40
89
|
|
|
41
90
|
<script type="text/x-red" data-help-name="get-tag">
|
|
42
|
-
<p>
|
|
43
|
-
<
|
|
44
|
-
</
|
|
91
|
+
<p>Gets the value of a FUXA tag.</p>
|
|
92
|
+
<ul>
|
|
93
|
+
<li>If <b>Tag</b> is set in the node, that tag is used.</li>
|
|
94
|
+
<li>If <b>Tag</b> is empty, <code>msg.topic</code> is used.</li>
|
|
95
|
+
</ul>
|
|
96
|
+
<p>The tag value is returned in <code>msg.payload</code>.</p>
|
|
97
|
+
<p>The resolved tag name is always set on <code>msg.topic</code>.</p>
|
|
98
|
+
<pre>msg.topic = "V2";</pre>
|
|
99
|
+
</script>
|
|
@@ -1,25 +1,57 @@
|
|
|
1
1
|
module.exports = function(RED) {
|
|
2
2
|
function FuxaGetTagNode(config) {
|
|
3
3
|
RED.nodes.createNode(this, config);
|
|
4
|
-
|
|
4
|
+
const node = this;
|
|
5
|
+
|
|
5
6
|
// Access FUXA functions from global context
|
|
6
|
-
|
|
7
|
+
const fuxa = RED.settings?.functionGlobalContext?.fuxa;
|
|
8
|
+
|
|
9
|
+
node.on("input", function(msg, send, done) {
|
|
10
|
+
send = send || node.send.bind(node);
|
|
7
11
|
|
|
8
|
-
this.on('input', function(msg) {
|
|
9
12
|
try {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
if (!fuxa) {
|
|
14
|
+
node.error("FUXA not available in functionGlobalContext", msg);
|
|
15
|
+
return done && done();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const uiTag = (typeof config.tag === "string") ? config.tag.trim() : "";
|
|
19
|
+
const topicTag = (typeof msg.topic === "string") ? msg.topic.trim() : "";
|
|
20
|
+
const tagRef = uiTag || topicTag;
|
|
21
|
+
let tagId = config.tagId || null;
|
|
22
|
+
|
|
23
|
+
// Keep master behavior (prefer configured tagId), then fallback to UI tag/topic lookup.
|
|
24
|
+
if (!tagId && !tagRef) {
|
|
25
|
+
node.error("No tag provided: set Tag in the node OR provide msg.topic", msg);
|
|
26
|
+
return done && done();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!tagId) {
|
|
30
|
+
tagId = fuxa.getTagId(tagRef, null);
|
|
18
31
|
}
|
|
32
|
+
|
|
33
|
+
if (!tagId) {
|
|
34
|
+
node.error("Tag not found: " + tagRef, msg);
|
|
35
|
+
return done && done();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const value = fuxa.getTag(tagId);
|
|
39
|
+
|
|
40
|
+
msg.payload = value;
|
|
41
|
+
if (tagRef) {
|
|
42
|
+
msg.topic = tagRef;
|
|
43
|
+
} else if (typeof config.tag === "string" && config.tag.trim()) {
|
|
44
|
+
msg.topic = config.tag.trim();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
send(msg);
|
|
48
|
+
return done && done();
|
|
19
49
|
} catch (err) {
|
|
20
50
|
node.error(err, msg);
|
|
51
|
+
return done && done(err);
|
|
21
52
|
}
|
|
22
53
|
});
|
|
23
54
|
}
|
|
55
|
+
|
|
24
56
|
RED.nodes.registerType("get-tag", FuxaGetTagNode);
|
|
25
|
-
}
|
|
57
|
+
};
|