node-edge
Version:
node-edge is a tcp communication library for edge computing using local area networking.
615 lines (514 loc) • 17.1 kB
JavaScript
const edge = require('node-edge')
const { Buffer } = require('node:buffer')
const { createHash } = require('node:crypto')
function voltageSource(){
return 50 + Math.floor(Math.random() * 10)
}
let port = 8125
/****************************************************************
A quick simple client/server api test
Pass: The test will run continously w/o errors for a least 30000 or half-minute
Fail: If any error occurs the test run will throw an error
****************************************************************/
/********************
server section
********************/
// edge.createServer(port, (server) => { // port must be provided
// throws an error due to data has ciphertext
// 1. create server w/ callback
/*edge.createServer(8127, (server) => {
server.publish('server-pub-1', (tcp) => {
tcp.send( voltageSource() )
})
server.subscribe('client-pub-1', (data) => {
console.log('client-pub-1', typeof data) // expect data as buffer from regular string or number
if(typeof data !== 'object'){
throw 'invalid data as buffer'
}
if(!Buffer.isBuffer(data)){
throw 'invalid data as buffer'
}
})
server.on('connection', (count) => {
console.log('****************** server2 connected client:', count)
if(count > 5){
console.log('runaway client count!')
process.exit()
}
})
})*/
// 2. create server w/o callback then return the server object and using server.listen(port) to start the server
// const server = edge.createServer(port) // add the port and server will start listening immediately instead of using the server.listen(port)
const server = edge.createServer()
/***
* publish method
*/
let serverPayload = {}
server.publish('server-pub-1', (tcp) => {
let v = voltageSource()
tcp.send( v ) // number payload
serverPayload.data = {topic:'server-pub-1', type:'number', value:v}
})
server.pub('server-pub-2', (tcp) => {
tcp.polling = 9000
let v = {value:voltageSource()}
const hash = createHash('sha256')
hash.update( JSON.stringify(v) )
serverPayload.data.hash = hash.digest('hex')
tcp.send( v ) // object payload
serverPayload.data = {topic:'server-pub-2', type:'object', value:v}
})
server.pub('server-pub-3', (tcp) => {
tcp.polling = 7000
let v = JSON.stringify({topic:'server-pub-3', value:voltageSource()})
const hash = createHash('sha256')
hash.update( v )
serverPayload.data.hash = hash.digest('hex')
tcp.send( v ) // json string payload
serverPayload.data = {topic:'server-pub-3', type:'json string', value:v}
})
server.publish('server-pub-4', (tcp) => {
let v = voltageSource().toString()
tcp.send( v ) // regular string payload
serverPayload.data = {topic:'server-pub-4', type:'regular string', value:v}
})
/***
* subscribe method
*/
server.subscribe('client-pub-1', (data) => { // data can be buffer, object and string
console.log('sub client-pub-1', typeof data, data) // expect data as buffer from regular string or number
if(!Buffer.isBuffer(data) && typeof data !== 'object' && typeof data !== 'string'){
throw 'invalid data'
}
})
server.sub('client-pub-2', (data) => {
console.log('client-pub-2', data) // expect data as object
if(typeof data !== 'object'){
throw 'invalid data as object'
}
})
server.sub('client-pub-3', (data) => {
console.log('client-pub-3', data) // expect data as json string
if(typeof data !== 'string'){
throw 'invalid data as json string'
}
let d = JSON.parse(data)
if(typeof d !== 'object'){
throw 'invalid parse data from json string'
}
})
/***
* dataSource for read/getData method
*/
server.dataSource('source-data-1', (tcp) => {
console.log('source-data-1', tcp.payload)
// payload can be a regular string | number | object | json string
if(tcp.payload === 'on'){
//tcp.send( tcp.payload )
tcp.end( tcp.payload )
}
else if(tcp.payload === 'off'){
tcp.send( tcp.payload )
//tcp.end( tcp.payload )
}
else{
tcp.send( voltageSource().toString() )
//tcp.end( voltageSource().toString() )
}
})
server.on('source-data-2', (tcp) => {
if(tcp.payload){
console.log('source-data-2', tcp.payload)
}
else{
tcp.end( voltageSource() )
}
})
server.on('source-data-3', (tcp) => {
tcp.send( {topic:'source-data-3' , value:voltageSource()} )
})
server.on('source-data-4', (tcp) => {
tcp.send( JSON.stringify({topic:'source-data-3' , value:voltageSource()}) )
})
/***
* dataSource for write/sendData method
*/
server.dataSource('send-data-1', (tcp) => {
if(tcp.payload){
console.log(tcp.topic, tcp.payload)
if(typeof tcp.payload !== 'object'){
throw 'invalid data as object'
}
tcp.send( tcp.payload )
}
})
let testSend = 0
server.on('send-data-2', (tcp) => { // JSON.stringify({value:55})
++testSend
if(tcp.payload){
console.log(tcp.topic, tcp.payload)
if(typeof tcp.payload !== 'string'){
throw 'invalid data as json string'
}
let d = JSON.parse(tcp.payload)
if(typeof d !== 'object'){
throw 'invalid data as object'
}
if(testSend > 10){
tcp.send( tcp.payload ) // reply as json string
}
else if(testSend > 20) {
tcp.send( d ) // reply as object
testSend = 0
}
else{
tcp.send( d )
}
}
})
server.on('send-data-3', (tcp) => {
if(tcp.payload){
console.log(tcp.topic, tcp.payload)
if(typeof tcp.payload !== 'number'){
throw 'invalid data as a number'
}
tcp.send( tcp.payload )
}
})
server.on('send-data-4', (tcp) => {
if(tcp.payload){
console.log(tcp.topic, tcp.payload)
if(typeof tcp.payload !== 'string'){
throw 'invalid data as regular string'
}
tcp.send(tcp.payload)
}
})
server.on('error', (e) => {
console.log('server error:', e.message)
if(e.message){
throw 'invalid server error message'
}
})
server.on('connection', (count) => {
console.log('****************** server1 connected client:', count)
if(count > 50){
console.log('runaway client count!')
process.exit()
}
})
server.listen(port)
setImmediate(() => {
console.log('server.listening', server.listening)
console.log('server.address()', server.address())
})
/*********************
client section
*********************/
let secure = true
// create a client w/o a callback then return the client
//let ec1 = new edge.client(port)
let ec1 = new edge.client({port:8125, restart:true, secure:secure })
console.log('edge client', port)
ec1.on('ready', (data) => {
console.log('ec1 ready', data)
if(!data){
throw 'invalid ready data'
}
})
ec1.on('error', (e) => {
console.log('ec1 error', e.message)
if(e.message){
throw 'invalid client error message'
}
})
/***
* getResources method
*/
ec1.getResources((data) => {
console.log('server resources', data)
if(!Array.isArray(data)){
throw 'invalid getResources data'
}
})
setInterval(() => {
ec1.read('server-pub-1', (data) => { // tcp.send( voltageSource() )
console.log('ec1 read latest server-pub-1', typeof data, data.toString())
if(!Buffer.isBuffer(data) && serverPayload.type === 'number'){
throw 'invalid data as buffer'
}
})
}, 1000)
/***
* subscribe method
*/
ec1.subscribe('server-pub-1', (data) => { // tcp.send( voltageSource() )
console.log('server-pub-1', typeof data, data, data.toString())
if(!Buffer.isBuffer(data) && serverPayload.data.type === 'number' && serverPayload.data.value === parseInt(data)){
throw 'invalid data as buffer'
}
let v = parseInt(data)
//console.log('server-pub-1', v)
if(v < 53){
ec1.send('source-data-1', 'on') //, (data) => { console.log('***send source-data-1 on', data) }) // ok
}
else{
ec1.send('source-data-1', 'off', (data) => {
console.log('***send source-data-1 off', data)
})
}
})
ec1.sub('server-pub-2', (data) => { // tcp.send( {value:voltageSource()} )
console.log('server-pub-2', data)
const hash = createHash('sha256')
hash.update( JSON.stringify(data) )
let dataHash = hash.digest('hex')
if(serverPayload.data.type === 'object' && serverPayload.data.value === data && serverPayload.data.hash === dataHash ){
throw 'invalid data as object'
}
})
ec1.sub('server-pub-3', (data) => { // tcp.send( JSON.stringify({topic:'server-pub-3', value:voltageSource()}) )
console.log('server-pub-3', typeof data, secure, data, serverPayload.data.value)
// expect a string json due to JSON.stringify({topic:'server-pub-3', value:voltageSource()})
const hash = createHash('sha256')
hash.update(data.toString())
let dataHash = hash.digest('hex')
if(serverPayload.data.value === data.toString() && serverPayload.data.hash === dataHash ){
throw 'invalid rcvd data as json string'
}
if(!Buffer.isBuffer(data)){
throw 'invalid data as buffer'
}
let d = JSON.parse(data)
if(typeof d !== 'object'){
throw 'invalid parse data from json string'
}
})
ec1.subscribe('server-pub-4', (data) => { // tcp.send( '155' )
console.log('server-pub-4', typeof data, data)
if(!Buffer.isBuffer(data) && serverPayload.data.type === 'regular string' && serverPayload.data.value === data.toString()){
throw 'invalid data as buffer'
}
let v = parseInt(data) // data is buffer
//console.log('edge-pub-', v)
if(v > 53){
ec1.send('source-data-1', 'on', (data) => {
console.log('***send source-data-1 on', data)
})
}
else{
ec1.send('source-data-1', 'off') //, (data) => { console.log('***send source-data-1 off', data) })
}
})
/***
* publish method
*/
ec1.publish('client-pub-1', (tcp) => { // dynamic payload test
let pl = voltageSource()
// tcp.send(pl)
if(pl <= 50){
tcp.send(pl.toString())
//throw 'string'
}
if(pl > 50 && pl < 55){
tcp.send(pl)
//throw 'number'
}
else if(pl > 55 && pl < 60){
tcp.send({value:pl})
//throw 'object'
}
else{
tcp.send(JSON.stringify({value:pl}))
//throw 'json as string'
}
})
ec1.pub('client-pub-2', (tcp) => {
let pl = voltageSource()
tcp.send({value:pl})
})
ec1.pub('client-pub-3', (tcp) => {
let pl = voltageSource()
tcp.send(JSON.stringify({value:pl}))
})
setInterval(() => { // dynamic payload stress test
ec1.read('client-pub-1', (data) => { // tcp.send( voltageSource() ) or tcp.send({value:pl}) or tcp.send(JSON.stringify({value:pl}))
console.log('ec1 read latest client-pub-1', typeof data, data, data.toString())
if(!Buffer.isBuffer(data) && typeof data !== 'object'){
throw 'invalid data as buffer'
}
})
}, 1200)
setInterval(() => {
ec1.read('client-pub-3', (data) => { // tcp.send(JSON.stringify({value:pl}))
console.log('ec1 read latest client-pub-3', typeof data, data, data.toString())
if(!Buffer.isBuffer(data)){ // secure = false/true
throw 'invalid data as buffer'
}
})
}, 1200)
/***
* read/getData method
*/
ec1.read('source-data-1', (data) => { // expect data as buffer from a regular string data source
console.log('ec1 source-data-2', data.toString() , data)
if(typeof data !== 'object'){
throw 'invalid data as buffer'
}
if(!Buffer.isBuffer(data)){
throw 'invalid data as buffer'
}
})
ec1.read('source-data-2', (data) => { // expect data as buffer from a number data source
console.log('ec1 source-data-2', data.toString() , data)
if(typeof data !== 'object'){
throw 'invalid data as buffer'
}
if(!Buffer.isBuffer(data)){
throw 'invalid data as buffer'
}
})
ec1.getData('source-data-3', (data) => { // expect data as object {topic:'source-data-3' , value:voltageSource()}
console.log('ec1 source-data-3', data)
console.log('ec1 source-data-3 data.topic', data.topic)
if(typeof data !== 'object'){
throw 'invalid data as object'
}
})
ec1.getData('source-data-4', (data) => { // expect data as object JSON.stringify({topic:'source-data-3' , value:voltageSource()})
console.log('ec1 source-data-3', data)
console.log('ec1 source-data-3 data.topic', data.topic)
if(!Buffer.isBuffer(data)){ // secure = false/true fixed 3/14/23
throw 'invalid data as buffer'
}
let d = JSON.parse(data)
if(typeof d !== 'object'){
throw 'invalid parse data from json string'
}
})
// getData stress test
setInterval(() => {
ec1.read('source-data-2', (data) => {
console.log('stress test - read ec1 source-data-2', data.toString() , data) // expect data as buffer from a number or regular string data source send
if(typeof data !== 'object'){
throw 'invalid data as buffer'
}
if(!Buffer.isBuffer(data)){
throw 'invalid data as buffer'
}
})
}, 500)
/***
* send/write/sendData or sendData method
*/
ec1.sendData('send-data-1', {value:55}, (data) => { // rcvd data is an object
console.log('*send-data-1', data)
if(typeof data !== 'object'){
throw 'invalid rcvd data as object'
}
})
ec1.write('send-data-2', JSON.stringify({value:55}), (data) => { // need to JSON.parse(rcvd data) in dataSource
console.log('*send-data-2', typeof data, secure, data)
if(typeof data !== 'string' && typeof data !== 'object'){
throw 'invalid payload type'
}
if(testSend > 21){
throw 'runaway testSend'
}
})
ec1.sendData('send-data-3', 105, (data) => { // rcvd data is a buffer
console.log('*send-data-3', data)
if(typeof data !== 'object'){
throw 'invalid rcvd data as buffer'
}
if(!Buffer.isBuffer(data)){
throw 'invalid data as buffer'
}
})
ec1.send('send-data-4', '105', (data) => { // rcvd data is a buffer
console.log('*send-data-4', data.toString(), data)
if(typeof data !== 'object'){
throw 'invalid rcvd data as buffer'
}
if(!Buffer.isBuffer(data)){
throw 'invalid data as buffer'
}
})
// sendData stress test
setInterval(() => {
ec1.sendData('send-data-1', {value:55}, (data) => { // rcvd data is an object
console.log('* stress test - send-data-1', data)
if(typeof data !== 'object'){
throw 'invalid rcvd data as object'
}
})
ec1.write('send-data-2', JSON.stringify({value:55}), (data) => { // need to JSON.parse(rcvd data) in dataSource
console.log('*send-data-2', typeof data, secure, data)
if(typeof data !== 'string' && typeof data !== 'object'){
throw 'invalid payload type'
}
})
}, 400)
setTimeout(() => {
/***
* unsubscribe method
*/
console.log('>>>>>>>> unsub')
ec1.unsubscribe('server-pub-1', (data) => {
console.log('unsub server-pub-1', typeof data, data.toString())
//if(typeof data !== 'boolean'){ // secure = true, not the case anymore - fixed 3/14/23
//throw 'invalid data as boolean'
//}
if(!Buffer.isBuffer(data)){ // valid for secure/unsecure
throw 'invalid data as buffer'
}
})
ec1.unsub('server-pub-2', (data) => {
console.log('unsub server-pub-2', data.toString())
//if(typeof data !== 'boolean'){
//throw 'invalid data as boolean'
//}
if(!Buffer.isBuffer(data)){
throw 'invalid data as buffer'
}
})
ec1.unsub('server-pub-3', (data) => {
console.log('unsub server-pub-3', data.toString())
//if(typeof data !== 'boolean'){
//throw 'invalid data as boolean'
//}
if(!Buffer.isBuffer(data)){
throw 'invalid data as buffer'
}
})
}, 20000)
setTimeout(() => {
/***
* subscribe again method
*/
console.log('>>>>>>>> sub again')
// callback should not be executed if successful
ec1.subscribe('server-pub-1', (data) => {
console.log('sub again server-pub-1', data.toString())
if(data){
throw 'invalid return data'
}
})
ec1.sub('server-pub-2')
ec1.sub('server-pub-3')
}, 30000)
let count = 0
setInterval(() => {
++count
console.log('count', count)
if(secure && count > 5){
secure = false
count = 0
}
else if(secure === false && count > 5){
secure = true
count = 0
}
console.log('secure', secure)
}, 10000)