socketio.js 12.4 KB
const db_total = require('./db_total.js'); //DB 합침
const secret_key = require('../keys/api_option').key;
const secret_key_traffic = require('../keys/api_option_traffic').key;
const secret_key_air = require('../keys/api_option_air').key;

const requesting = require('request');

const lat = "37.54922931758"; //weather 위도
const lon = "126.913415969472"; //weather 경도
const sido_num = "11"; //traffic 법정동 시도 코드
const gugun_num = "440"; //traffic 법정동 시군구 코드 - 합정동 일대
const search_year = "2018074"; //traffic 검색을 원하는 연도

module.exports = (server, app) => {

    const io = require('socket.io')(server, {
        transports: ['websocket'] // websocket 사용시 polling 사용을 배제하고 안정적인 websocket만 사용함
    });

    //명시적 형 선언 - Weather, Traffic, Common
    let Current_Weather = {};
    let Sensible_T = {};
    let Heat_index = {};
    let Discomport_index = {};
    let Ultra_Violet_index = {};
    let sending_to_client_info = {};
    let client_send = {};
    let client_name = "";
    let client_birth;
    let Destiny;
    let sql;
    let info = {} //;?

    let Current_TrafficAcc = {};
    let Current_AirPollution = {};

    //API 호출_Weather
    const req_API = (when, what) => {
        //async await 사용하기 위하여 promise 사용
        return new Promise((resolve, reject) => {
            requesting.get({
                // api를 요청할 주소 -- 시크릿키,위도,경도 입력
                url: `https://api2.sktelecom.com/weather/${when}/${what}?appKey=${secret_key}&lat=${lat}&lon=${lon}`,
                json: true
            },
                //api에게 응답 받았을때 실행되는 callback function
                function (err1, api_res1, api_body1) {
                    //err 존재시 promise reject 호출
                    if (err1) reject(err1);

                    // api의 response이 있을경우 promise resolve 호출
                    if (api_res1) {
                        console.log("calling weather api");
                        resolve(api_body1);
                    }
                });

        }) //Promise
    }


    //API 호출_Traffic
    const req_API_traffic = (cate1, cate2) => {
        //async await 사용하기 위하여 promise 사용
        return new Promise((resolve, reject) => {
            requesting.get({
                // api를 요청할 주소 -- 시크릿키,위도,경도 입력
                url: `http://apis.data.go.kr/B552061/${cate1}/${cate2}?ServiceKey=${secret_key_traffic}&searchYearCd=${search_year}&siDo=${sido_num}&guGun=${gugun_num}&type=json&numOfRows=1&pageNo=1`,
                json: true
            },
                //api에게 응답 받았을때 실행되는 callback function
                function (err, api_res, api_body) {
                    //err 존재시 promise reject 호출
                    if (err) reject(err);

                    // api의 response이 있을경우 promise resolve 호출
                    if (api_res) {
                        console.log("calling traffic api");
                        resolve(api_body);
                    }
                });
        }) //Promise
    }

    //API 호출_Traffic
    const req_API_air = () => {
        //async await 사용하기 위하여 promise 사용
        return new Promise((resolve, reject) => {
            requesting.get({
                // api를 요청할 주소 -- 시크릿키,위도,경도 입력
                url: `http://api.airvisual.com/v2/nearest_city?lat=${lat}&lon=${lon}&key=${secret_key_air}`,
                json: true
            },
                //api에게 응답 받았을때 실행되는 callback function
                function (err, api_res, api_body) {
                    //err 존재시 promise reject 호출
                    if (err) reject(err);

                    // api의 response이 있을경우 promise resolve 호출
                    if (api_res) {
                        console.log("calling air pollution api");
                        resolve(api_body);
                    }
                });
        }) //Promise
    }

    const API_bundle = async () => {
        try {
            var order = 0;
            while (order < 2) {
                switch (order) {
                    case 0:
                        console.log("APIWait - WAITING FOR ALL API");
                        Current_Weather = await req_API("current", "minutely"); //현재날씨 (분별) //when, what
                        Sensible_T = await req_API("index", "wct"); //체감온도
                        Heat_index = await req_API("index", "heat"); //열지수
                        Discomport_index = await req_API("index", "th"); //불쾌지수
                        Ultra_Violet_index = await req_API("index", "uv"); //자외선지수
                        Current_TrafficAcc = await req_API_traffic("frequentzoneLg", "getRestFrequentzoneLg"); //cate1, cate2                               
                        Current_AirPollution = await req_API_air();
                        order = 1;
                        break;
                    case 1:
                        console.log("APIDataProcess - CALLING INFO");
                        info = {
                            heat: Heat_index.weather.wIndex.heatIndex[0].current.index, //열지수
                            sensible_temperature: Sensible_T.weather.wIndex.wctIndex[0].current.index, //체감온도
                            discomport: Discomport_index.weather.wIndex.thIndex[0].current.index, //불쾌지수
                            UV: Ultra_Violet_index.weather.wIndex.uvindex[0].day01.index, //자외선지수
                            windspd: Current_Weather.weather.minutely[0].wind.wspd, //바람 속도
                            sky: Current_Weather.weather.minutely[0].sky.code, //하늘 상태
                            rain: Current_Weather.weather.minutely[0].rain.last24hour, //강수량 
                            current_temperature: Current_Weather.weather.minutely[0].temperature.tc, //현재 온도
                            lightning: Current_Weather.weather.minutely[0].lightning, //현재 낙뢰
                            warning: Current_Weather.common.alertYn, //현재 특보 유무
                            typhoon: Current_Weather.common.stormYn, //현재 태풍
                            time: Current_Weather.weather.minutely[0].timeObservation, // 불러온 시각
                            death_prob: 0, //확률

                            death_number: Current_TrafficAcc.items.item[0].dth_dnv_cnt, //사망자 수 
                            midhurt_number: Current_TrafficAcc.items.item[0].se_dnv_cnt, //중상자 수
                            lighthurt_number: Current_TrafficAcc.items.item[0].sl_dnv_cnt, //경상자 수
                            occur_number: Current_TrafficAcc.items.item[0].occrrnc_cnt, //발생건수

                            airpollution: Current_AirPollution.data.current.pollution.aqius //대기질 지수
                            //aqi = 대기질 지수, aqius = us기준으로 산정, mainus:p2 미세먼지 기준 pm2.5 
                            //0-50 좋음 //51-100 보통 // 101-150 나쁨
                        }
                        console.log("API INFO \n", info);

                        //  ------------------------------ death_prob 정의 ------------------------------

                        info.death_prob += info.sky.substr(5) * 1  //하늘 상태에 따라 확률 증가

                        if (info.lightning === 1)    //낙뢰시에 확률 증가
                            info.death_prob += 1.5;
                        if (info.typhoon === "Y")   //태풍시에 확률 증가
                            info.death_prob += 1.5;
                        if (info.warning === "Y") // 특보 발령시 확률 증가
                            info.death_prob += 1

                        //죽을 확률 계산(내맘대로 커스텀)
                        info.death_prob = (
                            (info.heat / 50) + (Math.abs(info.sensible_temperature - 15) / 10) + (info.discomport / 10) + (info.UV / 10)
                            + info.windspd * 1 + (info.rain / 10) + (Math.abs(info.current_temperature - 15) / 10)
                            + (info.death_number / 60) + (info.midhurt_number / 80) + (info.lighthurt_number / 90) + (info.occur_number / 90)
                            + (info.airpollution / 100)
                        );

                        //이벤트 기반으로 일정 시간 간격으로 클라이언트에게 보낼 정보, 홈페이지 그래프에 나타날 정보
                        client_send = {
                            time: info.time,
                            wind: info.windspd,
                            temperature: info.current_temperature,
                            rain: info.rain,
                            death: info.death_prob,
                            trafficdeath: info.death_number,
                            mhurt: info.midhurt_number,
                            lhurt: info.lighthurt_number,
                            occurence: info.occur_number,
                            pollution: info.airpollution
                        };
                        function getRandom_add_prob(min, max) {
                            return Math.random() * (max - min) + min;
                        }


                        // 심장이 크게 뛰며 확률이 증가하거나 감소 할 수 있음
                        Math.random() * 2 >= 1 ? client_send.death += getRandom_add_prob(0, 5) : client_send.death -= getRandom_add_prob(0, 5);


                        //운명의 장난으로 죽을 확률이 증가하거나 감소함 
                        const rand = Math.floor(Math.random() * 6) //생년월일 중 한자리 뽑음

                        Destiny = client_birth.charAt(rand) / 3;  //명시적 형 변환
                        if (Destiny == 0) Destiny = 1; //사용자 잘못 입력했을때 예외처리
                        Math.random() * 2 >= 1 ? client_send.death += Destiny : client_send.death -= Destiny;


                        //만약 날이 너무 안좋아서 확률이 100을 넘긴다면 100으로 예외처리
                        if (client_send.death >= 100) {
                            client_send.death = 100;
                        }

                        console.log("client send data \n", client_send)

                        app.get("socket").emit("weather_traffic_air_Info_minutely_send_to_client", client_send); // 클라이언트에게 정보 담아서 이벤트 발산
                        console.log("emit");

                        //db에 저장
                        sql = "INSERT INTO apisInfo (time,wind,temperature,rain,prob,tdeath,mhurt,lhurt,occurence,pollution) VALUES (?,?,?,?,?,?,?,?,?,?)";
                        db_total.query(sql, [client_send.time, client_send.wind, client_send.temperature, client_send.rain, client_send.death, client_send.trafficdeath, client_send.mhurt, client_send.lhurt, client_send.occurence, client_send.pollution], (err, result) => {
                            if (err) console.log(err);
                        })
                        order = 2;
                        break;
                    default:
                        order = 3;
                        break;
                }//switch
            }; //while

        } catch (err) {  //promise err or try err catch
            console.log("================Error Occured !!================\n", err);
        }
    }

    let call_interval;

    const Start_Interval = (second, CALL) => {
        CALL(); //처음 불러올때 한번 호출하고
        call_interval = setInterval(CALL, second * 1000); //그 후에 1분마다 호출
    }

    io.on('connection', (socket) => { //프론트와 소켓 연결시 이벤트 루프 동작

        app.set("socket", socket);
        socket.on("connection", (client_data) => {
            console.log("SOCKET CONNECTED");
            client_name = client_data.name; //이름 입력
            client_birth = client_data.birth; //생일 입력
            Start_Interval(60, API_bundle); //소켓 연결후 interval 활성화하여 1분마다 API 호출

        });

        socket.on('disconnect', (reason) => {
            console.log("disconnected");
            clearInterval(call_interval); //연결 종료시 interval 해제
        })
    })

}