node-opcua-file-transfer 2.71.0 → 2.72.2
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/dist/client/client_file.d.ts +68 -68
- package/dist/client/client_file.js +309 -309
- package/dist/index.d.ts +5 -5
- package/dist/index.js +21 -21
- package/dist/open_mode.d.ts +39 -39
- package/dist/open_mode.js +44 -44
- package/dist/server/file_type_helpers.d.ts +70 -70
- package/dist/server/file_type_helpers.js +492 -492
- package/package.json +15 -15
|
@@ -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
|