-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathtcp_connection.js
More file actions
136 lines (104 loc) · 4.03 KB
/
tcp_connection.js
File metadata and controls
136 lines (104 loc) · 4.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import BufferWriter from "../buffer_writer.js";
import BufferReader from "../buffer_reader.js";
import Constants from "../constants.js";
import Connection from "./connection.js";
class TCPConnection extends Connection {
constructor(host, port) {
super();
this.host = host;
this.port = port;
this.readBuffer = [];
}
async connect() {
// note: net module is only available in NodeJS, you shouldn't use TCPConnection from a web browser
const { Socket } = await import("net");
// create new socket
this.socket = new Socket();
// handle received data
this.socket.on('data', (data) => {
this.onSocketDataReceived(data);
});
// handle errors
this.socket.on('error', (error) => {
console.error('Connection Error', error);
});
// handle socket close
this.socket.on('close', (error) => {
this.onDisconnected();
});
// connect to server
this.socket.connect(this.port, this.host, async () => {
await this.onConnected();
});
}
onSocketDataReceived(data) {
// append received bytes to read buffer
this.readBuffer = [
...this.readBuffer,
...data,
];
// process read buffer while there is enough bytes for a frame header
// 3 bytes frame header = (1 byte frame type) + (2 bytes frame length as unsigned 16-bit little endian)
const frameHeaderLength = 3;
while(this.readBuffer.length >= frameHeaderLength){
try {
// extract frame header
const frameHeader = new BufferReader(this.readBuffer.slice(0, frameHeaderLength));
// ensure frame type supported
const frameType = frameHeader.readByte();
if(frameType !== Constants.SerialFrameTypes.Incoming && frameType !== Constants.SerialFrameTypes.Outgoing){
// unexpected byte, lets skip it and try again
this.readBuffer = this.readBuffer.slice(1);
continue;
}
// ensure frame length valid
const frameLength = frameHeader.readUInt16LE();
if(!frameLength){
// unexpected byte, lets skip it and try again
this.readBuffer = this.readBuffer.slice(1);
continue;
}
// check if we have received enough bytes for this frame, otherwise wait until more bytes received
const requiredLength = frameHeaderLength + frameLength;
if(this.readBuffer.length < requiredLength){
break;
}
// get frame data, and remove it and its frame header from the read buffer
const frameData = this.readBuffer.slice(frameHeaderLength, requiredLength);
this.readBuffer = this.readBuffer.slice(requiredLength);
// handle received frame
this.onFrameReceived(frameData);
} catch(e) {
console.error("Failed to process frame", e);
break;
}
}
}
close() {
try {
this.socket.destroy();
} catch(e) {
// console.log("failed to release lock on serial port readable, ignoring...", e);
}
}
async write(bytes) {
this.socket.write(new Uint8Array(bytes));
}
async writeFrame(frameType, frameData) {
// create frame
const frame = new BufferWriter();
// add frame header
frame.writeByte(frameType);
frame.writeUInt16LE(frameData.length);
// add frame data
frame.writeBytes(frameData);
// write frame to device
await this.write(frame.toBytes());
}
async sendToRadioFrame(data) {
// write "app to radio" frame 0x3c "<"
this.emit("tx", data);
await this.writeFrame(0x3c, data);
}
}
export default TCPConnection;