<commit_before>﻿'use strict';

import HardwareSocketMessageHandler from './hardware/hardwareSocketMessageHandler';

require('../playground/blocks');

const hardwareModuleType = {
    builtIn: 'builtin',
    module: 'module',
};

const hardwareStatement = {
    disconnected: 'disconnected',
    socketConnected: 'socketConnected',
    hardwareConnected: 'hardwareConnected',
};

Entry.HW = class {
    // 하드웨어 프로그램 접속용 주소 (https)
    get httpsServerAddress() {
        return 'https://hardware.playentry.org:23518';
    }

    // 하드웨어 프로그램 접속용 주소 (https)
    get httpsServerAddress2() {
        return 'https://hardware.play-entry.org:23518';
    }

    // 하드웨어 프로그램 접속용 주소 (http)
    get httpServerAddress() {
        return 'http://127.0.0.1:23518';
    }

    constructor() {
        this.sessionRoomId = localStorage.getItem('entryhwRoomId');
        if (!this.sessionRoomId) {
            this.sessionRoomId = this._createRandomRoomId();
            localStorage.setItem('entryhwRoomId', this.sessionRoomId);
        }

        this.TRIAL_LIMIT = 2;
        this.connected = false;
        this.portData = {};
        this.sendQueue = {};
        this.currentDeviceKey = null;
        this.hwModule = null;
        this.hwModuleType = hardwareModuleType.builtIn; // builtin, module
        this.socketType = null;

        const { options = {} } = Entry;
        const { disableHardware = false } = options;

        this._hwPopupCreate();
        !disableHardware && this._initSocket();

        Entry.addEventListener('stop', this.setZero.bind(this));

        // hwChanged 에 걸려있는 다른 이벤트 함수와 동일선상에 두기위함
        Entry.addEventListener('hwChanged', this.refreshHardwareBlockMenu.bind(this));
    }

    _createRandomRoomId() {
        return 'xxxxxxxxyx'.replace(/[xy]/g, (c) => {
            const r = (Math.random() * 16) | 0;
            const v = c === 'x' ? r : (r & 0x3) | 0x8;
            return v.toString(16);
        });
    }

    _connectWebSocket(url, option) {
        const socket = io(url, option);
        socket.io.reconnectionAttempts(this.TRIAL_LIMIT);
        socket.io.reconnectionDelayMax(1000);
        socket.io.timeout(1000);
        socket.on('connect', () => {
            this.socketType = 'WebSocket';
            this._initHardware(socket);
        });

        socket.on('mode', (mode) => {
            if (socket.mode === 0 && mode === 1) {
                this._disconnectHardware();
            }
            this.socketMode = mode;
            socket.mode = mode;
        });

        const messageHandler = new HardwareSocketMessageHandler(socket);
        messageHandler.addEventListener('init', this._loadExternalHardwareBlock.bind(this));
        messageHandler.addEventListener('state', async (statement, name) => {
            /*
            statement 로는 before_connect, connected 등 하드웨어 프로그램의 상태 전부가 오지만
            WS 에서는 connected 외에 전부 socketConnected 상태로 머무르게 된다.
             */
            switch (statement) {
                case 'disconnectHardware':
                    this._disconnectHardware();
                    break;
                case 'connected':
                    // init action 과 동일동작
                    await this._loadExternalHardwareBlock(name);
                    break;
                default:
                    break;
            }
        });

        // 1.7.0 버전 이전 하드웨어 프로그램 종료로직 대응으로 남겨두어야 한다.
        messageHandler.addEventListener('disconnect', this._disconnectHardware.bind(this));
        messageHandler.addEventListener('data', (portData) => {
            this.checkDevice(portData);
            this._updatePortData(portData);
        });

        socket.on('disconnect', () => {
            // cloud PC 연결 클릭시 순간 disconnect 되고 재연결을 시도하기 위한 로직
            this._initSocket();
        });

        return socket;
    }

    async _loadExternalHardwareBlock(moduleName) {
        try {
            await Entry.moduleManager.loadExternalModule(moduleName);
        } catch (e) {
            Entry.toast.alert('모듈 로드실패', `${moduleName} 모듈 불러오기에 실패했습니다.`);
        }
    }

    _initSocket() {
        this.connected = false;

        this.tlsSocketIo1 && this.tlsSocketIo1.removeAllListeners();
        this.tlsSocketIo2 && this.tlsSocketIo2.removeAllListeners();
        this.socketIo && this.socketIo.removeAllListeners();

        const connectHttpsWebSocket = (url) =>
            // TODO ajax 로 entry-hw 살아있는지 확인 후 연결시도 (TRIAL_LIMIT = ajax 로)
            this._connectWebSocket(url, {
                query: {
                    client: true,
                    roomId: this.sessionRoomId,
                },
            });

        if (['http:', 'file:'].indexOf(location.protocol)) {
            this.socketIo = connectHttpsWebSocket(this.httpServerAddress);
        }
        this.tlsSocketIo1 = connectHttpsWebSocket(this.httpsServerAddress);
        this.tlsSocketIo2 = connectHttpsWebSocket(this.httpsServerAddress2);

        Entry.dispatchEvent('hwChanged');
    }

    retryConnect() {
        this.TRIAL_LIMIT = 5;
        this._initSocket();
    }

    openHardwareProgram() {
        this.TRIAL_LIMIT = 5;
        this._executeHardware();

        if (!this.socket || !this.socket.connected) {
            setTimeout(() => {
                this._initSocket();
            }, 1000);
        }
    }

    /**
     * 하드웨어 프로그램과의 연결을 시도한다.
     * 만약 모듈이 로드되어있는 경우라면
     * 동일한 하드웨어 모듈을 로드하도록 하드웨어에 요청한다.
     * @param socket
     * @private<commit_msg>http 프로토콜에서 localhost 웹소켓 커넥션 요청을 하지 않던 문제 수정<commit_after>﻿'use strict';

import HardwareSocketMessageHandler from './hardware/hardwareSocketMessageHandler';

require('../playground/blocks');

const hardwareModuleType = {
    builtIn: 'builtin',
    module: 'module',
};

const hardwareStatement = {
    disconnected: 'disconnected',
    socketConnected: 'socketConnected',
    hardwareConnected: 'hardwareConnected',
};

Entry.HW = class {
    // 하드웨어 프로그램 접속용 주소 (https)
    get httpsServerAddress() {
        return 'https://hardware.playentry.org:23518';
    }

    // 하드웨어 프로그램 접속용 주소 (https)
    get httpsServerAddress2() {
        return 'https://hardware.play-entry.org:23518';
    }

    // 하드웨어 프로그램 접속용 주소 (http)
    get httpServerAddress() {
        return 'http://127.0.0.1:23518';
    }

    constructor() {
        this.sessionRoomId = localStorage.getItem('entryhwRoomId');
        if (!this.sessionRoomId) {
            this.sessionRoomId = this._createRandomRoomId();
            localStorage.setItem('entryhwRoomId', this.sessionRoomId);
        }

        this.TRIAL_LIMIT = 2;
        this.connected = false;
        this.portData = {};
        this.sendQueue = {};
        this.currentDeviceKey = null;
        this.hwModule = null;
        this.hwModuleType = hardwareModuleType.builtIn; // builtin, module
        this.socketType = null;

        const { options = {} } = Entry;
        const { disableHardware = false } = options;

        this._hwPopupCreate();
        !disableHardware && this._initSocket();

        Entry.addEventListener('stop', this.setZero.bind(this));

        // hwChanged 에 걸려있는 다른 이벤트 함수와 동일선상에 두기위함
        Entry.addEventListener('hwChanged', this.refreshHardwareBlockMenu.bind(this));
    }

    _createRandomRoomId() {
        return 'xxxxxxxxyx'.replace(/[xy]/g, (c) => {
            const r = (Math.random() * 16) | 0;
            const v = c === 'x' ? r : (r & 0x3) | 0x8;
            return v.toString(16);
        });
    }

    _connectWebSocket(url, option) {
        const socket = io(url, option);
        socket.io.reconnectionAttempts(this.TRIAL_LIMIT);
        socket.io.reconnectionDelayMax(1000);
        socket.io.timeout(1000);
        socket.on('connect', () => {
            this.socketType = 'WebSocket';
            this._initHardware(socket);
        });

        socket.on('mode', (mode) => {
            if (socket.mode === 0 && mode === 1) {
                this._disconnectHardware();
            }
            this.socketMode = mode;
            socket.mode = mode;
        });

        const messageHandler = new HardwareSocketMessageHandler(socket);
        messageHandler.addEventListener('init', this._loadExternalHardwareBlock.bind(this));
        messageHandler.addEventListener('state', async (statement, name) => {
            /*
            statement 로는 before_connect, connected 등 하드웨어 프로그램의 상태 전부가 오지만
            WS 에서는 connected 외에 전부 socketConnected 상태로 머무르게 된다.
             */
            switch (statement) {
                case 'disconnectHardware':
                    this._disconnectHardware();
                    break;
                case 'connected':
                    // init action 과 동일동작
                    await this._loadExternalHardwareBlock(name);
                    break;
                default:
                    break;
            }
        });

        // 1.7.0 버전 이전 하드웨어 프로그램 종료로직 대응으로 남겨두어야 한다.
        messageHandler.addEventListener('disconnect', this._disconnectHardware.bind(this));
        messageHandler.addEventListener('data', (portData) => {
            this.checkDevice(portData);
            this._updatePortData(portData);
        });

        socket.on('disconnect', () => {
            // cloud PC 연결 클릭시 순간 disconnect 되고 재연결을 시도하기 위한 로직
            this._initSocket();
        });

        return socket;
    }

    async _loadExternalHardwareBlock(moduleName) {
        try {
            await Entry.moduleManager.loadExternalModule(moduleName);
        } catch (e) {
            Entry.toast.alert('모듈 로드실패', `${moduleName} 모듈 불러오기에 실패했습니다.`);
        }
    }

    _initSocket() {
        this.connected = false;

        this.tlsSocketIo1 && this.tlsSocketIo1.removeAllListeners();
        this.tlsSocketIo2 && this.tlsSocketIo2.removeAllListeners();
        this.socketIo && this.socketIo.removeAllListeners();

        const connectHttpsWebSocket = (url) =>
            // TODO ajax 로 entry-hw 살아있는지 확인 후 연결시도 (TRIAL_LIMIT = ajax 로)
            this._connectWebSocket(url, {
                query: {
                    client: true,
                    roomId: this.sessionRoomId,
                },
            });

        if (['http:', 'file:'].indexOf(location.protocol) > -1) {
            this.socketIo = connectHttpsWebSocket(this.httpServerAddress);
        }
        this.tlsSocketIo1 = connectHttpsWebSocket(this.httpsServerAddress);
        this.tlsSocketIo2 = connectHttpsWebSocket(this.httpsServerAddress2);

        Entry.dispatchEvent('hwChanged');
    }

    retryConnect() {
        this.TRIAL_LIMIT = 5;
        this._initSocket();
    }

    openHardwareProgram() {
        this.TRIAL_LIMIT = 5;
        this._executeHardware();

        if (!this.socket || !this.socket.connected) {
            setTimeout(() => {
                this._initSocket();
            }, 1000);
        }
    }

    /**
     * 하드웨어 프로그램과의 연결을 시도한다.
     * 만약 모듈이 로드되어있는 경우라면
     * 동일한 하드웨어 모듈을 로드하도록 하드웨어에 요청한다.
     * @param socket
     * @private
{'fraction_non_alphanumeric': 0.1189889588466101, 'fraction_numerical': 0.010676156583629894, 'mean_word_length': 1.9375502546234253, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 20, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2526119', 'n_tokens_mistral': 3664, 'n_tokens_neox': 3883, 'n_words': 609}