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.
@@ -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
+ };