node-red-trexmes-commands 0.0.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/LICENSE +674 -0
- package/README.md +25 -0
- package/package.json +30 -0
- package/src/icons/trex.png +0 -0
- package/src/locales/en-US/trexcommands.html +166 -0
- package/src/locales/en-US/trexcommands.json +58 -0
- package/src/trexcommands.html +388 -0
- package/src/trexcommands.js +417 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
|
|
2
|
+
module.exports = function (RED) {
|
|
3
|
+
'use strict';
|
|
4
|
+
const mustache = require('mustache');
|
|
5
|
+
const sql = require('mssql');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* checks if `n` is a valid number.
|
|
9
|
+
* @param {string | number} n
|
|
10
|
+
*/
|
|
11
|
+
function isNumber (n) {
|
|
12
|
+
return !isNaN(parseFloat(n)) && isFinite(n);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parse a string or number into an integer and return it.
|
|
17
|
+
* If `n` cannot be parsed, `defaultValue` is returned instead.
|
|
18
|
+
* @param {string | number} n
|
|
19
|
+
* @param {boolean} defaultValue - the value to return if `n` is not a valid number
|
|
20
|
+
* @returns {integer} `n` parsed to an integer or `defaultValue` if `n` is invalid
|
|
21
|
+
*/
|
|
22
|
+
function safeParseInt (n, defaultValue) {
|
|
23
|
+
try {
|
|
24
|
+
const x = parseInt(n);
|
|
25
|
+
if (isNumber(x)) {
|
|
26
|
+
return x;
|
|
27
|
+
}
|
|
28
|
+
} catch (error) {
|
|
29
|
+
// do nothing
|
|
30
|
+
}
|
|
31
|
+
return defaultValue;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function checkPayload(payload) {
|
|
35
|
+
let result = { "isOk" : false };
|
|
36
|
+
if(!payload.WorkstationId){
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
result.wsId = payload.WorkstationId;
|
|
40
|
+
|
|
41
|
+
if(payload.operationMode == "1" ) { // Load Job Plan
|
|
42
|
+
if(!payload.PlanId){
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
result.command = JSON.stringify({ "WorkstationId" : payload.WorkstationId, "PlanId": payload.PlanId });
|
|
46
|
+
result.isOk = true;
|
|
47
|
+
}
|
|
48
|
+
else if(payload.operationMode == "2" ) { // Change Job Plan
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
else if(payload.operationMode == "3" ) { // Save Production
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function sqlNgpCommandQueInsert(record) {
|
|
59
|
+
let sql =
|
|
60
|
+
`DECLARE
|
|
61
|
+
@maxID INT,
|
|
62
|
+
@companyId INT,
|
|
63
|
+
@userID INT,
|
|
64
|
+
@wsId INT,
|
|
65
|
+
@cmdId INT,
|
|
66
|
+
@command NVARCHAR(MAX)
|
|
67
|
+
|
|
68
|
+
EXEC DBO.GETTABLEMAXVALUE 'NGPCOMMANDQUEUE', 'COMMANDID', @maxID OUTPUT SELECT @maxID maxID
|
|
69
|
+
|
|
70
|
+
set @userID = ${record.userId}
|
|
71
|
+
set @companyId = ${record.companyId}
|
|
72
|
+
set @wsId = ${record.wsId}
|
|
73
|
+
set @command = '${record.command}'
|
|
74
|
+
set @cmdId = ${record.cmdId}
|
|
75
|
+
|
|
76
|
+
INSERT INTO NGPCOMMANDQUEUE (COMPANYID, COMMANDID,COMMANDTYPE,PWORKSTATIONID,COMMAND,ISPROCESSED,INSERTUSERID,INSERTDATE)
|
|
77
|
+
values(@companyId, @maxId, @cmdId, @wsId, @command, 0, @userId, getdate())`;
|
|
78
|
+
|
|
79
|
+
return sql;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function connection(config) {
|
|
83
|
+
RED.nodes.createNode(this, config);
|
|
84
|
+
const node = this;
|
|
85
|
+
|
|
86
|
+
//add mustache transformation to connection object
|
|
87
|
+
const configStr = JSON.stringify(config);
|
|
88
|
+
const transform1 = mustache.render(configStr, process.env);
|
|
89
|
+
config = JSON.parse(transform1);
|
|
90
|
+
|
|
91
|
+
//add mustache transformation to credentials object
|
|
92
|
+
try {
|
|
93
|
+
const credStr = JSON.stringify(node.credentials);
|
|
94
|
+
const transform2 = mustache.render(credStr, process.env);
|
|
95
|
+
node.credentials = JSON.parse(transform2);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error(error);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
node.config = {
|
|
101
|
+
user: (node.credentials ? node.credentials.username : '') || '',
|
|
102
|
+
password: (node.credentials ? node.credentials.password : '') || '',
|
|
103
|
+
domain: node.credentials ? node.credentials.domain : '',
|
|
104
|
+
server: config.server,
|
|
105
|
+
database: config.database,
|
|
106
|
+
companyId: config.companyId,
|
|
107
|
+
userId: config.userId,
|
|
108
|
+
options: {
|
|
109
|
+
port: config.port ? safeParseInt(config.port, 1433) : undefined,
|
|
110
|
+
tdsVersion: config.tdsVersion || '7_4',
|
|
111
|
+
encrypt: config.encyption,
|
|
112
|
+
trustServerCertificate: !((config.trustServerCertificate === 'false' || config.trustServerCertificate === false)), //defaults to true for backwards compatibility - TODO: Review this default.
|
|
113
|
+
useUTC: config.useUTC,
|
|
114
|
+
connectTimeout: config.connectTimeout ? safeParseInt(config.connectTimeout, 15000) : undefined,
|
|
115
|
+
requestTimeout: config.requestTimeout ? safeParseInt(config.requestTimeout, 15000) : undefined,
|
|
116
|
+
cancelTimeout: config.cancelTimeout ? safeParseInt(config.cancelTimeout, 5000) : undefined,
|
|
117
|
+
camelCaseColumns: (config.camelCaseColumns === 'true' || config.camelCaseColumns === true) ? true : undefined, //defaults to undefined.
|
|
118
|
+
parseJSON: !!((config.parseJSON === 'true' || config.parseJSON === true)), //defaults to true.
|
|
119
|
+
enableArithAbort: !((config.enableArithAbort === 'false' || config.enableArithAbort === false)), //defaults to true.
|
|
120
|
+
readOnlyIntent: (config.readOnlyIntent === 'true' || config.readOnlyIntent === true) //defaults to false.
|
|
121
|
+
},
|
|
122
|
+
pool: {
|
|
123
|
+
max: safeParseInt(config.pool, 5),
|
|
124
|
+
min: 0,
|
|
125
|
+
idleTimeoutMillis: 3000
|
|
126
|
+
//log: (message, logLevel) => console.log(`POOL: [${logLevel}] ${message}`)
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
//config options seem to differ between pool and tedious connection
|
|
131
|
+
//so for compatibility I just repeat the ones that differ so they get picked up in _poolCreate ()
|
|
132
|
+
node.config.port = node.config.options.port;
|
|
133
|
+
node.config.connectionTimeout = node.config.options.connectTimeout;
|
|
134
|
+
node.config.requestTimeout = node.config.options.requestTimeout;
|
|
135
|
+
node.config.cancelTimeout = node.config.options.cancelTimeout;
|
|
136
|
+
node.config.encrypt = node.config.options.encrypt;
|
|
137
|
+
|
|
138
|
+
node.connectedNodes = [];
|
|
139
|
+
|
|
140
|
+
node.connectionCleanup = function (quiet) {
|
|
141
|
+
const updateStatusAndLog = !quiet;
|
|
142
|
+
try {
|
|
143
|
+
if (node.poolConnect) {
|
|
144
|
+
if (updateStatusAndLog) node.log(`Disconnecting server : ${node.config.server}, database : ${node.config.database}, port : ${node.config.options.port}, user : ${node.config.user}`);
|
|
145
|
+
node.poolConnect.then(_ => _.close()).catch(e => { console.error(e); });
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// node-mssql 5.x to 6.x changes
|
|
151
|
+
// ConnectionPool.close() now returns a promise / callbacks will be executed once closing of the
|
|
152
|
+
if (node.pool && node.pool.close) {
|
|
153
|
+
node.pool.close().catch(() => {})
|
|
154
|
+
}
|
|
155
|
+
if (updateStatusAndLog) node.status({ fill: 'grey', shape: 'dot', text: 'disconnected' });
|
|
156
|
+
node.poolConnect = null;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
node.pool = new sql.ConnectionPool(node.config);
|
|
160
|
+
node.pool.on('error', err => {
|
|
161
|
+
node.error(err);
|
|
162
|
+
node.connectionCleanup();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
node.connect = function () {
|
|
166
|
+
if (node.poolConnect) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
node.status({
|
|
170
|
+
fill: 'yellow',
|
|
171
|
+
shape: 'dot',
|
|
172
|
+
text: 'connecting'
|
|
173
|
+
});
|
|
174
|
+
node.poolConnect = node.pool.connect();
|
|
175
|
+
return node.poolConnect;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
node.execSql = async function (queryMode, sqlQuery, params, paramValues, callback) {
|
|
179
|
+
const _info = [];
|
|
180
|
+
try {
|
|
181
|
+
if (!node.poolConnect && !!(await node.connect())) {
|
|
182
|
+
node.log(`Connected to server : ${node.config.server}, database : ${node.config.database}, port : ${node.config.options.port}, user : ${node.config.user}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const req = node.pool.request();
|
|
186
|
+
|
|
187
|
+
req.on('info', info => {
|
|
188
|
+
_info.push(info);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
let result;
|
|
192
|
+
switch (queryMode) {
|
|
193
|
+
/*case 'bulk':
|
|
194
|
+
result = await req.bulk(bulkTable);
|
|
195
|
+
break;*/
|
|
196
|
+
case 'execute':
|
|
197
|
+
result = await req.execute(sqlQuery);
|
|
198
|
+
break;
|
|
199
|
+
default:
|
|
200
|
+
result = await req.query(sqlQuery);
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
callback(null, result, _info);
|
|
205
|
+
} catch (e) {
|
|
206
|
+
node.log(`Error connecting to server : ${node.config.server}, database : ${node.config.database}, port : ${node.config.options.port}, user : ${node.config.user}`);
|
|
207
|
+
console.error(e);
|
|
208
|
+
node.poolConnect = null;
|
|
209
|
+
callback(e);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
node.disconnect = function (nodeId) {
|
|
214
|
+
const index = node.connectedNodes.indexOf(nodeId);
|
|
215
|
+
if (index >= 0) {
|
|
216
|
+
node.connectedNodes.splice(index, 1);
|
|
217
|
+
}
|
|
218
|
+
if (node.connectedNodes.length === 0) {
|
|
219
|
+
node.connectionCleanup();
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
RED.nodes.registerType('trexMes-CN', connection, {
|
|
225
|
+
credentials: {
|
|
226
|
+
username: {
|
|
227
|
+
type: 'text'
|
|
228
|
+
},
|
|
229
|
+
password: {
|
|
230
|
+
type: 'password'
|
|
231
|
+
},
|
|
232
|
+
domain: {
|
|
233
|
+
type: 'text'
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
function trexcommands(config) {
|
|
240
|
+
RED.nodes.createNode(this, config);
|
|
241
|
+
const trexmesCN = RED.nodes.getNode(config.trexmesCN);
|
|
242
|
+
const node = this;
|
|
243
|
+
|
|
244
|
+
node.query = config.query;
|
|
245
|
+
node.outField = 'payload';
|
|
246
|
+
node.returnType = 0;
|
|
247
|
+
node.throwErrors = !(!config.throwErrors || config.throwErrors === '0');
|
|
248
|
+
|
|
249
|
+
node.modeOptType = config.modeOptType || '1';
|
|
250
|
+
|
|
251
|
+
const setResult = function (msg, field, value, returnType = 0) {
|
|
252
|
+
const set = (obj, path, val) => {
|
|
253
|
+
const keys = path.split('.');
|
|
254
|
+
const lastKey = keys.pop();
|
|
255
|
+
// eslint-disable-next-line no-return-assign
|
|
256
|
+
const lastObj = keys.reduce((obj, key) =>
|
|
257
|
+
obj[key] = obj[key] || {},
|
|
258
|
+
obj);
|
|
259
|
+
lastObj[lastKey] = val;
|
|
260
|
+
};
|
|
261
|
+
set(msg, field, value.recordset);
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
node.processError = function (err, msg) {
|
|
265
|
+
let errMsg = 'Error';
|
|
266
|
+
if (typeof err === 'string') {
|
|
267
|
+
errMsg = err;
|
|
268
|
+
msg.error = err;
|
|
269
|
+
} else if (err && err.message) {
|
|
270
|
+
errMsg = err.message;
|
|
271
|
+
|
|
272
|
+
if (err.precedingErrors !== undefined &&
|
|
273
|
+
err.precedingErrors.length &&
|
|
274
|
+
err.precedingErrors[0].originalError !== undefined &&
|
|
275
|
+
err.precedingErrors[0].originalError.info !== null &&
|
|
276
|
+
err.precedingErrors[0].originalError.info.message !== null) {
|
|
277
|
+
errMsg += ' (' + err.precedingErrors[0].originalError.info.message + ')';
|
|
278
|
+
}
|
|
279
|
+
//Make an error object from the err. NOTE: We cant just assign err to msg.error as a promise
|
|
280
|
+
//rejection occurs when the node has 2 wires on the output.
|
|
281
|
+
//(redUtil.cloneMessage(m) causes error "node-red Cannot assign to read only property 'originalError'")
|
|
282
|
+
msg.error = {
|
|
283
|
+
class: err.class,
|
|
284
|
+
code: err.code,
|
|
285
|
+
lineNumber: err.lineNumber,
|
|
286
|
+
message: err.message,
|
|
287
|
+
details: errMsg,
|
|
288
|
+
name: err.name,
|
|
289
|
+
number: err.number,
|
|
290
|
+
procName: err.procName,
|
|
291
|
+
serverName: err.serverName,
|
|
292
|
+
state: err.state,
|
|
293
|
+
toString: function () {
|
|
294
|
+
return this.message;
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
node.status({
|
|
300
|
+
fill: 'red',
|
|
301
|
+
shape: 'ring',
|
|
302
|
+
text: errMsg
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
if (node.throwErrors) {
|
|
306
|
+
node.error(msg.error, msg);
|
|
307
|
+
} else {
|
|
308
|
+
node.log(err);
|
|
309
|
+
node.send(msg);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
node.on('input', function (msg) {
|
|
314
|
+
node.status({}); //clear node status
|
|
315
|
+
delete msg.error; //remove any .error property passed in from previous node
|
|
316
|
+
|
|
317
|
+
let splitted = node.modeOptType.split('|');
|
|
318
|
+
msg.operationMode = "1";
|
|
319
|
+
if(splitted.length > 1) {
|
|
320
|
+
msg.operationMode = splitted[0];
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
doSQL(node, msg);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
function doSQL(node, msg) {
|
|
327
|
+
node.status({
|
|
328
|
+
fill: 'blue',
|
|
329
|
+
shape: 'dot',
|
|
330
|
+
text: 'requesting'
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
try {
|
|
334
|
+
|
|
335
|
+
let query = "";
|
|
336
|
+
let resultCmd = checkPayload(msg);
|
|
337
|
+
if ( resultCmd.isOk == false ) {
|
|
338
|
+
node.status({
|
|
339
|
+
fill: 'red',
|
|
340
|
+
shape: 'dot',
|
|
341
|
+
text: 'payload data not enough!'
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
let record = {};
|
|
348
|
+
record.companyId = trexmesCN.config.companyId;
|
|
349
|
+
record.userId = trexmesCN.config.userId;
|
|
350
|
+
record.cmdId = msg.operationMode;
|
|
351
|
+
record.command = resultCmd.command;
|
|
352
|
+
record.wsId = resultCmd.wsId;
|
|
353
|
+
|
|
354
|
+
query = sqlNgpCommandQueInsert(record);
|
|
355
|
+
//node.log("query: " + query);
|
|
356
|
+
|
|
357
|
+
trexmesCN.execSql("", query, [], {}, function (err, data, info) {
|
|
358
|
+
if (err) {
|
|
359
|
+
node.processError(err, msg);
|
|
360
|
+
} else {
|
|
361
|
+
node.status({
|
|
362
|
+
fill: 'yellow',
|
|
363
|
+
shape: 'dot',
|
|
364
|
+
text: 'Sended, aprove waiting.. '
|
|
365
|
+
});
|
|
366
|
+
setResult(msg, node.outField, data, node.returnType);
|
|
367
|
+
//node.send(msg);
|
|
368
|
+
//node.send(msg.payload.length);
|
|
369
|
+
let commandId = 0;
|
|
370
|
+
if(msg.payload.length>0) {
|
|
371
|
+
commandId = msg.payload[0].maxID;
|
|
372
|
+
}
|
|
373
|
+
//Process onay bekleniyor
|
|
374
|
+
setTimeout(function () {
|
|
375
|
+
query = "select COMMANDID,ISPROCESSED from NGPCOMMANDQUEUE where COMMANDID=" + commandId;
|
|
376
|
+
//node.log(query);
|
|
377
|
+
trexmesCN.execSql("", query, [], {}, function (err, data, info) {
|
|
378
|
+
if (err) {
|
|
379
|
+
node.processError(err, msg);
|
|
380
|
+
} else {
|
|
381
|
+
setResult(msg, node.outField, data, node.returnType);
|
|
382
|
+
let isProcessed = 0;
|
|
383
|
+
if(msg.payload.length>0) {
|
|
384
|
+
isProcessed = msg.payload[0].ISPROCESSED;
|
|
385
|
+
}
|
|
386
|
+
node.send(msg);
|
|
387
|
+
if(isProcessed == 1) {
|
|
388
|
+
node.status({
|
|
389
|
+
fill: 'green',
|
|
390
|
+
shape: 'dot',
|
|
391
|
+
text: 'done'
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
node.status({
|
|
396
|
+
fill: 'orange',
|
|
397
|
+
shape: 'dot',
|
|
398
|
+
text: 'command not processed in 2 sec.'
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
}, 2000);
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
} catch (err) {
|
|
407
|
+
node.processError(err, msg);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
node.on('close', function () {
|
|
412
|
+
trexmesCN.disconnect(node.id);
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
RED.nodes.registerType('trexMes-CMD', trexcommands);
|
|
417
|
+
};
|