import SocketIOClient from 'socket.io-client'
import get from 'lodash-es/get.js'
import genPm from 'wsemi/src/genPm.mjs'
import genID from 'wsemi/src/genID.mjs'
import Evem from 'wsemi/src/evem.mjs'
import sendSplitData from './sendSplitData.mjs'
import mergeSplitData from './mergeSplitData.mjs'
/**
* 建立SocketIO使用者(Node.js與Browser)端物件
*
* @class
* @param {Object} opt 輸入設定參數物件
* @param {String} [opt.url='http://localhost:8080'] 輸入SocketIO伺服器網址,預設為'http://localhost:8080'
* @param {String} [opt.token='*'] 輸入使用者認證用token,預設為'*'
* @param {Integer} [opt.strSplitLength=1000000] 輸入傳輸封包長度整數,預設為1000000
* @param {Object} [opt.ioSettings={}] 輸入SocketIO初始化設定物件,預設為{}
* @returns {Object} 回傳通訊物件,可監聽事件open、openOnce、close、error、reconn、broadcast、deliver,可使用函數execute、broadcast、deliver
* @example
*
* import WConversiClient from 'w-conversi/dist/w-conversi-client.umd.js'
*
* let opt = {
* url: 'http://localhost:8080',
* token: '*',
* }
*
* //new
* let wo = new WConversiClient(opt)
*
* wo.on('open', function() {
* console.log('client nodejs: open')
* })
* wo.on('openOnce', function() {
* console.log('client nodejs: openOnce')
*
* //execute
* wo.execute('add', { p1: 1, p2: 2 },
* function (prog) {
* console.log('client nodejs: execute prog=', prog)
* })
* .then(function(r) {
* console.log('client nodejs: execute: add=', r)
* })
*
* //broadcast
* wo.broadcast('client nodejs broadcast hi', function (prog) {
* console.log('client nodejs: broadcast prog=', prog)
* })
*
* //deliver
* wo.deliver('client deliver hi', function (prog) {
* console.log('client nodejs: deliver prog=', prog)
* })
*
* })
* wo.on('close', function() {
* console.log('client nodejs: close')
* })
* wo.on('error', function(err) {
* console.log('client nodejs: error', err)
* })
* wo.on('reconn', function() {
* console.log('client nodejs: reconn')
* })
* wo.on('broadcast', function(data) {
* console.log('client nodejs: broadcast', data)
* })
* // wo.on('deliver', function(data) { //伺服器目前無法針對client直接deliver
* // console.log('client nodejs: deliver=', data)
* // })
*
*/
function WConversiClient(opt) {
let bOpened = false //SocketIO是否第一次開啟
let ioc = null //SocketIO
//ee, ev
let ee = new Evem()
let ev = new Evem()
//eeEmit
function eeEmit(name, ...args) {
setTimeout(() => {
ee.emit(name, ...args)
}, 1)
}
function core() {
//default
if (!opt.url) {
opt.url = 'http://localhost:8080'
}
if (!opt.token) {
opt.token = '*'
}
if (!opt.strSplitLength) {
opt.strSplitLength = 1000000
}
if (!opt.ioSettings) {
opt.ioSettings = {}
}
//socket.io-client
try {
ioc = new SocketIOClient(opt.url, opt.ioSettings)
}
catch (err) {
error('create SocketIOClient catch error', err)
return null
}
//check
if (!ioc) {
error('can not create SocketIOClient', 'ioc from SocketIOClient is null')
return null
}
//connect
ioc.on('connect', function() {
open()
openOnce()
})
//message
ioc.on('message', function(data) {
message(data)
})
//disconnect
ioc.on('disconnect', function() {
close()
})
//reconnect
ioc.on('reconnect', function() {
reconn()
})
//error
ioc.on('error', function(err) {
error('ioc error', err)
})
// //connect_error
// ioc.on('connect_error', function(err) {
// error('ioc connect_error', err)
// })
// ioc.io.on('connect_error', function(err) {
// error('ioc.io connect_error', err)
// })
// //connect_timeout
// ioc.on('connect_timeout', function(err) {
// error('ioc connect_timeout', err)
// })
// //reconnect_error
// ioc.on('reconnect_error', function(err) {
// error('ioc reconnect_error', err)
// })
// //reconnect_failed
// ioc.on('reconnect_failed', function(err) {
// error('ioc reconnect_failed', err)
// })
/**
* SocketIO監聽開啟事件
*
* @memberof WConversiClient
* @example
* wo.on('open', function() {
* ...
* })
*/
function onOpen() {} onOpen()
function open() {
eeEmit('open')
}
/**
* SocketIO監聽第一次開啟事件
*
* @memberof WConversiClient
* @example
* wo.on('openOnce', function() {
* ...
* })
*/
function onOpenOnce() {} onOpenOnce()
function openOnce() {
if (!bOpened) {
eeEmit('openOnce')
bOpened = true
}
}
/**
* SocketIO監聽關閉事件
*
* @memberof WConversiClient
* @example
* wo.on('close', function() {
* ...
* })
*/
function onClose() {} onClose()
function close() {
eeEmit('close')
}
/**
* SocketIO錯誤事件
*
* @memberof WConversiClient
* @param {*} err 接收錯誤訊息
* @example
* wo.on('error', function(err) {
* ...
* })
*/
function onError() {} onError()
function error(msg, err) {
eeEmit('error', { msg, err })
}
function message(message) {
//console.log('message', message)
//mergeSplitData
mergeSplitData(message, parserData)
}
function parserData(data) {
//_mode
let _mode = get(data, '_mode', '')
//emit
if (_mode === 'execute') {
if (get(data, '_id') && get(data, 'output')) {
//_id
let _id = get(data, '_id')
//output
let output = get(data, 'output')
//emit
ev.emit(_id, output)
}
else {
//無效資料
error('can not find _id and output in data', data)
}
}
else if (_mode === 'broadcast') {
//broadcast 廣播
eeEmit('broadcast', get(data, 'data'))
}
else if (_mode === 'deliver') {
//deliver 交付
eeEmit('deliver', get(data, 'data'))
}
else {
error('can not find _mode in data', data)
}
}
function sendData(data, cbProgress) {
//console.log('sendData', data)
try {
//sendSplitData
sendSplitData(ioc, opt.strSplitLength, data, cbProgress, function (err) {
error('can not send message', err)
})
}
catch (err) {
error('sendSplitData catch error', err)
}
}
function triggerExecute(func, input = null, cbResult, cbProgress) {
//_id
let _id = genID()
//msg
let msg = {
_mode: 'execute',
_id,
func,
input,
}
//sendData
sendData(msg, cbProgress)
//等待結果回傳
ev.on(_id, function (output) {
//cbResult
cbResult(output)
//removeAllListeners
ev.removeAllListeners(_id)
})
}
function triggerBroadcast(data, cbProgress) {
//msg
let msg = {
_mode: 'broadcast',
data,
}
//sendData
sendData(msg, cbProgress)
}
function triggerDeliver(data, cbProgress) {
//msg
let msg = {
_mode: 'deliver',
data,
}
//sendData
sendData(msg, cbProgress)
}
//triggerExecute, 若斷線重連則需自動清除過去監聽事件
ee.removeAllListeners('triggerExecute')
ee.on('triggerExecute', triggerExecute)
//triggerBroadcast, 若斷線重連則需自動清除過去監聽事件
ee.removeAllListeners('triggerBroadcast')
ee.on('triggerBroadcast', triggerBroadcast)
//triggerDeliver, 若斷線重連則需自動清除過去監聽事件
ee.removeAllListeners('triggerDeliver')
ee.on('triggerDeliver', triggerDeliver)
}
/**
* SocketIO監聽伺服器端廣播事件
*
* @memberof WConversiClient
* @param {*} data 傳入廣播訊息
* @example
* wo.on('broadcast', function(data) {
* ...
* })
*/
function onBroadcast() {} onBroadcast()
/**
* SocketIO監聽伺服器端交付事件
*
* @memberof WConversiClient
* @param {*} data 傳入交付訊息
* @example
* wo.on('deliver', function(data) {
* ...
* })
*/
function onDeliver() {} onDeliver()
/**
* SocketIO通訊物件對伺服器端執行函數,表示傳送資料給伺服器,並請伺服器執行函數
*
* @memberof WConversiClient
* @function execute
* @param {String} func 輸入執行函數之名稱字串
* @param {*} [input=null] 輸入執行函數之輸入資訊
* @example
* let func = 'NameOfFunction'
* let input = {...}
* wo.execute(func, input)
*/
ee.execute = function (func, input, cbProgress = function () {}) {
let pm = genPm()
eeEmit('triggerExecute', func, input,
function(output) { //結果用promise回傳
pm.resolve(output)
},
cbProgress //傳輸進度用cb回傳
)
return pm
}
/**
* SocketIO通訊物件對伺服器端廣播函數,表示傳送資料給伺服器,還需轉送其他客戶端
*
* @memberof WConversiClient
* @function broadcast
* @param {*} data 輸入廣播函數之輸入資訊
* @example
* let data = {...}
* wo.broadcast(data)
*/
ee.broadcast = function (data, cbProgress = function () {}) {
eeEmit('triggerBroadcast', data, cbProgress)
}
/**
* SocketIO通訊物件對伺服器端交付函數,表示僅傳送資料給伺服器
*
* @memberof WConversiClient
* @function deliver
* @param {*} data 輸入廣播函數之輸入資訊
* @example
* let data = {...}
* wo.deliver(data)
*/
ee.deliver = function (data, cbProgress = function () {}) {
eeEmit('triggerDeliver', data, cbProgress)
}
/**
* SocketIO監聽重連事件
*
* @memberof WConversiClient
* @example
* wo.on('reconn', function() {
* ...
* })
*/
function onReconn() {} onReconn()
function reconn() {
eeEmit('reconn')
}
//core
core()
return ee
}
export default WConversiClient