-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtulip_machine_attribute.html
348 lines (325 loc) · 12.1 KB
/
tulip_machine_attribute.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
<script type="text/javascript">
RED.nodes.registerType('tulip-machine-attribute', {
category: 'Tulip',
inputs: 1,
outputs: 1,
color: '#65CCB8',
icon: 'tulip.svg',
defaults: {
name: { value: 'tulip-machine-attribute' },
apiAuth: {
value: '',
type: 'tulip-api-auth',
required: true,
},
keepAlive: { value: true },
keepAliveMsecs: { value: 10000 },
retainMsgProps: { value: true },
singleAttribute: { value: true },
deviceInfo: {
value: '{"attributeId":"","deviceId":""}',
required: true,
},
payloadSource: {
value: 'payload',
required: true,
validate: RED.validators.typedInput('payloadType'),
},
payloadType: { value: 'msg' },
},
paletteLabel: 'machine-attr',
label: function () {
return this.name || 'tulip-machine-attribute';
},
oneditprepare: function () {
// Whether or not to show keep-alive msecs option
const updateKeepAlive = () => {
const keepAlive = $('#node-input-keepAlive').is(':checked');
if (keepAlive) {
$('#div-node-input-keepAliveMsecs').show();
} else {
$('#div-node-input-keepAliveMsecs').hide();
}
};
updateKeepAlive();
$('#node-input-keepAlive').change(() => {
updateKeepAlive();
});
const node = this;
// Add retainMsgProps as a property to transition legacy nodes. Set to false since
// previously msg props were not retained.
if (!('retainMsgProps' in node)) {
node.retainMsgProps = false;
}
// Add singleAttribute as a property to transition legacy nodes. Set to true since
// previously only single tag was supported.
if (node.singleAttribute === undefined || node.singleAttribute === null) {
node.singleAttribute = true;
}
// Set initial state of singleAttribute option & tag selection
if (node.singleAttribute) {
$('.single-attribute-wrapper').show();
$('#node-input-singleAttribute').prop('checked', true);
} else {
$('.single-attribute-wrapper').hide();
$('#node-input-singleAttribute').prop('checked', false);
}
// Show option for selecting tag if singleAttribute is true
$('#node-input-singleAttribute').change(() => {
let singleAttribute = $('#node-input-singleAttribute').is(':checked');
if (singleAttribute) {
$('.single-attribute-wrapper').show();
} else {
$('.single-attribute-wrapper').hide();
}
});
// Show attribute help text on click
$('#attribute-config-info').click(() => {
const isHidden = $('#attribute-config-help').is(':hidden');
if (isHidden) {
$('#attribute-config-help').show();
} else {
$('#attribute-config-help').hide();
}
});
// Generate typed input fields
$('#node-input-payloadSource').typedInput({
default: 'msg',
types: ['msg', 'flow', 'global', 'str', 'num', 'jsonata', 'env'],
typeField: $('#node-input-payloadType'),
});
$('#node-input-deviceInfo').typedInput({
default: 'json',
types: ['json'],
});
},
});
</script>
<script type="text/html" data-template-name="tulip-machine-attribute">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name" />
</div>
<div class="form-row">
<label for="node-input-apiAuth"
><i class="fa fa-user-circle"></i> Tulip API Authentication</label
>
<input id="node-input-apiAuth" />
</div>
<div class="form-row">
<label>
Attribute<br>Config
<i id="attribute-config-info" class="fa fa-info-circle"></i>
</label>
<input
id="node-input-singleAttribute"
type="checkbox"
autocomplete="off"
style="margin-left:10px; vertical-align:top; width:auto"
/>
<label for="node-input-singleAttribute" style="width:auto; vertical-align:top">
Single attribute
</label>
</div>
<div id="attribute-config-help" class="form-tips" style="margin:12px" hidden>
<p>
If checked, specify a single machine attribute to send data to (identified by
<code>machineId</code> and <code>attributeId</code>). Write values to that attribute
through the configured attribute source.
</p>
<p>
If not checked, you can dynamically pass as <code>msg.payload</code> a list of attributes
and values to write.
For example, set <code>msg.payload</code> to
<pre><code>[
{
"machineId": "{machineId}",
"attributeId": "{attributeId}",
"value": {value}
}
]</code></pre>
to write the string <code>"val1"</code> to the string-type attribute
with id <code>attributeId</code> for machine with id <code>machineId</code>.
</p>
</div>
<div class="form-row single-attribute-wrapper">
<label for="node-input-deviceInfo"><i class="fa fa-clipboard"></i> Device Info</label>
<input type="text" id="node-input-deviceInfo" style="width:70%"/>
</div>
<div class="form-tips single-attribute-wrapper">
<b>Tip:</b> This field can be copy-pasted from your Tulip factory instance.
Navigate to Shop Floor > Machines, select your machine from the Machine
Library, and click the clipboard icon next to the target Machine Attribute
to copy-paste its device info.
</div>
<hr />
<div class="form-row">
<h4>Advanced</h4>
</div>
<br>
<div class="form-row">
<label for="node-input-keepAlive"> HTTP Options</label>
<input
type="checkbox"
id="node-input-keepAlive"
autocomplete="off"
style="margin-left:10px; vertical-align:top; width:auto"
/>
<label for="node-input-keepAlive" style="width:auto">
Enable Connection Keep-Alive
</label>
</div>
<div class="form-row" id="div-node-input-keepAliveMsecs" hidden>
<label for="node-input-keepAliveMsecs"> </label>
<span style="width:auto">Keep-Alive Initial Delay (ms)</span>
<input
type="number"
min="0"
step="1"
id="node-input-keepAliveMsecs"
value="10000"
style="width:auto"
/>
</div>
<br>
<div class="form-row">
<label for="node-input-retainMsgProps"> Output</label>
<input
type="checkbox"
id="node-input-retainMsgProps"
autocomplete="off"
style="margin-left:10px; vertical-align:top; width:auto"
/>
<label for="node-input-retainMsgProps" style="width:auto">Retain Input Message Properties</label>
</div>
<br>
<div class="form-row single-attribute-wrapper">
<label for="node-input-payloadSource"> Attribute Source</label>
<input type="text" id="node-input-payloadSource" style="width:70%" />
<input type="hidden" id="node-input-payloadType" />
</div>
</script>
<script type="text/html" data-help-name="tulip-machine-attribute">
<p>Sends data to machine attribute endpoints using the Tulip Machine API.</p>
<h3>Inputs (single attribute)</h3>
<dl class="message-properties">
<dt>
payload
<span class="property-type">string | boolean | integer | float</span>
</dt>
<dd>
The value of the attribute to send. Should match the type of the machine attribute endpoint.
</dd>
<dt class="optional">
machineId
<span class="property-type">string</span>
</dt>
<dd>
Value to override the <code>machineId</code> set in "Device Info".
</dd>
<dt class="optional">
attributeId
<span class="property-type">string</span>
</dt>
<dd>
Value to override the <code>attributeId</code> set in "Device Info".
</dd>
<dt class="optional">
headers
<span class="property-type">JSON</span>
</dt>
<dd>
HTTP headers to include in the request. If a POST or PUT request, the header "Content-Type"
will be set to "application/json" and will override <code>msg.headers["Content-Type"]</code>.
</dd>
</dl>
<h3>Inputs (not single attribute)</h3>
<dl class="message-properties">
<dt>
payload
<span class="property-type">Object[]</span>
</dt>
<dd>
A list of { machineId, attributeId, value } objects to write in one batched Machine API request.
</dd>
<dt class="optional">
headers
<span class="property-type">JSON</span>
</dt>
<dd>
HTTP headers to include in the request. If a POST or PUT request, the header "Content-Type"
will be set to "application/json" and will override <code>msg.headers["Content-Type"]</code>.
</dd>
</dl>
<h3>Outputs</h3>
<ol class="node-ports">
<dl class="message-properties">
<dt>response <span class="property-type">JSON</span></dt>
<dd>The HTTP response object from the API request.</dd>
<dt>payload</dt>
<dd>The parsed response body.</dd>
</dl>
</ol>
<h3>Overview</h3>
<p>
Each <code>tulip-machine-attribute</code> node is configured to send data to the Tulip Machine API.
If "Single attribute" is checked, then the node will send data to a a single machine attribute
for a single machine on your Tulip factory instance. If not checked, then the node can send data to
a dynamically-selected attribute or list of attributes.
This authentication node can be shared between <code>tulip-machine-attribute</code> nodes.
</p>
<h3>Details</h3>
<p>
For every <code>msg</code>, the node will by default send an POST request to
<a>https://[your-factory-url]/api/v3/attributes/report</a> with request body:
<pre><code>{
"attributes": [
{
"machineId": deviceInfo.machineId,
"attributeId": deviceInfo.attributeId,
"value": msg.payload
}
]
}</code></pre>
</p>
<p>
For basic operation, only <code>msg.payload</code> needs to be passed into the node, and the <code>deviceInfo</code> can be
copy-pasted into "Device Info" from the Machine page on your factory instance.
</p>
<h3>Advanced Operation</h3>
<p>
For more advanced operation, you can specify the attribute value to come from a source other than <code>msg.payload</code>.
Supported sources are: properties of <code>msg</code>, <code>flow</code>, or <code>global</code>; strings; numbers; JSONata
expressions; environment variables.
</p>
<p>
Select "Retain Input Message Properties" to keep all properties of the input message in the output message, otherwise they will be cleared.
If an input message property conflicts with a property set by this node (ex: <code>msg.response</code>) then the input message property
will be overwritten.
</p>
<p>
You can also override the properties in the "Device Info" field with <code>msg</code> properties to configure the node to send
to different endpoints. Specifically, <code>msg.attributeId</code> will override <code>deviceInfo.attributeId</code>, and
<code>msg.machineId</code> will override <code>deviceInfo.machineId</code>. If you override either property with every
<code>msg</code>, it is not needed in the "Device Info" field. For example, if every <code>msg</code> has both <code>machineId</code>
and <code>attribute</code> set, you can leave "Device Info" as <code>{}</code>.
</p>
<p>
If "Single attribute" is unchecked, then you can instead performed batched writes setting <code>msg.payload</code> to a list of attributes,
each in the format <code>{attributeId, machineId, value}</code>.
</p>
<p>
Lastly, you can configure the HTTP agent to use keep-alive by checking "Enable Connection Keep-Alive" (default=<code>true</code>),
and set the agent keepAliveMsecs (default=<code>10000ms</code>). Note that this sets the keep-alive behaviour of the HTTP agent,
which is different than setting the "Connection": "Keep-Alive" HTTP header (which can be set through <code>msg.headers</code>).
</p>
<h3>References</h3>
<ul>
<li>
<a href=https://support.tulip.co/en/articles/4896233-using-node-red-with-edge-mc><b>Using Node-RED with Edge MC</b></a>
</li>
<li>
<b>Tulip API Documentation:</b> available at <a>https://[your-factory-url]/apiDocs</a>, see <code>/attributes/report</code> endpoint.
</li>
</ul>
</script>