mirror of
https://github.com/jwetzell/acn-js.git
synced 2026-09-05 05:28:59 +00:00
Merge pull request #5 from jwetzell/feat/sdt
Add support for decoding SDT protocol blocks inside RLP
This commit is contained in:
@@ -4,5 +4,23 @@ not sure how far I will take this but messing around with E1.17 ACN protocol
|
|||||||
## areas
|
## areas
|
||||||
- rlp
|
- rlp
|
||||||
- sdt
|
- sdt
|
||||||
|
- vectors
|
||||||
|
- [x] JOIN
|
||||||
|
- [x] JOIN_REFUSE
|
||||||
|
- [x] JOIN_ACCEPT
|
||||||
|
- [x] LEAVING
|
||||||
|
- [x] NAK
|
||||||
|
- [x] REL_WRAP
|
||||||
|
- [x] UNREL_WRAP
|
||||||
|
- [x] GET_SESSIONS
|
||||||
|
- [ ] SESSIONS
|
||||||
|
- [x] ACK
|
||||||
|
- [ ] CHANNEL_PARAMS
|
||||||
|
- [ ] LEAVE
|
||||||
|
- [x] CONNECT
|
||||||
|
- [x] CONNECT_ACCEPT
|
||||||
|
- [x] CONNECT_REFUSE
|
||||||
|
- [x] DISCONNECT
|
||||||
|
- [x] DISCONNECTING
|
||||||
- dmp
|
- dmp
|
||||||
- ddl
|
- ddl
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
export enum Protocol {
|
||||||
|
SDT = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SDTVector {
|
||||||
|
REL_WRAP = 1,
|
||||||
|
UNREL_WRAP,
|
||||||
|
CHANNEL_PARAMS,
|
||||||
|
JOIN,
|
||||||
|
JOIN_REFUSE,
|
||||||
|
JOIN_ACCEPT,
|
||||||
|
LEAVE,
|
||||||
|
LEAVING,
|
||||||
|
CONNECT,
|
||||||
|
CONNECT_ACCEPT,
|
||||||
|
CONNECT_REFUSE,
|
||||||
|
DISCONNECT,
|
||||||
|
DISCONNECTING,
|
||||||
|
ACK,
|
||||||
|
NAK,
|
||||||
|
GET_SESSIONS,
|
||||||
|
SESSIONS,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TransportLayerAddressType {
|
||||||
|
SDT_ADDR_NULL = 0,
|
||||||
|
SDT_ADDR_IPV4,
|
||||||
|
SDT_ADDR_IPV6,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SDTReasonCode {
|
||||||
|
NONSPECIFIC = 1,
|
||||||
|
ILLEGAL_PARAMETERS,
|
||||||
|
LOW_RESOURCES,
|
||||||
|
ALREADY_MEMBER,
|
||||||
|
BAD_ADDRESS_TYPE,
|
||||||
|
NO_RECIPROCAL_CHANNEL,
|
||||||
|
CHANNEL_EXPIRED,
|
||||||
|
LOST_SEQUENCE,
|
||||||
|
SATURATED,
|
||||||
|
TRANSPORT_ADDRESS_CHANGING,
|
||||||
|
ASKED_TO_LEAVE,
|
||||||
|
NO_RECIPIENT,
|
||||||
|
ONLY_UNICAST_SUPPORTED,
|
||||||
|
}
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './models';
|
export * from './types';
|
||||||
export * from './udp';
|
export * from './udp';
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
export type Protocols = {
|
|
||||||
SDT: 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type UDPPreamble = {
|
|
||||||
preambleSize: number;
|
|
||||||
postambleSize: number;
|
|
||||||
packetIdentifier: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ACNPacket<PreambleType, PDUBlockType, PostambleType> = {
|
|
||||||
preamble: PreambleType;
|
|
||||||
pduBlock: PDUBlockType;
|
|
||||||
postamble: PostambleType;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RootLayerPDU = {
|
|
||||||
vector: number;
|
|
||||||
header: string;
|
|
||||||
data: Uint8Array;
|
|
||||||
};
|
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import rlp from './rlp';
|
import rlp from './rlp';
|
||||||
|
import sdt from './sdt';
|
||||||
export default {
|
export default {
|
||||||
rlp,
|
rlp,
|
||||||
|
sdt,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { RootLayerPDU } from '../models';
|
import pdu from '.';
|
||||||
|
import { Protocol } from '../enums';
|
||||||
|
import { RootLayerPDU, SessionDataTransportPDU } from '../types';
|
||||||
import { toHex } from '../utils';
|
import { toHex } from '../utils';
|
||||||
|
|
||||||
function decode(bytes: Uint8Array): RootLayerPDU {
|
function decode(bytes: Uint8Array): RootLayerPDU {
|
||||||
@@ -49,7 +51,22 @@ function decode(bytes: Uint8Array): RootLayerPDU {
|
|||||||
const dataOffset = headerOffset + 16;
|
const dataOffset = headerOffset + 16;
|
||||||
// NOTE(jwetzell): flags/lengthH + lengthL + lengthX + vector + header
|
// NOTE(jwetzell): flags/lengthH + lengthL + lengthX + vector + header
|
||||||
const dataLength = length - (1 + 1 + (lengthFlag ? 1 : 0) + 4 + 16);
|
const dataLength = length - (1 + 1 + (lengthFlag ? 1 : 0) + 4 + 16);
|
||||||
const data = bytes.subarray(dataOffset, dataOffset + dataLength);
|
let data: SessionDataTransportPDU[] | Uint8Array = bytes.subarray(dataOffset, dataOffset + dataLength);
|
||||||
|
|
||||||
|
if (vector === Protocol.SDT) {
|
||||||
|
const blocks: SessionDataTransportPDU[] = [];
|
||||||
|
|
||||||
|
let blockDataOffset = 0;
|
||||||
|
// TODO(jwetzell): this could blow up so might need some attention later?
|
||||||
|
while (blockDataOffset < dataLength) {
|
||||||
|
const blockData = pdu.sdt.decode(bytes.subarray(dataOffset + blockDataOffset));
|
||||||
|
blocks.push(blockData);
|
||||||
|
blockDataOffset += blockData.length;
|
||||||
|
}
|
||||||
|
data = blocks;
|
||||||
|
} else {
|
||||||
|
console.error(`unhandled protocol in RLP: ${vector}`);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
vector,
|
vector,
|
||||||
|
|||||||
@@ -0,0 +1,275 @@
|
|||||||
|
import { Protocol, SDTVector } from '../enums';
|
||||||
|
import {
|
||||||
|
SDTAckData,
|
||||||
|
SDTConnectAcceptData,
|
||||||
|
SDTConnectData,
|
||||||
|
SDTConnectRefuseData,
|
||||||
|
SDTDisconnectData,
|
||||||
|
SDTDisconnectingData,
|
||||||
|
SDTGetSessionsData,
|
||||||
|
SDTJoinAcceptData,
|
||||||
|
SDTJoinData,
|
||||||
|
SDTJoinRefuseData,
|
||||||
|
SDTLeavingData,
|
||||||
|
SDTNakData,
|
||||||
|
SDTWrapperData,
|
||||||
|
SessionDataTransportPDU,
|
||||||
|
TransportLayerAddress,
|
||||||
|
} from '../types';
|
||||||
|
import { toHex } from '../utils';
|
||||||
|
|
||||||
|
// TODO(jwetzell): work out flag inheritance, will need previous PDU
|
||||||
|
|
||||||
|
export function decode(bytes: Uint8Array): SessionDataTransportPDU {
|
||||||
|
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
||||||
|
const flags = view.getUint8(0) >> 4;
|
||||||
|
const lengthFlag = ((flags >> 3) & 0x1) === 1;
|
||||||
|
const vectorFlag = ((flags >> 2) & 0x1) === 1;
|
||||||
|
|
||||||
|
if (!vectorFlag) {
|
||||||
|
throw new Error('SDT PDU must have a vector');
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataFlag = (flags & 0x1) === 1;
|
||||||
|
|
||||||
|
if (!dataFlag) {
|
||||||
|
// TODO(jwetzell): idk if this is true
|
||||||
|
throw new Error('SDT PDU must have data');
|
||||||
|
}
|
||||||
|
|
||||||
|
const lengthH = view.getUint8(0) & 0x0f;
|
||||||
|
let lengthOffset = 1;
|
||||||
|
const lengthL = view.getUint8(lengthOffset);
|
||||||
|
|
||||||
|
let length = (lengthH << 8) + lengthL;
|
||||||
|
if (lengthFlag) {
|
||||||
|
lengthOffset += 1;
|
||||||
|
const lengthX = view.getUint8(lengthOffset);
|
||||||
|
length = (lengthH << 16) + (lengthL << 8) + lengthX;
|
||||||
|
}
|
||||||
|
let vectorOffset = lengthOffset + 1;
|
||||||
|
|
||||||
|
if (lengthFlag) {
|
||||||
|
vectorOffset += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const vector = view.getUint8(vectorOffset);
|
||||||
|
|
||||||
|
const dataOffset = vectorOffset + 1;
|
||||||
|
// NOTE(jwetzell): flags/lengthH + lengthL + lengthX + vector
|
||||||
|
const dataLength = length - (1 + 1 + (lengthFlag ? 1 : 0) + 1);
|
||||||
|
const data = decodeData(vector, bytes.subarray(dataOffset, dataOffset + dataLength));
|
||||||
|
|
||||||
|
return {
|
||||||
|
vector,
|
||||||
|
length,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeClientBlock(bytes: Uint8Array) {
|
||||||
|
if (bytes.byteLength === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
||||||
|
const flags = view.getUint8(0) >> 4;
|
||||||
|
const lengthFlag = ((flags >> 3) & 0x1) === 1;
|
||||||
|
const vectorFlag = ((flags >> 2) & 0x1) === 1;
|
||||||
|
|
||||||
|
if (!vectorFlag) {
|
||||||
|
throw new Error('SDT PDU must have a vector');
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataFlag = (flags & 0x1) === 1;
|
||||||
|
|
||||||
|
if (!dataFlag) {
|
||||||
|
// TODO(jwetzell): idk if this is true
|
||||||
|
throw new Error('SDT PDU must have data');
|
||||||
|
}
|
||||||
|
|
||||||
|
const lengthH = view.getUint8(0) & 0x0f;
|
||||||
|
let lengthOffset = 1;
|
||||||
|
const lengthL = view.getUint8(lengthOffset);
|
||||||
|
|
||||||
|
let length = (lengthH << 8) + lengthL;
|
||||||
|
if (lengthFlag) {
|
||||||
|
lengthOffset += 1;
|
||||||
|
const lengthX = view.getUint8(lengthOffset);
|
||||||
|
length = (lengthH << 16) + (lengthL << 8) + lengthX;
|
||||||
|
}
|
||||||
|
let vectorOffset = lengthOffset + 1;
|
||||||
|
|
||||||
|
if (lengthFlag) {
|
||||||
|
vectorOffset += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const memberID = view.getUint16(vectorOffset);
|
||||||
|
|
||||||
|
const headerOffset = vectorOffset + 2;
|
||||||
|
const clientProtocol = view.getUint32(headerOffset);
|
||||||
|
const association = view.getUint16(headerOffset + 4);
|
||||||
|
|
||||||
|
const dataOffset = headerOffset + 6;
|
||||||
|
// NOTE(jwetzell): flags/lengthH + lengthL + lengthX + vector + header
|
||||||
|
const dataLength = length - (1 + 1 + (lengthFlag ? 1 : 0) + 2 + 6);
|
||||||
|
let data: SessionDataTransportPDU | Uint8Array = bytes.subarray(dataOffset, dataOffset + dataLength);
|
||||||
|
|
||||||
|
if (clientProtocol === Protocol.SDT) {
|
||||||
|
data = decode(data);
|
||||||
|
} else {
|
||||||
|
console.error(`SDT client block contains unknown protocol: ${clientProtocol}`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
memberID,
|
||||||
|
clientProtocol,
|
||||||
|
association,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeData(
|
||||||
|
vector: SDTVector,
|
||||||
|
bytes: Uint8Array
|
||||||
|
):
|
||||||
|
| SDTJoinData
|
||||||
|
| SDTJoinAcceptData
|
||||||
|
| SDTJoinRefuseData
|
||||||
|
| SDTWrapperData
|
||||||
|
| SDTAckData
|
||||||
|
| SDTLeavingData
|
||||||
|
| SDTGetSessionsData
|
||||||
|
| SDTNakData
|
||||||
|
| SDTConnectData
|
||||||
|
| SDTConnectAcceptData
|
||||||
|
| SDTConnectRefuseData
|
||||||
|
| SDTDisconnectData
|
||||||
|
| SDTDisconnectingData
|
||||||
|
| Uint8Array {
|
||||||
|
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
||||||
|
switch (vector) {
|
||||||
|
case SDTVector.JOIN: {
|
||||||
|
const destinationAddress: TransportLayerAddress = {
|
||||||
|
type: view.getUint8(30),
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO(jwetzell): handle IPv6, type === 2
|
||||||
|
if (destinationAddress.type === 1) {
|
||||||
|
// IPv4
|
||||||
|
destinationAddress.port = view.getUint16(31);
|
||||||
|
destinationAddress.address = bytes.subarray(33, 37).join('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const adhocExpiry = view.getUint8(45);
|
||||||
|
const joinData = {
|
||||||
|
componentID: toHex(bytes.subarray(0, 16)),
|
||||||
|
memberID: view.getUint16(16),
|
||||||
|
channelNumber: view.getUint16(18),
|
||||||
|
reciprocalChannel: view.getUint16(20),
|
||||||
|
totalSequenceNumber: view.getUint32(22),
|
||||||
|
reliableSequenceNumber: view.getUint32(26),
|
||||||
|
destinationAddress,
|
||||||
|
channelParameters: {
|
||||||
|
expiry: view.getUint8(37),
|
||||||
|
nakOutboundFlag: view.getUint8(38),
|
||||||
|
nakHoldoff: view.getUint16(39),
|
||||||
|
nakModulus: view.getUint16(41),
|
||||||
|
nakMaxWait: view.getUint16(43),
|
||||||
|
},
|
||||||
|
adhocExpiry,
|
||||||
|
};
|
||||||
|
return joinData;
|
||||||
|
}
|
||||||
|
case SDTVector.JOIN_ACCEPT: {
|
||||||
|
const joinData = {
|
||||||
|
leaderComponentID: toHex(bytes.subarray(0, 16)),
|
||||||
|
channelNumber: view.getUint16(16),
|
||||||
|
memberID: view.getUint16(18),
|
||||||
|
reliableSequenceNumber: view.getUint32(20),
|
||||||
|
reciprocalChannel: view.getUint16(24),
|
||||||
|
};
|
||||||
|
return joinData;
|
||||||
|
}
|
||||||
|
case SDTVector.JOIN_REFUSE: {
|
||||||
|
const joinData = {
|
||||||
|
leaderComponentID: toHex(bytes.subarray(0, 16)),
|
||||||
|
channelNumber: view.getUint16(16),
|
||||||
|
memberID: view.getUint16(18),
|
||||||
|
reliableSequenceNumber: view.getUint32(20),
|
||||||
|
refuseCode: view.getUint8(24),
|
||||||
|
};
|
||||||
|
|
||||||
|
return joinData;
|
||||||
|
}
|
||||||
|
case SDTVector.REL_WRAP:
|
||||||
|
case SDTVector.UNREL_WRAP: {
|
||||||
|
const wrapperData: SDTWrapperData = {
|
||||||
|
channelNumber: view.getUint16(0),
|
||||||
|
totalSequenceNumber: view.getUint32(2),
|
||||||
|
reliableSequenceNumber: view.getUint32(6),
|
||||||
|
oldestAvailableWrapper: view.getUint32(10),
|
||||||
|
firstMemberToAck: view.getUint16(14),
|
||||||
|
lastMemberToAck: view.getUint16(16),
|
||||||
|
makThreshold: view.getUint16(18),
|
||||||
|
sdtClientBlock: decodeClientBlock(bytes.subarray(20)),
|
||||||
|
};
|
||||||
|
|
||||||
|
return wrapperData;
|
||||||
|
}
|
||||||
|
case SDTVector.ACK: {
|
||||||
|
return {
|
||||||
|
reliableSequenceNumber: view.getUint32(0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case SDTVector.LEAVING: {
|
||||||
|
return {
|
||||||
|
leaderComponentID: toHex(bytes.subarray(0, 16)),
|
||||||
|
channelNumber: view.getUint16(16),
|
||||||
|
memberID: view.getUint16(18),
|
||||||
|
reliableSequenceNumber: view.getUint32(20),
|
||||||
|
reasonCode: view.getUint8(24),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case SDTVector.GET_SESSIONS: {
|
||||||
|
return {
|
||||||
|
componentID: toHex(bytes.subarray(0, 16)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case SDTVector.NAK: {
|
||||||
|
return {
|
||||||
|
leaderComponentID: toHex(bytes.subarray(0, 16)),
|
||||||
|
channelNumber: view.getUint16(16),
|
||||||
|
memberID: view.getUint16(18),
|
||||||
|
reliableSequenceNumber: view.getUint32(20),
|
||||||
|
firstMissedSequence: view.getUint32(24),
|
||||||
|
lastMissedSequence: view.getUint32(28),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case SDTVector.DISCONNECT:
|
||||||
|
case SDTVector.CONNECT:
|
||||||
|
case SDTVector.CONNECT_ACCEPT: {
|
||||||
|
return {
|
||||||
|
protocolID: view.getUint32(0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case SDTVector.CONNECT_REFUSE: {
|
||||||
|
return {
|
||||||
|
protocolID: view.getUint32(0),
|
||||||
|
refuseCode: view.getUint8(4),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case SDTVector.DISCONNECTING: {
|
||||||
|
return {
|
||||||
|
protocolID: view.getUint32(0),
|
||||||
|
reasonCode: view.getUint8(4),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
console.error(`unhandled SDT vector: ${vector}`);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
decode,
|
||||||
|
};
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import { TransportLayerAddressType, SDTVector, SDTReasonCode } from './enums';
|
||||||
|
|
||||||
|
export type UDPPreamble = {
|
||||||
|
preambleSize: number;
|
||||||
|
postambleSize: number;
|
||||||
|
packetIdentifier: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ACNPacket<PreambleType, PDUBlockType, PostambleType> = {
|
||||||
|
preamble: PreambleType;
|
||||||
|
pduBlock: PDUBlockType;
|
||||||
|
postamble: PostambleType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RootLayerPDU = {
|
||||||
|
vector: number;
|
||||||
|
header: string;
|
||||||
|
data: SessionDataTransportPDU[] | Uint8Array;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SDTChannelParams = {
|
||||||
|
expiry: number;
|
||||||
|
nakOutboundFlag: number;
|
||||||
|
nakHoldoff: number;
|
||||||
|
nakModulus: number;
|
||||||
|
nakMaxWait: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TransportLayerAddress = {
|
||||||
|
type: TransportLayerAddressType;
|
||||||
|
port?: number;
|
||||||
|
address?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SDTJoinData = {
|
||||||
|
componentID: string;
|
||||||
|
memberID: number;
|
||||||
|
channelNumber: number;
|
||||||
|
reciprocalChannel: number;
|
||||||
|
totalSequenceNumber: number;
|
||||||
|
reliableSequenceNumber: number;
|
||||||
|
destinationAddress: TransportLayerAddress;
|
||||||
|
channelParameters: SDTChannelParams;
|
||||||
|
adhocExpiry: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SDTJoinAcceptData = {
|
||||||
|
leaderComponentID: string;
|
||||||
|
channelNumber: number;
|
||||||
|
memberID: number;
|
||||||
|
reliableSequenceNumber: number;
|
||||||
|
reciprocalChannel: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SDTJoinRefuseData = {
|
||||||
|
leaderComponentID: string;
|
||||||
|
channelNumber: number;
|
||||||
|
memberID: number;
|
||||||
|
reliableSequenceNumber: number;
|
||||||
|
refuseCode: SDTReasonCode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SDTLeavingData = {
|
||||||
|
leaderComponentID: string;
|
||||||
|
channelNumber: number;
|
||||||
|
memberID: number;
|
||||||
|
reliableSequenceNumber: number;
|
||||||
|
reasonCode: SDTReasonCode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SDTWrapperData = {
|
||||||
|
channelNumber: number;
|
||||||
|
totalSequenceNumber: number;
|
||||||
|
reliableSequenceNumber: number;
|
||||||
|
oldestAvailableWrapper: number;
|
||||||
|
firstMemberToAck: number;
|
||||||
|
lastMemberToAck: number;
|
||||||
|
makThreshold: number;
|
||||||
|
sdtClientBlock?: SDTClientBlock;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SDTAckData = {
|
||||||
|
reliableSequenceNumber: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SDTNakData = {
|
||||||
|
leaderComponenetID: string;
|
||||||
|
channelNumber: number;
|
||||||
|
memberID: number;
|
||||||
|
reliableSequenceNumber: number;
|
||||||
|
firstMissedSequence: number;
|
||||||
|
lastMissedSequence: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SDTConnectData = {
|
||||||
|
protocolID: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SDTConnectAcceptData = SDTConnectData;
|
||||||
|
export type SDTConnectRefuseData = {
|
||||||
|
protocolID: number;
|
||||||
|
refuseCode: SDTReasonCode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SDTDisconnectData = SDTConnectData;
|
||||||
|
export type SDTDisconnectingData = {
|
||||||
|
protocolID: number;
|
||||||
|
reasonCode: SDTReasonCode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SDTGetSessionsData = {
|
||||||
|
componentID: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SessionDataTransportPDU = {
|
||||||
|
vector: SDTVector;
|
||||||
|
length: number;
|
||||||
|
// TODO(jwetzell): cleanup these types
|
||||||
|
data:
|
||||||
|
| SDTJoinData
|
||||||
|
| SDTJoinAcceptData
|
||||||
|
| SDTJoinRefuseData
|
||||||
|
| SDTWrapperData
|
||||||
|
| SDTAckData
|
||||||
|
| SDTLeavingData
|
||||||
|
| SDTGetSessionsData
|
||||||
|
| SDTNakData
|
||||||
|
| SDTConnectData
|
||||||
|
| SDTConnectAcceptData
|
||||||
|
| SDTConnectRefuseData
|
||||||
|
| SDTDisconnectData
|
||||||
|
| SDTDisconnectingData
|
||||||
|
| Uint8Array;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SDTClientBlock = {
|
||||||
|
memberID: number;
|
||||||
|
clientProtocol: number;
|
||||||
|
association: number;
|
||||||
|
data: SessionDataTransportPDU | Uint8Array;
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ACNPacket, RootLayerPDU, UDPPreamble } from './models';
|
import { ACNPacket, RootLayerPDU, UDPPreamble } from './types';
|
||||||
import pdu from './pdu';
|
import pdu from './pdu';
|
||||||
|
|
||||||
// ANSI E1.17 - 2015 (R2020) EPI 17
|
// ANSI E1.17 - 2015 (R2020) EPI 17
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export function toHex(bytes: Uint8Array): string{
|
export function toHex(bytes: Uint8Array): string {
|
||||||
let hex = '';
|
let hex = '';
|
||||||
bytes.forEach((byte)=>{
|
bytes.forEach((byte) => {
|
||||||
hex += byte.toString(16).padStart(2,'0')
|
hex += byte.toString(16).padStart(2, '0');
|
||||||
})
|
});
|
||||||
return hex;
|
return hex;
|
||||||
}
|
}
|
||||||
@@ -20,10 +20,33 @@ const goodTests = [
|
|||||||
pduBlock: {
|
pduBlock: {
|
||||||
vector: 1,
|
vector: 1,
|
||||||
header: 'b0abd82a20d24240a7cd3e49d999a4e5',
|
header: 'b0abd82a20d24240a7cd3e49d999a4e5',
|
||||||
data: new Uint8Array([
|
data: [
|
||||||
112, 50, 4, 73, 136, 131, 52, 255, 255, 74, 69, 176, 66, 102, 183, 33, 235, 216, 155, 0, 1, 244, 136, 0, 0, 0,
|
{
|
||||||
0, 244, 136, 0, 0, 244, 136, 1, 67, 216, 239, 192, 220, 207, 5, 0, 0, 2, 0, 10, 0, 20, 1, 44,
|
vector: 4,
|
||||||
]),
|
length: 50,
|
||||||
|
data: {
|
||||||
|
componentID: '49888334ffff4a45b04266b721ebd89b',
|
||||||
|
memberID: 1,
|
||||||
|
channelNumber: 62600,
|
||||||
|
reciprocalChannel: 0,
|
||||||
|
totalSequenceNumber: 62600,
|
||||||
|
reliableSequenceNumber: 62600,
|
||||||
|
destinationAddress: {
|
||||||
|
type: 1,
|
||||||
|
port: 17368,
|
||||||
|
address: '239.192.220.207'
|
||||||
|
},
|
||||||
|
channelParameters: {
|
||||||
|
expiry: 5,
|
||||||
|
nakOutboundFlag: 0,
|
||||||
|
nakHoldoff: 2,
|
||||||
|
nakModulus: 10,
|
||||||
|
nakMaxWait: 20
|
||||||
|
},
|
||||||
|
adhocExpiry: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
},
|
},
|
||||||
postamble: undefined,
|
postamble: undefined,
|
||||||
},
|
},
|
||||||
@@ -71,7 +94,9 @@ const badTests = [
|
|||||||
describe('ACN Message Decoding Pass', () => {
|
describe('ACN Message Decoding Pass', () => {
|
||||||
goodTests.forEach((messageTest) => {
|
goodTests.forEach((messageTest) => {
|
||||||
it(messageTest.description, () => {
|
it(messageTest.description, () => {
|
||||||
|
console.log(JSON.stringify(messageTest.expected))
|
||||||
const decoded = acn.decode(messageTest.bytes);
|
const decoded = acn.decode(messageTest.bytes);
|
||||||
|
console.log(JSON.stringify(decoded))
|
||||||
deepEqual(decoded, messageTest.expected);
|
deepEqual(decoded, messageTest.expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user