node-opcua-file-transfer 2.76.0 → 2.77.0

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.
@@ -1,493 +1,493 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.installFileType = exports.defaultMaxSize = exports.getFileData = exports.FileTypeData = void 0;
13
- /**
14
- * @module node-opcua-file-transfer
15
- */
16
- const fsOrig = require("fs");
17
- const util_1 = require("util");
18
- const node_opcua_assert_1 = require("node-opcua-assert");
19
- const node_opcua_debug_1 = require("node-opcua-debug");
20
- const node_opcua_nodeid_1 = require("node-opcua-nodeid");
21
- const node_opcua_status_code_1 = require("node-opcua-status-code");
22
- const node_opcua_variant_1 = require("node-opcua-variant");
23
- const open_mode_1 = require("../open_mode");
24
- const debugLog = (0, node_opcua_debug_1.make_debugLog)("FileType");
25
- const errorLog = (0, node_opcua_debug_1.make_errorLog)("FileType");
26
- const warningLog = (0, node_opcua_debug_1.make_warningLog)("FileType");
27
- const doDebug = (0, node_opcua_debug_1.checkDebugFlag)("FileType");
28
- /**
29
- *
30
- */
31
- class FileTypeData {
32
- constructor(options, file) {
33
- this.filename = "";
34
- this.maxSize = 0;
35
- this.mimeType = "";
36
- this._openCount = 0;
37
- this._fileSize = 0;
38
- this.file = file;
39
- this._fs = options.fileSystem || fsOrig;
40
- this.filename = options.filename;
41
- this.maxSize = options.maxSize;
42
- this.mimeType = options.mineType || "";
43
- // openCount indicates the number of currently valid file handles on the file.
44
- this._openCount = 0;
45
- file.openCount.bindVariable({
46
- get: () => new node_opcua_variant_1.Variant({ dataType: node_opcua_variant_1.DataType.UInt16, value: this._openCount })
47
- }, true);
48
- file.openCount.minimumSamplingInterval = 0; // changed immediatly
49
- file.size.bindVariable({
50
- get: () => new node_opcua_variant_1.Variant({ dataType: node_opcua_variant_1.DataType.UInt64, value: this._fileSize })
51
- }, true);
52
- file.size.minimumSamplingInterval = 0; // changed immediatly
53
- this.refresh();
54
- }
55
- set openCount(value) {
56
- this._openCount = value;
57
- this.file.openCount.touchValue();
58
- }
59
- get openCount() {
60
- return this._openCount;
61
- }
62
- set fileSize(value) {
63
- this._fileSize = value;
64
- this.file.size.touchValue();
65
- }
66
- get fileSize() {
67
- return this._fileSize;
68
- }
69
- /**
70
- * refresh position and size
71
- * this method should be call by the server if the file
72
- * is modified externally
73
- *
74
- */
75
- refresh() {
76
- return __awaiter(this, void 0, void 0, function* () {
77
- const abstractFs = this._fs;
78
- // lauch an async request to update filesize
79
- yield (function extractFileSize(self) {
80
- return __awaiter(this, void 0, void 0, function* () {
81
- try {
82
- if (!abstractFs.existsSync(self.filename)) {
83
- self._fileSize = 0;
84
- return;
85
- }
86
- const stat = yield (0, util_1.promisify)(abstractFs.stat)(self.filename);
87
- self._fileSize = stat.size;
88
- debugLog("original file size ", self.filename, " size = ", self._fileSize);
89
- }
90
- catch (err) {
91
- self._fileSize = 0;
92
- if (err instanceof Error) {
93
- warningLog("Cannot access file ", self.filename, err.message);
94
- }
95
- }
96
- });
97
- })(this);
98
- });
99
- }
100
- }
101
- exports.FileTypeData = FileTypeData;
102
- function getFileData(opcuaFile2) {
103
- return opcuaFile2.$fileData;
104
- }
105
- exports.getFileData = getFileData;
106
- function _prepare(addressSpace, context) {
107
- const _context = addressSpace;
108
- _context.$$currentFileHandle = _context.$$currentFileHandle ? _context.$$currentFileHandle : 41;
109
- _context.$$files = _context.$$files || {};
110
- return _context;
111
- }
112
- function _getSessionId(context) {
113
- var _a;
114
- if (!context.session) {
115
- return new node_opcua_nodeid_1.NodeId();
116
- }
117
- (0, node_opcua_assert_1.assert)(context.session && context.session.getSessionId);
118
- return ((_a = context.session) === null || _a === void 0 ? void 0 : _a.getSessionId()) || new node_opcua_nodeid_1.NodeId();
119
- }
120
- function _addFile(addressSpace, context, openMode) {
121
- const _context = _prepare(addressSpace, context);
122
- _context.$$currentFileHandle++;
123
- const fileHandle = _context.$$currentFileHandle;
124
- const sessionId = _getSessionId(context);
125
- const _fileData = {
126
- fd: -1,
127
- handle: fileHandle,
128
- openMode,
129
- position: [0, 0],
130
- size: 0,
131
- sessionId
132
- };
133
- _context.$$files[fileHandle] = _fileData;
134
- return fileHandle;
135
- }
136
- function _getFileInfo(addressSpace, context, fileHandle) {
137
- const _context = _prepare(addressSpace, context);
138
- const _fileInfo = _context.$$files[fileHandle];
139
- const sessionId = _getSessionId(context);
140
- if (!_fileInfo || !(0, node_opcua_nodeid_1.sameNodeId)(_fileInfo.sessionId, sessionId)) {
141
- errorLog("Invalid session ID this file descriptor doesn't belong to this session");
142
- return null;
143
- }
144
- return _fileInfo;
145
- }
146
- function _close(addressSpace, context, fileData) {
147
- const _context = _prepare(addressSpace, context);
148
- delete _context.$$files[fileData.fd];
149
- }
150
- function toNodeJSMode(opcuaMode) {
151
- let flags;
152
- switch (opcuaMode) {
153
- case open_mode_1.OpenFileMode.Read:
154
- flags = "r";
155
- break;
156
- case open_mode_1.OpenFileMode.ReadWrite:
157
- case open_mode_1.OpenFileMode.Write:
158
- flags = "w+";
159
- break;
160
- case open_mode_1.OpenFileMode.ReadWriteAppend:
161
- case open_mode_1.OpenFileMode.WriteAppend:
162
- flags = "a+";
163
- break;
164
- case open_mode_1.OpenFileMode.WriteEraseExisting:
165
- case open_mode_1.OpenFileMode.ReadWriteEraseExisting:
166
- flags = "w+";
167
- break;
168
- default:
169
- flags = "?";
170
- break;
171
- }
172
- return flags;
173
- }
174
- /**
175
- * Open is used to open a file represented by an Object of FileType.
176
- * When a client opens a file it gets a file handle that is valid while the
177
- * session is open. Clients shall use the Close Method to release the handle
178
- * when they do not need access to the file anymore. Clients can open the
179
- * same file several times for read.
180
- * A request to open for writing shall return Bad_NotWritable when the file is
181
- * already opened.
182
- * A request to open for reading shall return Bad_NotReadable
183
- * when the file is already opened for writing.
184
- *
185
- * Method Result Codes (defined in Call Service)
186
- * Result Code Description
187
- * BadNotReadable File might be locked and thus not readable.
188
- * BadNotWritable The file is locked and thus not writable.
189
- * BadInvalidState
190
- * BadInvalidArgument Mode setting is invalid.
191
- * BadNotFound .
192
- * BadUnexpectedError
193
- *
194
- * @private
195
- */
196
- function _openFile(inputArguments, context) {
197
- return __awaiter(this, void 0, void 0, function* () {
198
- const addressSpace = this.addressSpace;
199
- const mode = inputArguments[0].value;
200
- /**
201
- * mode (Byte) Indicates whether the file should be opened only for read operations
202
- * or for read and write operations and where the initial position is set.
203
- * The mode is an 8-bit unsigned integer used as bit mask with the structure
204
- * defined in the following table:
205
- * Field Bit Description
206
- * Read 0 The file is opened for reading. If this bit is not
207
- * set the Read Method cannot be executed.
208
- * Write 1 The file is opened for writing. If this bit is not
209
- * set the Write Method cannot be executed.
210
- * EraseExisting 2 This bit can only be set if the file is opened for writing
211
- * (Write bit is set). The existing content of the file is
212
- * erased and an empty file is provided.
213
- * Append 3 When the Append bit is set the file is opened at end
214
- * of the file, otherwise at begin of the file.
215
- * The SetPosition Method can be used to change the position.
216
- * Reserved 4:7 Reserved for future use. Shall always be zero.
217
- */
218
- // see https://nodejs.org/api/fs.html#fs_file_system_flags
219
- const flags = toNodeJSMode(mode);
220
- if (flags === "?") {
221
- errorLog("Invalid mode " + open_mode_1.OpenFileMode[mode] + " (" + mode + ")");
222
- return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
223
- }
224
- /**
225
- * fileHandle (UInt32) A handle for the file used in other method calls indicating not the
226
- * file (this is done by the Object of the Method call) but the access
227
- * request and thus the position in the file. The fileHandle is generated
228
- * by the server and is unique for the Session. Clients cannot transfer the
229
- * fileHandle to another Session but need to get a new fileHandle by calling
230
- * the Open Method.
231
- */
232
- const fileHandle = _addFile(addressSpace, context, mode);
233
- const _fileInfo = _getFileInfo(addressSpace, context, fileHandle);
234
- if (!_fileInfo) {
235
- return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
236
- }
237
- const fileData = context.object.$fileData;
238
- const filename = fileData.filename;
239
- const abstractFs = _getFileSystem(context);
240
- try {
241
- _fileInfo.fd = yield (0, util_1.promisify)(abstractFs.open)(filename, flags);
242
- // update position
243
- _fileInfo.position = [0, 0];
244
- const fileLength = (yield (0, util_1.promisify)(abstractFs.stat)(filename)).size;
245
- _fileInfo.size = fileLength;
246
- // tslint:disable-next-line:no-bitwise
247
- if ((mode & open_mode_1.OpenFileModeMask.AppendBit) === open_mode_1.OpenFileModeMask.AppendBit) {
248
- _fileInfo.position[1] = fileLength;
249
- }
250
- if ((mode & open_mode_1.OpenFileModeMask.EraseExistingBit) === open_mode_1.OpenFileModeMask.EraseExistingBit) {
251
- _fileInfo.size = 0;
252
- }
253
- fileData.openCount += 1;
254
- }
255
- catch (err) {
256
- if (err instanceof Error) {
257
- errorLog(err.message);
258
- errorLog(err.stack);
259
- }
260
- return { statusCode: node_opcua_status_code_1.StatusCodes.BadUnexpectedError };
261
- }
262
- debugLog("Opening file handle ", fileHandle, "filename: ", fileData.filename, "openCount: ", fileData.openCount);
263
- const callMethodResult = {
264
- outputArguments: [
265
- {
266
- dataType: node_opcua_variant_1.DataType.UInt32,
267
- value: fileHandle
268
- }
269
- ],
270
- statusCode: node_opcua_status_code_1.StatusCodes.Good
271
- };
272
- return callMethodResult;
273
- });
274
- }
275
- function _getFileSystem(context) {
276
- const fs = context.object.$fs;
277
- return fs;
278
- }
279
- /**
280
- * Close is used to close a file represented by a FileType.
281
- * When a client closes a file the handle becomes invalid.
282
- *
283
- * @param inputArguments
284
- * @param context
285
- * @private
286
- */
287
- function _closeFile(inputArguments, context) {
288
- return __awaiter(this, void 0, void 0, function* () {
289
- const abstractFs = _getFileSystem(context);
290
- const addressSpace = this.addressSpace;
291
- const fileHandle = inputArguments[0].value;
292
- const _fileInfo = _getFileInfo(addressSpace, context, fileHandle);
293
- if (!_fileInfo) {
294
- return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
295
- }
296
- const data = context.object.$fileData;
297
- debugLog("Closing file handle ", fileHandle, "filename: ", data.filename, "openCount: ", data.openCount);
298
- yield (0, util_1.promisify)(abstractFs.close)(_fileInfo.fd);
299
- _close(addressSpace, context, _fileInfo);
300
- data.openCount -= 1;
301
- return {
302
- statusCode: node_opcua_status_code_1.StatusCodes.Good
303
- };
304
- });
305
- }
306
- /**
307
- * Read is used to read a part of the file starting from the current file position.
308
- * The file position is advanced by the number of bytes read.
309
- *
310
- * @param inputArguments
311
- * @param context
312
- * @private
313
- */
314
- function _readFile(inputArguments, context) {
315
- return __awaiter(this, void 0, void 0, function* () {
316
- const addressSpace = this.addressSpace;
317
- const abstractFs = _getFileSystem(context);
318
- // fileHandle A handle indicating the access request and thus indirectly the
319
- // position inside the file.
320
- const fileHandle = inputArguments[0].value;
321
- // Length Defines the length in bytes that should be returned in data, starting from the current
322
- // position of the file handle. If the end of file is reached all data until the end of the file is
323
- // returned. The Server is allowed to return less data than specified length.
324
- let length = inputArguments[1].value;
325
- // Only positive values are allowed.
326
- if (length < 0) {
327
- return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
328
- }
329
- const _fileInfo = _getFileInfo(addressSpace, context, fileHandle);
330
- if (!_fileInfo) {
331
- return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidState };
332
- }
333
- // tslint:disable-next-line:no-bitwise
334
- if ((_fileInfo.openMode & open_mode_1.OpenFileModeMask.ReadBit) === 0x0) {
335
- // open mode did not specify Read Flag
336
- return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidState };
337
- }
338
- length = Math.min(_fileInfo.size - _fileInfo.position[1], length);
339
- const data = Buffer.alloc(length);
340
- let ret = { bytesRead: 0 };
341
- try {
342
- // note: we do not util.promise here as it has a wierd behavior...
343
- ret = yield new Promise((resolve, reject) => abstractFs.read(_fileInfo.fd, data, 0, length, _fileInfo.position[1], (err, bytesRead, buff) => {
344
- if (err) {
345
- return reject(err);
346
- }
347
- return resolve({ bytesRead });
348
- }));
349
- _fileInfo.position[1] += ret.bytesRead;
350
- }
351
- catch (err) {
352
- if (err instanceof Error) {
353
- errorLog("Read error : ", err.message);
354
- }
355
- return { statusCode: node_opcua_status_code_1.StatusCodes.BadUnexpectedError };
356
- }
357
- // Data Contains the returned data of the file. If the ByteString is empty it indicates that the end
358
- // of the file is reached.
359
- return {
360
- outputArguments: [{ dataType: node_opcua_variant_1.DataType.ByteString, value: data.slice(0, ret.bytesRead) }],
361
- statusCode: node_opcua_status_code_1.StatusCodes.Good
362
- };
363
- });
364
- }
365
- function _writeFile(inputArguments, context) {
366
- return __awaiter(this, void 0, void 0, function* () {
367
- const addressSpace = this.addressSpace;
368
- const abstractFs = _getFileSystem(context);
369
- const fileHandle = inputArguments[0].value;
370
- const _fileInfo = _getFileInfo(addressSpace, context, fileHandle);
371
- if (!_fileInfo) {
372
- return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
373
- }
374
- // tslint:disable-next-line:no-bitwise
375
- if ((_fileInfo.openMode & open_mode_1.OpenFileModeMask.WriteBit) === 0x00) {
376
- // File has not been open with write mode
377
- return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidState };
378
- }
379
- const data = inputArguments[1].value;
380
- let ret = { bytesWritten: 0 };
381
- try {
382
- // note: we do not util.promise here as it has a wierd behavior...
383
- ret = yield new Promise((resolve, reject) => {
384
- abstractFs.write(_fileInfo.fd, data, 0, data.length, _fileInfo.position[1], (err, bytesWritten) => {
385
- if (err) {
386
- errorLog("Err", err);
387
- return reject(err);
388
- }
389
- return resolve({ bytesWritten });
390
- });
391
- });
392
- (0, node_opcua_assert_1.assert)(typeof ret.bytesWritten === "number");
393
- _fileInfo.position[1] += ret.bytesWritten;
394
- _fileInfo.size = Math.max(_fileInfo.size, _fileInfo.position[1]);
395
- const fileTypeData = context.object.$fileData;
396
- debugLog(fileTypeData.fileSize);
397
- fileTypeData.fileSize = Math.max(fileTypeData.fileSize, _fileInfo.position[1]);
398
- debugLog(fileTypeData.fileSize);
399
- }
400
- catch (err) {
401
- if (err instanceof Error) {
402
- errorLog("Write error : ", err.message);
403
- }
404
- return { statusCode: node_opcua_status_code_1.StatusCodes.BadUnexpectedError };
405
- }
406
- return {
407
- outputArguments: [],
408
- statusCode: node_opcua_status_code_1.StatusCodes.Good
409
- };
410
- });
411
- }
412
- function _setPositionFile(inputArguments, context) {
413
- return __awaiter(this, void 0, void 0, function* () {
414
- const addressSpace = this.addressSpace;
415
- const fileHandle = inputArguments[0].value;
416
- const position = inputArguments[1].value;
417
- const _fileInfo = _getFileInfo(addressSpace, context, fileHandle);
418
- if (!_fileInfo) {
419
- return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
420
- }
421
- _fileInfo.position = position;
422
- return { statusCode: node_opcua_status_code_1.StatusCodes.Good };
423
- });
424
- }
425
- function _getPositionFile(inputArguments, context) {
426
- return __awaiter(this, void 0, void 0, function* () {
427
- const addressSpace = this.addressSpace;
428
- const fileHandle = inputArguments[0].value;
429
- const _fileInfo = _getFileInfo(addressSpace, context, fileHandle);
430
- if (!_fileInfo) {
431
- return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
432
- }
433
- return {
434
- outputArguments: [
435
- {
436
- arrayType: node_opcua_variant_1.VariantArrayType.Scalar,
437
- dataType: node_opcua_variant_1.DataType.UInt64,
438
- value: _fileInfo.position
439
- }
440
- ],
441
- statusCode: node_opcua_status_code_1.StatusCodes.Good
442
- };
443
- });
444
- }
445
- exports.defaultMaxSize = 100000000;
446
- function install_method_handle_on_type(addressSpace) {
447
- const fileType = addressSpace.findObjectType("FileType");
448
- if (fileType.open.isBound()) {
449
- return;
450
- }
451
- fileType.open.bindMethod(_openFile);
452
- fileType.close.bindMethod(_closeFile);
453
- fileType.read.bindMethod(_readFile);
454
- fileType.write.bindMethod(_writeFile);
455
- fileType.setPosition.bindMethod(_setPositionFile);
456
- fileType.getPosition.bindMethod(_getPositionFile);
457
- }
458
- /**
459
- * bind all methods of a UAFile OPCUA node
460
- * @param file the OPCUA Node that has a typeDefinition of FileType
461
- * @param options the options
462
- */
463
- function installFileType(file, options) {
464
- if (file.$fileData) {
465
- errorLog("File already installed ", file.nodeId.toString(), file.browseName.toString());
466
- return;
467
- }
468
- file.$fs = options.fileSystem || fsOrig;
469
- // make sure that FileType methods are also bound.
470
- install_method_handle_on_type(file.addressSpace);
471
- // to protect the server we setup a maximum limit in bytes on the file
472
- // if the client try to access or set the position above this limit
473
- // the server will return an error
474
- options.maxSize = options.maxSize === undefined ? exports.defaultMaxSize : options.maxSize;
475
- const $fileData = new FileTypeData(options, file);
476
- file.$fileData = $fileData;
477
- // ----- install mime type
478
- if (options.mineType) {
479
- if (file.mimeType) {
480
- file.mimeType.bindVariable({
481
- get: () => file.$fileOptions.mineType
482
- });
483
- }
484
- }
485
- file.open.bindMethod(_openFile);
486
- file.close.bindMethod(_closeFile);
487
- file.read.bindMethod(_readFile);
488
- file.write.bindMethod(_writeFile);
489
- file.setPosition.bindMethod(_setPositionFile);
490
- file.getPosition.bindMethod(_getPositionFile);
491
- }
492
- exports.installFileType = installFileType;
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.installFileType = exports.defaultMaxSize = exports.getFileData = exports.FileTypeData = void 0;
13
+ /**
14
+ * @module node-opcua-file-transfer
15
+ */
16
+ const fsOrig = require("fs");
17
+ const util_1 = require("util");
18
+ const node_opcua_assert_1 = require("node-opcua-assert");
19
+ const node_opcua_debug_1 = require("node-opcua-debug");
20
+ const node_opcua_nodeid_1 = require("node-opcua-nodeid");
21
+ const node_opcua_status_code_1 = require("node-opcua-status-code");
22
+ const node_opcua_variant_1 = require("node-opcua-variant");
23
+ const open_mode_1 = require("../open_mode");
24
+ const debugLog = (0, node_opcua_debug_1.make_debugLog)("FileType");
25
+ const errorLog = (0, node_opcua_debug_1.make_errorLog)("FileType");
26
+ const warningLog = (0, node_opcua_debug_1.make_warningLog)("FileType");
27
+ const doDebug = (0, node_opcua_debug_1.checkDebugFlag)("FileType");
28
+ /**
29
+ *
30
+ */
31
+ class FileTypeData {
32
+ constructor(options, file) {
33
+ this.filename = "";
34
+ this.maxSize = 0;
35
+ this.mimeType = "";
36
+ this._openCount = 0;
37
+ this._fileSize = 0;
38
+ this.file = file;
39
+ this._fs = options.fileSystem || fsOrig;
40
+ this.filename = options.filename;
41
+ this.maxSize = options.maxSize;
42
+ this.mimeType = options.mineType || "";
43
+ // openCount indicates the number of currently valid file handles on the file.
44
+ this._openCount = 0;
45
+ file.openCount.bindVariable({
46
+ get: () => new node_opcua_variant_1.Variant({ dataType: node_opcua_variant_1.DataType.UInt16, value: this._openCount })
47
+ }, true);
48
+ file.openCount.minimumSamplingInterval = 0; // changed immediatly
49
+ file.size.bindVariable({
50
+ get: () => new node_opcua_variant_1.Variant({ dataType: node_opcua_variant_1.DataType.UInt64, value: this._fileSize })
51
+ }, true);
52
+ file.size.minimumSamplingInterval = 0; // changed immediatly
53
+ this.refresh();
54
+ }
55
+ set openCount(value) {
56
+ this._openCount = value;
57
+ this.file.openCount.touchValue();
58
+ }
59
+ get openCount() {
60
+ return this._openCount;
61
+ }
62
+ set fileSize(value) {
63
+ this._fileSize = value;
64
+ this.file.size.touchValue();
65
+ }
66
+ get fileSize() {
67
+ return this._fileSize;
68
+ }
69
+ /**
70
+ * refresh position and size
71
+ * this method should be call by the server if the file
72
+ * is modified externally
73
+ *
74
+ */
75
+ refresh() {
76
+ return __awaiter(this, void 0, void 0, function* () {
77
+ const abstractFs = this._fs;
78
+ // lauch an async request to update filesize
79
+ yield (function extractFileSize(self) {
80
+ return __awaiter(this, void 0, void 0, function* () {
81
+ try {
82
+ if (!abstractFs.existsSync(self.filename)) {
83
+ self._fileSize = 0;
84
+ return;
85
+ }
86
+ const stat = yield (0, util_1.promisify)(abstractFs.stat)(self.filename);
87
+ self._fileSize = stat.size;
88
+ debugLog("original file size ", self.filename, " size = ", self._fileSize);
89
+ }
90
+ catch (err) {
91
+ self._fileSize = 0;
92
+ if (err instanceof Error) {
93
+ warningLog("Cannot access file ", self.filename, err.message);
94
+ }
95
+ }
96
+ });
97
+ })(this);
98
+ });
99
+ }
100
+ }
101
+ exports.FileTypeData = FileTypeData;
102
+ function getFileData(opcuaFile2) {
103
+ return opcuaFile2.$fileData;
104
+ }
105
+ exports.getFileData = getFileData;
106
+ function _prepare(addressSpace, context) {
107
+ const _context = addressSpace;
108
+ _context.$$currentFileHandle = _context.$$currentFileHandle ? _context.$$currentFileHandle : 41;
109
+ _context.$$files = _context.$$files || {};
110
+ return _context;
111
+ }
112
+ function _getSessionId(context) {
113
+ var _a;
114
+ if (!context.session) {
115
+ return new node_opcua_nodeid_1.NodeId();
116
+ }
117
+ (0, node_opcua_assert_1.assert)(context.session && context.session.getSessionId);
118
+ return ((_a = context.session) === null || _a === void 0 ? void 0 : _a.getSessionId()) || new node_opcua_nodeid_1.NodeId();
119
+ }
120
+ function _addFile(addressSpace, context, openMode) {
121
+ const _context = _prepare(addressSpace, context);
122
+ _context.$$currentFileHandle++;
123
+ const fileHandle = _context.$$currentFileHandle;
124
+ const sessionId = _getSessionId(context);
125
+ const _fileData = {
126
+ fd: -1,
127
+ handle: fileHandle,
128
+ openMode,
129
+ position: [0, 0],
130
+ size: 0,
131
+ sessionId
132
+ };
133
+ _context.$$files[fileHandle] = _fileData;
134
+ return fileHandle;
135
+ }
136
+ function _getFileInfo(addressSpace, context, fileHandle) {
137
+ const _context = _prepare(addressSpace, context);
138
+ const _fileInfo = _context.$$files[fileHandle];
139
+ const sessionId = _getSessionId(context);
140
+ if (!_fileInfo || !(0, node_opcua_nodeid_1.sameNodeId)(_fileInfo.sessionId, sessionId)) {
141
+ errorLog("Invalid session ID this file descriptor doesn't belong to this session");
142
+ return null;
143
+ }
144
+ return _fileInfo;
145
+ }
146
+ function _close(addressSpace, context, fileData) {
147
+ const _context = _prepare(addressSpace, context);
148
+ delete _context.$$files[fileData.fd];
149
+ }
150
+ function toNodeJSMode(opcuaMode) {
151
+ let flags;
152
+ switch (opcuaMode) {
153
+ case open_mode_1.OpenFileMode.Read:
154
+ flags = "r";
155
+ break;
156
+ case open_mode_1.OpenFileMode.ReadWrite:
157
+ case open_mode_1.OpenFileMode.Write:
158
+ flags = "w+";
159
+ break;
160
+ case open_mode_1.OpenFileMode.ReadWriteAppend:
161
+ case open_mode_1.OpenFileMode.WriteAppend:
162
+ flags = "a+";
163
+ break;
164
+ case open_mode_1.OpenFileMode.WriteEraseExisting:
165
+ case open_mode_1.OpenFileMode.ReadWriteEraseExisting:
166
+ flags = "w+";
167
+ break;
168
+ default:
169
+ flags = "?";
170
+ break;
171
+ }
172
+ return flags;
173
+ }
174
+ /**
175
+ * Open is used to open a file represented by an Object of FileType.
176
+ * When a client opens a file it gets a file handle that is valid while the
177
+ * session is open. Clients shall use the Close Method to release the handle
178
+ * when they do not need access to the file anymore. Clients can open the
179
+ * same file several times for read.
180
+ * A request to open for writing shall return Bad_NotWritable when the file is
181
+ * already opened.
182
+ * A request to open for reading shall return Bad_NotReadable
183
+ * when the file is already opened for writing.
184
+ *
185
+ * Method Result Codes (defined in Call Service)
186
+ * Result Code Description
187
+ * BadNotReadable File might be locked and thus not readable.
188
+ * BadNotWritable The file is locked and thus not writable.
189
+ * BadInvalidState
190
+ * BadInvalidArgument Mode setting is invalid.
191
+ * BadNotFound .
192
+ * BadUnexpectedError
193
+ *
194
+ * @private
195
+ */
196
+ function _openFile(inputArguments, context) {
197
+ return __awaiter(this, void 0, void 0, function* () {
198
+ const addressSpace = this.addressSpace;
199
+ const mode = inputArguments[0].value;
200
+ /**
201
+ * mode (Byte) Indicates whether the file should be opened only for read operations
202
+ * or for read and write operations and where the initial position is set.
203
+ * The mode is an 8-bit unsigned integer used as bit mask with the structure
204
+ * defined in the following table:
205
+ * Field Bit Description
206
+ * Read 0 The file is opened for reading. If this bit is not
207
+ * set the Read Method cannot be executed.
208
+ * Write 1 The file is opened for writing. If this bit is not
209
+ * set the Write Method cannot be executed.
210
+ * EraseExisting 2 This bit can only be set if the file is opened for writing
211
+ * (Write bit is set). The existing content of the file is
212
+ * erased and an empty file is provided.
213
+ * Append 3 When the Append bit is set the file is opened at end
214
+ * of the file, otherwise at begin of the file.
215
+ * The SetPosition Method can be used to change the position.
216
+ * Reserved 4:7 Reserved for future use. Shall always be zero.
217
+ */
218
+ // see https://nodejs.org/api/fs.html#fs_file_system_flags
219
+ const flags = toNodeJSMode(mode);
220
+ if (flags === "?") {
221
+ errorLog("Invalid mode " + open_mode_1.OpenFileMode[mode] + " (" + mode + ")");
222
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
223
+ }
224
+ /**
225
+ * fileHandle (UInt32) A handle for the file used in other method calls indicating not the
226
+ * file (this is done by the Object of the Method call) but the access
227
+ * request and thus the position in the file. The fileHandle is generated
228
+ * by the server and is unique for the Session. Clients cannot transfer the
229
+ * fileHandle to another Session but need to get a new fileHandle by calling
230
+ * the Open Method.
231
+ */
232
+ const fileHandle = _addFile(addressSpace, context, mode);
233
+ const _fileInfo = _getFileInfo(addressSpace, context, fileHandle);
234
+ if (!_fileInfo) {
235
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
236
+ }
237
+ const fileData = context.object.$fileData;
238
+ const filename = fileData.filename;
239
+ const abstractFs = _getFileSystem(context);
240
+ try {
241
+ _fileInfo.fd = yield (0, util_1.promisify)(abstractFs.open)(filename, flags);
242
+ // update position
243
+ _fileInfo.position = [0, 0];
244
+ const fileLength = (yield (0, util_1.promisify)(abstractFs.stat)(filename)).size;
245
+ _fileInfo.size = fileLength;
246
+ // tslint:disable-next-line:no-bitwise
247
+ if ((mode & open_mode_1.OpenFileModeMask.AppendBit) === open_mode_1.OpenFileModeMask.AppendBit) {
248
+ _fileInfo.position[1] = fileLength;
249
+ }
250
+ if ((mode & open_mode_1.OpenFileModeMask.EraseExistingBit) === open_mode_1.OpenFileModeMask.EraseExistingBit) {
251
+ _fileInfo.size = 0;
252
+ }
253
+ fileData.openCount += 1;
254
+ }
255
+ catch (err) {
256
+ if (err instanceof Error) {
257
+ errorLog(err.message);
258
+ errorLog(err.stack);
259
+ }
260
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadUnexpectedError };
261
+ }
262
+ debugLog("Opening file handle ", fileHandle, "filename: ", fileData.filename, "openCount: ", fileData.openCount);
263
+ const callMethodResult = {
264
+ outputArguments: [
265
+ {
266
+ dataType: node_opcua_variant_1.DataType.UInt32,
267
+ value: fileHandle
268
+ }
269
+ ],
270
+ statusCode: node_opcua_status_code_1.StatusCodes.Good
271
+ };
272
+ return callMethodResult;
273
+ });
274
+ }
275
+ function _getFileSystem(context) {
276
+ const fs = context.object.$fs;
277
+ return fs;
278
+ }
279
+ /**
280
+ * Close is used to close a file represented by a FileType.
281
+ * When a client closes a file the handle becomes invalid.
282
+ *
283
+ * @param inputArguments
284
+ * @param context
285
+ * @private
286
+ */
287
+ function _closeFile(inputArguments, context) {
288
+ return __awaiter(this, void 0, void 0, function* () {
289
+ const abstractFs = _getFileSystem(context);
290
+ const addressSpace = this.addressSpace;
291
+ const fileHandle = inputArguments[0].value;
292
+ const _fileInfo = _getFileInfo(addressSpace, context, fileHandle);
293
+ if (!_fileInfo) {
294
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
295
+ }
296
+ const data = context.object.$fileData;
297
+ debugLog("Closing file handle ", fileHandle, "filename: ", data.filename, "openCount: ", data.openCount);
298
+ yield (0, util_1.promisify)(abstractFs.close)(_fileInfo.fd);
299
+ _close(addressSpace, context, _fileInfo);
300
+ data.openCount -= 1;
301
+ return {
302
+ statusCode: node_opcua_status_code_1.StatusCodes.Good
303
+ };
304
+ });
305
+ }
306
+ /**
307
+ * Read is used to read a part of the file starting from the current file position.
308
+ * The file position is advanced by the number of bytes read.
309
+ *
310
+ * @param inputArguments
311
+ * @param context
312
+ * @private
313
+ */
314
+ function _readFile(inputArguments, context) {
315
+ return __awaiter(this, void 0, void 0, function* () {
316
+ const addressSpace = this.addressSpace;
317
+ const abstractFs = _getFileSystem(context);
318
+ // fileHandle A handle indicating the access request and thus indirectly the
319
+ // position inside the file.
320
+ const fileHandle = inputArguments[0].value;
321
+ // Length Defines the length in bytes that should be returned in data, starting from the current
322
+ // position of the file handle. If the end of file is reached all data until the end of the file is
323
+ // returned. The Server is allowed to return less data than specified length.
324
+ let length = inputArguments[1].value;
325
+ // Only positive values are allowed.
326
+ if (length < 0) {
327
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
328
+ }
329
+ const _fileInfo = _getFileInfo(addressSpace, context, fileHandle);
330
+ if (!_fileInfo) {
331
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidState };
332
+ }
333
+ // tslint:disable-next-line:no-bitwise
334
+ if ((_fileInfo.openMode & open_mode_1.OpenFileModeMask.ReadBit) === 0x0) {
335
+ // open mode did not specify Read Flag
336
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidState };
337
+ }
338
+ length = Math.min(_fileInfo.size - _fileInfo.position[1], length);
339
+ const data = Buffer.alloc(length);
340
+ let ret = { bytesRead: 0 };
341
+ try {
342
+ // note: we do not util.promise here as it has a wierd behavior...
343
+ ret = yield new Promise((resolve, reject) => abstractFs.read(_fileInfo.fd, data, 0, length, _fileInfo.position[1], (err, bytesRead, buff) => {
344
+ if (err) {
345
+ return reject(err);
346
+ }
347
+ return resolve({ bytesRead });
348
+ }));
349
+ _fileInfo.position[1] += ret.bytesRead;
350
+ }
351
+ catch (err) {
352
+ if (err instanceof Error) {
353
+ errorLog("Read error : ", err.message);
354
+ }
355
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadUnexpectedError };
356
+ }
357
+ // Data Contains the returned data of the file. If the ByteString is empty it indicates that the end
358
+ // of the file is reached.
359
+ return {
360
+ outputArguments: [{ dataType: node_opcua_variant_1.DataType.ByteString, value: data.slice(0, ret.bytesRead) }],
361
+ statusCode: node_opcua_status_code_1.StatusCodes.Good
362
+ };
363
+ });
364
+ }
365
+ function _writeFile(inputArguments, context) {
366
+ return __awaiter(this, void 0, void 0, function* () {
367
+ const addressSpace = this.addressSpace;
368
+ const abstractFs = _getFileSystem(context);
369
+ const fileHandle = inputArguments[0].value;
370
+ const _fileInfo = _getFileInfo(addressSpace, context, fileHandle);
371
+ if (!_fileInfo) {
372
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
373
+ }
374
+ // tslint:disable-next-line:no-bitwise
375
+ if ((_fileInfo.openMode & open_mode_1.OpenFileModeMask.WriteBit) === 0x00) {
376
+ // File has not been open with write mode
377
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidState };
378
+ }
379
+ const data = inputArguments[1].value;
380
+ let ret = { bytesWritten: 0 };
381
+ try {
382
+ // note: we do not util.promise here as it has a wierd behavior...
383
+ ret = yield new Promise((resolve, reject) => {
384
+ abstractFs.write(_fileInfo.fd, data, 0, data.length, _fileInfo.position[1], (err, bytesWritten) => {
385
+ if (err) {
386
+ errorLog("Err", err);
387
+ return reject(err);
388
+ }
389
+ return resolve({ bytesWritten });
390
+ });
391
+ });
392
+ (0, node_opcua_assert_1.assert)(typeof ret.bytesWritten === "number");
393
+ _fileInfo.position[1] += ret.bytesWritten;
394
+ _fileInfo.size = Math.max(_fileInfo.size, _fileInfo.position[1]);
395
+ const fileTypeData = context.object.$fileData;
396
+ debugLog(fileTypeData.fileSize);
397
+ fileTypeData.fileSize = Math.max(fileTypeData.fileSize, _fileInfo.position[1]);
398
+ debugLog(fileTypeData.fileSize);
399
+ }
400
+ catch (err) {
401
+ if (err instanceof Error) {
402
+ errorLog("Write error : ", err.message);
403
+ }
404
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadUnexpectedError };
405
+ }
406
+ return {
407
+ outputArguments: [],
408
+ statusCode: node_opcua_status_code_1.StatusCodes.Good
409
+ };
410
+ });
411
+ }
412
+ function _setPositionFile(inputArguments, context) {
413
+ return __awaiter(this, void 0, void 0, function* () {
414
+ const addressSpace = this.addressSpace;
415
+ const fileHandle = inputArguments[0].value;
416
+ const position = inputArguments[1].value;
417
+ const _fileInfo = _getFileInfo(addressSpace, context, fileHandle);
418
+ if (!_fileInfo) {
419
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
420
+ }
421
+ _fileInfo.position = position;
422
+ return { statusCode: node_opcua_status_code_1.StatusCodes.Good };
423
+ });
424
+ }
425
+ function _getPositionFile(inputArguments, context) {
426
+ return __awaiter(this, void 0, void 0, function* () {
427
+ const addressSpace = this.addressSpace;
428
+ const fileHandle = inputArguments[0].value;
429
+ const _fileInfo = _getFileInfo(addressSpace, context, fileHandle);
430
+ if (!_fileInfo) {
431
+ return { statusCode: node_opcua_status_code_1.StatusCodes.BadInvalidArgument };
432
+ }
433
+ return {
434
+ outputArguments: [
435
+ {
436
+ arrayType: node_opcua_variant_1.VariantArrayType.Scalar,
437
+ dataType: node_opcua_variant_1.DataType.UInt64,
438
+ value: _fileInfo.position
439
+ }
440
+ ],
441
+ statusCode: node_opcua_status_code_1.StatusCodes.Good
442
+ };
443
+ });
444
+ }
445
+ exports.defaultMaxSize = 100000000;
446
+ function install_method_handle_on_type(addressSpace) {
447
+ const fileType = addressSpace.findObjectType("FileType");
448
+ if (fileType.open.isBound()) {
449
+ return;
450
+ }
451
+ fileType.open.bindMethod(_openFile);
452
+ fileType.close.bindMethod(_closeFile);
453
+ fileType.read.bindMethod(_readFile);
454
+ fileType.write.bindMethod(_writeFile);
455
+ fileType.setPosition.bindMethod(_setPositionFile);
456
+ fileType.getPosition.bindMethod(_getPositionFile);
457
+ }
458
+ /**
459
+ * bind all methods of a UAFile OPCUA node
460
+ * @param file the OPCUA Node that has a typeDefinition of FileType
461
+ * @param options the options
462
+ */
463
+ function installFileType(file, options) {
464
+ if (file.$fileData) {
465
+ errorLog("File already installed ", file.nodeId.toString(), file.browseName.toString());
466
+ return;
467
+ }
468
+ file.$fs = options.fileSystem || fsOrig;
469
+ // make sure that FileType methods are also bound.
470
+ install_method_handle_on_type(file.addressSpace);
471
+ // to protect the server we setup a maximum limit in bytes on the file
472
+ // if the client try to access or set the position above this limit
473
+ // the server will return an error
474
+ options.maxSize = options.maxSize === undefined ? exports.defaultMaxSize : options.maxSize;
475
+ const $fileData = new FileTypeData(options, file);
476
+ file.$fileData = $fileData;
477
+ // ----- install mime type
478
+ if (options.mineType) {
479
+ if (file.mimeType) {
480
+ file.mimeType.bindVariable({
481
+ get: () => file.$fileOptions.mineType
482
+ });
483
+ }
484
+ }
485
+ file.open.bindMethod(_openFile);
486
+ file.close.bindMethod(_closeFile);
487
+ file.read.bindMethod(_readFile);
488
+ file.write.bindMethod(_writeFile);
489
+ file.setPosition.bindMethod(_setPositionFile);
490
+ file.getPosition.bindMethod(_getPositionFile);
491
+ }
492
+ exports.installFileType = installFileType;
493
493
  //# sourceMappingURL=file_type_helpers.js.map