forked from josephdadams/udp-to-serial
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
334 lines (291 loc) · 10.7 KB
/
index.js
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
const { SerialPort } = require('serialport')
const { DelimiterParser } = require('@serialport/parser-delimiter')
//udp-to-serial
const udp = require('dgram')
const fs = require('fs')
const configFile = 'config.json'
let config = {
port_udp: 52381,
port_udp_count: 2,
port_serial: '/dev/ttyUSB0',
baudRate: 9600,
customViscaId: undefined,
}
let serialPort = null //variable for serial port reference
let sockets = [] //variable to store UDP socket for responding
//save last used address and port for response
let last_socket = null
let last_address = null
let last_port = null
function startup() {
// runs the UDP and Serial connections
loadFile()
console.log('Loading config', config)
//Open Serial Port
console.log('Opening Serial Port: ' + config.port_serial)
serialPort = new SerialPort(
{
path: config.port_serial,
baudRate: config.baudRate,
},
function (err) {
if (err) {
console.log('Error opening serial port: ', err.message)
}
}
)
// Create Hex Parser and link to serialPort
const parser = serialPort.pipe(new DelimiterParser({ delimiter: hexToBytes('ff'), includeDelimiter: true }))
parser.on('data', viscaRS232ResponseParser)
//console.log('Parser Created')
//Open UDP Ports
for (let i = 0; i < config.port_udp_count; i++) {
sockets[i] = createMySocket(config.port_udp + i)
}
}
function loadFile() {
//loads settings on first load of app
try {
let rawdata = fs.readFileSync(configFile)
let myJson = JSON.parse(rawdata)
if (myJson.port_udp) {
config.port_udp = myJson.port_udp
}
if (myJson.port_serial) {
config.port_serial = myJson.port_serial
}
if (myJson.baudRate) {
config.baudRate = myJson.baudRate
}
if (myJson.port_udp_count) {
config.port_udp_count = myJson.port_udp_count
}
} catch (error) {
console.log('Error parsing config file:')
console.log(error)
}
}
function createMySocket(port) {
// Helper function creates udp4 socket and binds events
let socket = udp.createSocket('udp4')
socket.on('error', function (error) {
console.log('Error: ' + error)
socket.close()
})
socket.on('message', function (msg, rinfo) {
//console.log('\nUDP Received %d bytes from %s:%d',msg.length, rinfo.address, rinfo.port, msg);
//save address and port for reuse with replies
last_socket = socket
last_address = rinfo.address
last_port = rinfo.port
translate_visca_ip_to_cv620_visca_rs232(msg)
})
socket.on('listening', function () {
let address = socket.address()
console.log('Socket listening at: ' + address.address + ':' + address.port)
})
socket.on('close', function () {
console.log('Socket is closed!')
})
socket.on('connect', function () {
console.log('Socket is connected')
})
socket.bind(port, () => {
const size = socket.ref()
//console.log(size.eventNames());
})
return socket
}
function udp_respond(msg_text) {
// Reference function to wrap udp responses to last known port and address considering Visca ID change and HEX conversion
msg_text = translateViscaIdForIP(msg_text)
const msg_hex = hexToBytes(msg_text)
last_socket.send(msg_hex, last_port, last_address)
}
function viscaRS232ResponseParser(msg) {
const msg_string = bytesToHexStrSpace(msg)
console.log('Visca-RS232-received:: ', msg_string)
// Define some Responses that should trigger special actions
const rs232_syntax_error_text = '90 60 02 ff'
const rs232_information_cam_powerInq_regex = /90 50 0[23] ff/
if (compare_array(rs232_syntax_error_text, msg)) {
console.log('\tVisca-Response: Syntax error')
} else if (msg_string.match(rs232_information_cam_powerInq_regex) != null) {
const relevant = msg_string.substring(6, 9)
if (relevant == '02') {
console.log('\tVisca-Response: CAM_PowerInq = On', msg[10])
} else if (relevant == '03') {
console.log('\tVisca-Response: CAM_PowerInq = Off(Standby)', msg[10])
}
translate_visca_rs232_to_visca_ip(msg)
} else {
translate_visca_rs232_to_visca_ip(msg)
}
}
function addSpaceAndZerosToHexStr(str) {
// Function to make sure hex String includes 0 and is formated in pairs split by space
str = str.toString()
str =
str.length == 0
? '00 '
: str.length == 1
? '0' + str + ' '
: str.length == 2
? str + ' '
: str.substring(str.length - 2, str.length)
return str
}
// Convert a hex string to a Uint8Array
function hexToBytes(hex) {
let bytes
//console.log('Trying Hex string -> Byte on',hex)
for (bytes = [], c = 0; c < hex.length; c += 3) bytes.push(parseInt(hex.substr(c, 2), 16))
let result = new Uint8Array(bytes)
//console.log('result: ',result)
return result
}
// Convert Bytes to Hex String
function bytesToHexStrSpace(bytes) {
let result = ''
//console.log('Trying Byte -> Hex string on',bytes)
bytes = new Uint8Array(bytes)
for (i in bytes) {
//console.log(bytes[i])
let str = bytes[i].toString(16)
str = addSpaceAndZerosToHexStr(str)
result += str
}
result = result.substring(0, result.length - 1)
//console.log('result: ',result)
return result
}
function compare_array(array1, array2) {
let result = array1 == bytesToHexStrSpace(array2)
//console.log('Comparing text\n',array1,' to \n', bytesToHexStrSpace(array2), result)
return result
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
function translateViscaIdForIP(text) {
/*
Modifies the destination address of outgoing Visca-IP messages to the respective visca-rs232 ID from conifg
Always shifts receiver back to 90 to mimic Visca-IP required Visca-ID 1
*/
const regex_to_camera = /8[1234567]/
const regex_from_camera = /[9ABCDEFabcdef]0/
let result = ''
let identifier = text.substring(0, 2)
//console.log('checking translation for ident:',identifier)
if (identifier == '01') {
//with Visca IP Payload call recursion with Visca Payload only
result = text.substring(0, 24) + translateViscaIdForIP(text.substring(24, text.length))
} else if (identifier.match(regex_to_camera)) {
//(identifier == '81') // Visca which should be sent to camera
//{
//if (identifier == '81') // If ViscaID 1 map to Visca ID Matching 1+Offset from default udp port
let socket_id = last_socket.address()['port'] - config.port_udp + 1
//if it's the default socket temporary viscaID overwrides need to be taken into account
if (socket_id == 1 && config.customViscaId != undefined)
{
identifier = config.customViscaId + 80
console.log(`custom viscaID set for first socket ${identifier}`)
}
//otherwise simply translate back with offset
else {
identifier = parseInt(identifier, 16) + socket_id - 1
identifier = identifier.toString(16)
}
result = identifier + text.substring(2, text.length)
/* }
else // Visca ID 2-7 forwarded directly without change
{
console.log('regex_to_camera but not 81')
result = text
}*/
} else if (identifier.match(regex_from_camera)) {
// Visca returned from Camera
// This is always set to camera ID 1 in order to match VS-PTC-IP expectation with port forwarding
// Companion Software ignores respones therefore can safely get the same reply
// WARNING - Any other controller which is able to address other Visca-IDs using Visca-IP needs changes here!
identifier = '90'
result = identifier + text.substring(2, text.length)
} else {
result = text
console.log('Error - no change, not Visca-IP neither from or to Visca')
}
return result
}
function translate_visca_rs232_to_visca_ip(msg) {
/* Function that translates rs232 messages back to Visca IP by adding payload and respective headers
Also triggers conversion for visca ID mapping
*/
const payload_length = addSpaceAndZerosToHexStr(msg.length)
msg = bytesToHexStrSpace(msg)
msg = translateViscaIdForIP(msg)
const response_ip_text = '01 11 00 ' + payload_length + '00 00 00 01 ' + msg
console.log('\tForwardning from RS232 to Visca-IP as', response_ip_text)
udp_respond(response_ip_text)
}
function translate_visca_ip_to_cv620_visca_rs232(msg) {
//camera specific methos which selectively answers or forwards
console.log('Visca-IP Message Processing for', last_address, ':', last_port, 'on:', msg)
//define basic responsens and querys to check for
const visca_ack_text = '90 41 FF'
const visca_ack_ip_text = '01 11 00 03 00 00 00 01 ' + visca_ack_text
const visca_complete_text = '90 51 FF'
const visca_complete_ip_text = '01 11 00 03 00 00 00 01 ' + visca_complete_text
//Control Command - Reset Sequence Number
const visca_reset_sequence_number_ip_text = '02 00 00 01 00 00 00 00 01'
const visca_reset_sequence_number_ip_response_text = '02 01 00 01 00 00 00 00 01' //Acknowledge Control Request
//Tally Mode 5: (Power LED:Red Standby:Red)
const visca_tally_mode_regex = /(8[1234567] 01 7e 01 0a 01 0)[023567]( ff)/
//Visca Inquiry: CAM_PowerInq
//visca_CAMP_PowerInq_text = '81 09 04 00 ff'
//visca_CAMP_PowerInq_ip_text = '01 10 00 05 00 00 00 01 '+visca_CAMP_PowerInq_text
if (compare_array(visca_reset_sequence_number_ip_text, msg)) {
//console.log('\tDetected Visca-IP Control Command - Reset Sequence Number')
//console.log('\tDirect Reply with ACK Control Command because RS232 does not have this option:',
// visca_reset_sequence_number_ip_response_text)
udp_respond(visca_reset_sequence_number_ip_response_text)
} else if (bytesToHexStrSpace(msg).match(visca_tally_mode_regex) != null) {
//special for CV620-bk4 - Does not have Tally but needs to confirm message
console.log('\tDetected Visca-RS232 Command: Tally Mode X:')
console.log('\tDirect Reply with ACK:', visca_ack_ip_text)
udp_respond(visca_ack_ip_text)
console.log('\tDirect Reply with COMPLETE:', visca_complete_ip_text)
udp_respond(visca_complete_ip_text)
} else {
const visca_type = new Uint16Array(msg)[0]
if (0x01 == visca_type) {
const visca_type_2 = new Uint16Array(msg)[1]
if (0x00 == visca_type_2 || 0x10 == visca_type_2) {
//VISCA command || VISCA inquiry
let visca_text = bytesToHexStrSpace(new Uint16Array(msg).slice(8))
visca_text = translateViscaIdForIP(visca_text)
const visca_hex = hexToBytes(visca_text)
serialPort.write(Buffer.from(visca_hex))
console.log('\tVisca-RS232-sent: ', visca_text)
} else if (0x20 == visca_type_2) {
// VISCA device setting command
let visca_text = new Uint16Array(msg).slice(8)
if (0x80 == visca_text[0]) {
//custom message for module visca ID change
let new_id = visca_text[4]
console.log(`VISCA device setting command custom command for ID change of module ${new_id}`)
config.customViscaId = new_id
} else {
console.log('VISCA device setting command forwarding is not implemented')
}
console.log('\tDirect Reply with ACK:', visca_ack_ip_text)
udp_respond(visca_ack_ip_text)
} else {
console.log('Unknown Payload type! - Check Visca-IP Syntax')
}
} else if (2 == visca_type) {
console.log('Control Message of Unknown Type', msg)
} else {
}
}
}
startup()