WComorWebsocketClient.mjs

import WSC from 'w-websocket-client/src/WWebsocketClient.mjs'
import get from 'lodash-es/get.js'
import set from 'lodash-es/set.js'
import genPm from 'wsemi/src/genPm.mjs'
import genID from 'wsemi/src/genID.mjs'
import Evem from 'wsemi/src/evem.mjs'
import j2o from 'wsemi/src/j2o.mjs'
import isWindow from 'wsemi/src/isWindow.mjs'
import isfun from 'wsemi/src/isfun.mjs'


/**
 * 建立WebSocket使用者(Node.js與Browser)端物件
 *
 * @param {Object} opt 輸入設定參數物件
 * @param {String} [opt.url='ws://localhost:8080'] 輸入WebSocket伺服器ws網址,預設為'ws://localhost:8080'
 * @param {String} [opt.token='*'] 輸入使用者認證用token,預設為'*'
 * @param {Function} opt.open 輸入監聽open函數
 * @param {Function} opt.close 輸入監聽close函數
 * @param {Function} opt.error 輸入監聽error函數
 * @param {Function} opt.reconn 輸入監聽reconn函數
 * @returns {Promise} 回傳Promise,resolve為映射伺服器端可用函數之物件,各函數輸入皆為單一物件,各函數回傳皆為Promise,用resolve與reject處理回傳結果
 * @example
 *
 * import WComorWebsocketClient from 'w-comor-websocket/dist/w-comor-websocket-client.umd.js'
 *
 * //opt
 * let opt = {
 *     url: 'ws://localhost:8080',
 *     token: '*',
 *     open: function() {
 *         console.log('client nodejs: open')
 *     },
 *     close: function() {
 *         console.log('client nodejs: close')
 *     },
 *     error: function(err) {
 *         console.log('client nodejs: error:', err)
 *     },
 *     reconn: function() {
 *         console.log('client nodejs: reconn')
 *     },
 * }
 *
 * //WComorWebsocketClient
 * new WComorWebsocketClient(opt)
 *     .then(function(wo) {
 *         console.log('client: funcs: ', wo)
 *
 *         function core(ps) {
 *             wo.group.plus(ps)
 *                 .then(function(r) {
 *                     console.log('client: plus(' + JSON.stringify(ps) + ')=' + r)
 *                 })
 *                 .catch(function(err) {
 *                     console.log('client: plus: catch: ', err)
 *                 })
 *             wo.group.div(ps)
 *                 .then(function(r) {
 *                     console.log('client: div(' + JSON.stringify(ps) + ')=' + r)
 *                 })
 *                 .catch(function(err) {
 *                     console.log('client: div: catch: ', err)
 *                 })
 *             wo.add(ps)
 *                 .then(function(r) {
 *                     console.log(`client: add(${JSON.stringify(ps)})=${r}`)
 *                 })
 *                 .catch(function(err) {
 *                     console.log('client: add: catch: ', err)
 *                 })
 *             wo.minu(ps)
 *                 .then(function(r) {
 *                     console.log(`client: minu(${JSON.stringify(ps)})=${r}`)
 *                 })
 *                 .catch(function(err) {
 *                     console.log('client: minu: catch: ', err)
 *                 })
 *         }
 *
 *         let i = 100
 *         setInterval(function() {
 *             i += 1
 *             core({
 *                 p1: i,
 *                 p2: 10,
 *             })
 *         }, 1000)
 *
 *     })
 *     .catch(function(err) {
 *         console.log('client: catch', err)
 *     })
 *
 */
function WComorWebsocketClient(opt) {
    let pm = genPm()
    let wsc = null //WebSocket
    let wo = {} //回傳操作物件


    //ev
    let ev = new Evem()


    //env
    let env = isWindow() ? 'browser' : 'nodejs'
    // console.log('env', env)


    function core() {


        //optt
        let optt = {
            url: null,
            token: null,
            open: function() {
                fOpen()
            },
            close: function() {
                fClose()
            },
            message: function(data) {
                fMessage(data)
            },
            error: function(err) {
                fError(err)
            },
        }


        //default
        if (!opt.url) {
            optt.url = 'ws://localhost:8080'
        }
        if (!opt.token) {
            optt.token = '*'
        }


        //WebSocket, 網址傳token參數作為識別使用者
        try {
            wsc = new WSC(optt)
        }
        catch (err) {
            reconn()
            return null
        }


        //check
        if (get(wsc, 'error')) {
            reconn()
            return null
        }


        //fOpen
        function fOpen() {
            if (isfun(opt.open)) {
                opt.open()
            }
            execFunction('getFuncs', null)
        }


        //execFunction
        function execFunction(func, input = null) {
            let pmm = genPm()

            //_id
            let _id = genID()

            //msg
            let msg = {
                token: opt.token,
                _id,
                func,
                input,
            }

            //send
            if (wsc.readyState === wsc.OPEN) {
                wsc.send(JSON.stringify(msg), function(err) {
                    if (err) {
                        if (isfun(opt.error)) {
                            opt.error(err)
                        }
                    }
                })
            }

            //等待結果回傳
            ev.on(_id, function (output) {

                //resolve
                pmm.resolve(output)

                //removeAllListeners
                ev.removeAllListeners(_id)

            })

            return pmm
        }


        //fMessage
        function fMessage(message) {
            // console.log('fMessage message', message)

            if (env === 'browser') {
                //瀏覽器端會自動轉string
            }
            else { //nodejs

                //message
                message = Buffer.from(message).toString('utf8')

            }

            //data
            let data = j2o(message)

            //get sys funcs
            if (get(data, 'output.sys') === 'sys' && get(data, 'output.funcs')) {

                //funcs
                let funcs = data['output']['funcs']

                //clear wo
                wo = {}

                //bind funcs
                for (let i = 0; i < funcs.length; i++) {

                    //func
                    let func = funcs[i]

                    //add func
                    let f = function(input) {
                        return execFunction(func, input)
                    }
                    set(wo, func, f)

                }

                //resolve
                pm.resolve(wo)

            }

            //get result
            if (get(data, '_id') && get(data, 'output')) {

                //_id
                let _id = get(data, '_id')

                //output
                let output = get(data, 'output')

                //emit
                ev.emit(_id, output)

            }

        }


        //fClose
        function fClose() {
            if (isfun(opt.close)) {
                opt.close()
            }
            reconn()
        }


        //fError
        function fError(err) {
            if (isfun(opt.error)) {
                opt.error(err)
            }
            wsc.close()
        }


    }


    function reconn() {
        setTimeout(function() {
            if (isfun(opt.reconn)) {
                opt.reconn()
            }
            core()
        }, 1000)
    }


    //core
    core()


    return pm
}


export default WComorWebsocketClient