node-opcua-transport
Version:
pure nodejs OPCUA SDK - module transport
122 lines (99 loc) • 3.68 kB
text/typescript
/**
* @module node-opcua-transport
*/
import url from "url";
import { assert } from "node-opcua-assert";
import { BinaryStream, OutputBinaryStream } from "node-opcua-binary-stream";
import { createFastUninitializedBuffer } from "node-opcua-buffer-utils";
import { readMessageHeader } from "node-opcua-chunkmanager";
import { BaseUAObject } from "node-opcua-factory";
import { TCPErrorMessage } from "./TCPErrorMessage";
function is_valid_msg_type(msgType: string): boolean {
assert(
[
"HEL",
"ACK",
"ERR", // Connection Layer
"OPN",
"MSG",
"CLO" // OPC Unified Architecture, Part 6 page 36
].indexOf(msgType) >= 0,
"invalid message type " + msgType
);
return true;
}
export type ConstructorFunc = new (...args: any[]) => BaseUAObject;
export function decodeMessage(stream: BinaryStream, classNameConstructor: ConstructorFunc): BaseUAObject {
assert(stream instanceof BinaryStream);
assert(classNameConstructor instanceof Function, " expecting a function for " + classNameConstructor);
const header = readMessageHeader(stream);
assert(stream.length === 8);
let obj;
if (header.msgType === "ERR") {
obj = new TCPErrorMessage();
obj.decode(stream);
return obj;
} else {
obj = new classNameConstructor();
obj.decode(stream);
return obj;
}
}
export function packTcpMessage(msgType: string, encodableObject: BaseUAObject): Buffer {
assert(is_valid_msg_type(msgType));
const messageChunk = createFastUninitializedBuffer(encodableObject.binaryStoreSize() + 8);
// encode encode-ableObject in a packet
const stream = new BinaryStream(messageChunk);
encodeMessage(msgType, encodableObject, stream);
return messageChunk;
}
// opc.tcp://hostname:51210/UA/SampleServer
export function parseEndpointUrl(endpointUrl: string): url.Url {
const _url = url.parse(endpointUrl);
if (!_url.protocol || !_url.hostname) {
throw new Error("Invalid endpoint url " + endpointUrl);
}
return _url;
/*
const r = /^([a-z.]*):\/\/([a-zA-Z_\-.\-0-9]*):([0-9]*)(\/.*){0,1}/;
const matches = r.exec(endpointUrl);
if (!matches) {
throw new Error("Invalid endpoint url " + endpointUrl);
}
return {
protocol: matches[1],
hostname: matches[2],
port: parseInt(matches[3], 10),
address: matches[4] || ""
};
*/
}
export function is_valid_endpointUrl(endpointUrl: string): boolean {
const e = parseEndpointUrl(endpointUrl);
return Object.prototype.hasOwnProperty.call(e, "hostname");
}
export function writeTCPMessageHeader(
msgType: string,
chunkType: string,
totalLength: number,
stream: OutputBinaryStream
): void {
if (stream instanceof Buffer) {
stream = new BinaryStream(stream);
}
assert(is_valid_msg_type(msgType));
assert(["A", "F", "C"].indexOf(chunkType) !== -1);
stream.writeUInt8(msgType.charCodeAt(0));
stream.writeUInt8(msgType.charCodeAt(1));
stream.writeUInt8(msgType.charCodeAt(2));
// Chunk type
stream.writeUInt8(chunkType.charCodeAt(0)); // reserved
stream.writeUInt32(totalLength);
}
function encodeMessage(msgType: string, messageContent: BaseUAObject, stream: OutputBinaryStream) {
// the length of the message, in bytes. (includes the 8 bytes of the message header)
const totalLength = messageContent.binaryStoreSize() + 8;
writeTCPMessageHeader(msgType, "F", totalLength, stream);
messageContent.encode(stream);
assert(totalLength === stream.length, "invalid message size");
}