socketio.js 11.1 KB
const db = require('./db.js');
const db_traffic = require('./db_traffic.js');
const secret_key = require('../keys/api_option').key;
const secret_key_traffic = require('../keys/api_option_traffic').key;

const requesting = require('request');

const lat = "37.54922931758";//"37.239795"; //weather 위도
const lon = "126.913415969472";//"127.083240"; //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 sql_traffic;
    //let DeathNum_index = {}; //사상자
    //let M_HurtNum_index = {}; //중상자
    //let L_HurtNum_index = {}; //경상자
    //let LocalName_index = {}; //지역 이름
    //let OccurNum_index = {}; //발생 건수
    //afos_fid: 다발지역 FID, bjd_cd: 법정동 코드, spot_cd:지점코드
 
    //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 (err, api_res, api_body) {
                    //err 존재시 promise reject 호출
                    if (err) reject(err);

                    // api의 response이 있을경우 promise resolve 호출
                    if (api_res) {
                        console.log("calling weather api");
                        resolve(api_body);
                    }
                });
        })
    }
    //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`,
                //나중에 삭제 http://apis.data.go.kr/B552061/frequentzoneLg/getRestFrequentzoneLg?ServiceKey=XOLqI9tY4gVCdk1i2BoOM3EYmSUYqt8WlXUfIDSfD4RnNBrFwwthbNAtGOxoskP540JbUAzcnuABsydoE2U2dA%3D%3D&searchYearCd=2017&siDo=11&guGun=200&type=json&numOfRows=10&pageNo=1
                //나중에 삭제 http://apis.data.go.kr/B552061/frequentzoneLg/getRestFrequentzoneLg?ServiceKey=XOLqI9tY4gVCdk1i2BoOM3EYmSUYqt8WlXUfIDSfD4RnNBrFwwthbNAtGOxoskP540JbUAzcnuABsydoE2U2dA%3D%3D&searchYearCd=2018074&siDo=11&guGun=440&type=json&numOfRows=10&pageNo=1
                //sido = 11 gugun = 440 (마포구 합정동, 도화동, 도화동)
                //cate1 = frequentzoneLg
                //cate2 = getRestFrequentzoneLg
                //xml: true
                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);
                    }
                });
        })
    }

    const API_bundle = async () => {

        try {
            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
            /*DeathNum_index = await req_API_traffic("frequentzoneLg", "getRestFrequentzoneLg");
            M_HurtNum_index = await req_API_traffic("frequentzoneLg", "getRestFrequentzoneLg");
            L_HurtNum_index = await req_API_traffic("frequentzoneLg", "getRestFrequentzoneLg");
            LocalName_index = await req_API_traffic("frequentzoneLg", "getRestFrequentzoneLg");
            OccurNum_index = await req_API_traffic("frequentzoneLg", "getRestFrequentzoneLg");*/

            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].caslt_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 //발생건수
                //"spot_cd": "11440001"
            }
            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)
            );

            //이벤트 기반으로 일정 시간 간격으로 클라이언트에게 보낼 정보, 홈페이지 그래프에 나타날 정보
            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
            };
            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_and_traffic_Info_minutely_send_to_client", client_send); // 클라이언트에게 정보 담아서 이벤트 발산
            console.log("emit");

            //db에 저장
            sql = "INSERT INTO weatherInfo (time,wind,temperature,rain,prob) VALUES (?,?,?,?,?)";
            db.query(sql, [client_send.time, client_send.wind, client_send.temperature, client_send.rain, client_send.death], (err, result) => {
                if (err) console.log(err);
            })
          
            sql_traffic = "INSERT INTO trafficInfo (tdeath,mhurt,lhurt,occurence,prob) VALUES (?,?,?,?,?)";
            db_traffic.query(sql_traffic, [client_send.trafficdeath, client_send.mhurt, client_send.lhurt, client_send.occurence, client_send.death], (err, result) => {
                if (err) console.log(err);
            })
        } 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 해제
        })
    })

}