/*
*========================================================================
*
*   SYSTEM   : NS_PLAZA_LANE APP
*   FILE     : lane_main.c
*   Progrmer : JBY (baarong@neighbor21.co.kr)
*   Date     : 2018.08.24
*   Modified :
*   Remark   : Type Change Low To High Trace
*
*========================================================================
*/

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/msg.h>
#include <unistd.h>
#include <sys/utsname.h>

#include "hi_ctrl.h"     /* 차로와 통신을 위한 HIGH-LEVEL CONTROL DEFINITION */
#include "lo_ctrl.h"     /* 차로와 통신을 위한 LOW -LEVEL CONTROL DEFINITION */
#include "dir_path.h"
#include "file_lb.h"
#include "func_list.h"
#include "snd_rcv_knd.h"
#include "lb_func_list.h"

FILE    *debug;
char    debug_d[128];
char    debug_f[128];

#define    _LINE_TEST_      1         /* 회선테스트 적용    */
#define    _LINE_TEST_SEC  60         /* 회선테스트 SEC    */
int line_check_flag = 0;         /* 1 Setting시 회선차단*/

#define    _LCL_ADDR_CHK    0          /* ADDRESS CHECK 적용     */
char lcl_addr[15+1];

#define    SAVE_BUFFER_CNT  4

int       conn_sock;                            /* SOCK Descriptor        */
u_char    r_buffer [4096];                      /* SOCK recv buffer       */
u_char    s_buffer [4096];                      /* SOCK send buffer       */
u_char    rs_buffer[4096 * SAVE_BUFFER_CNT];    /* SOCK recv save buffer  */
int       r_length;                             /* SOCK length to be read */
int       s_length;                             /* SOCK send length       */
int       rs_length=0;                          /* SOCK recv save length  */
#ifdef _ORACLE_ //기존 ORACLE 사용
struct    oracon *oracon;                       /* 오라클 상태관리 GLOBAL */
int       oraconn =  0;                         /* 오라클 상태관리 LOCAL  */
int       oracon_old =  1;                      /* 오라클 상태 전송유무    */
int       oracon_snd =  0;                      /* 오라클 상태 전송유무    */
int       db_errno;
#endif
void      SIG_term();                           /* signal handler  */
struct    hi_comm_ctrl    hi_comm_ctrl;         /* High-level Comm. Control Block */

int       f_errno;                              /* fcontrol lib 전용 Error 변수    */
int       f_recnum;

/*
 *========================================================================
 *
 *   Function : main()
 *   Progrmer : JBY (baarong@neighbor21.co.kr)
 *   Date     : 2018.08.24
 *   Modified :
 *   Remark   : 하위차로 제어 프로그램 MAIN PROCEDURE
 *
 *========================================================================
*/
int main(int argc, char *argv[])
{
    extern    int    optind,  opterr, optopt;
    extern    char    *optarg;
    struct    tm     *tm;
    struct rcv_lane_data  rd;
    int  options;
    int  rdflg, nResult;
    char plz_id[2];
    char date[8+1];
    char *p;
    FILE *pack_f = NULL;

    p = argv[0];
    signal(SIGHUP,  SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGTERM, SIG_term);
    memset(plz_id, 0x0, sizeof(plz_id));
    memset(date, 0x0, sizeof(date));
    memset(&hi_comm_ctrl, 0x00, sizeof(hi_comm_ctrl));

    opterr = 0;
    while((options = getopt(argc, argv, "l:p:")) != EOF)
    {
        switch (options)
        {
             case 'l': /* 차로번호    */
                  hi_comm_ctrl.lane_no = atoi(optarg);
                  break;
             case 'p': /* 영업소ID    */
                  sprintf(plz_id, "%.2s", optarg);
                  break;
             default:  break;
        }
    }
//lane_log("Lane[%d] Plz_id[%.2s]\n", hi_comm_ctrl.lane_no, plz_id);
    if (hi_comm_ctrl.lane_no == 0) /* lane_no : 차로번호 */
    {
        hi_comm_ctrl.lane_no = 99;
    }

    sprintf((char *)hi_comm_ctrl.lane_stat_info.lane_no, "%02d%c", hi_comm_ctrl.lane_no, '\0'); /* 차로번호 저장 */
    sprintf((char *)hi_comm_ctrl.write_info.lane_no, "%02d%c", hi_comm_ctrl.lane_no, '\0'); /* 차로번호 저장 */
    e_memcpy(hi_comm_ctrl.bas_tbl.plz_id, plz_id);
    e_memcpy(hi_comm_ctrl.write_info.plz_id, plz_id);
    e_memcpy(hi_comm_ctrl.lane_stat_info.plz_id, plz_id);

    lb_user_chg();        /* 사용자ID, GROUP ID를 */
                         /* TCS ID로 FIX         */
    hi_comm_ctrl.time = time(0);
    tm = localtime(&hi_comm_ctrl.time);
    memcpy(&hi_comm_ctrl.cday, tm, sizeof(struct tm));

    sprintf(date, "%04d%02d%02d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
    log_open(date, pack_f);
    lane_log( "=============== 차로통신 프로세스 START [%d/%d/%d %d:%d:%d] ===============\n", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
    packet_open(date, pack_f);
    Packet("======================= 패킷 START [%d/%d/%d %d:%d:%d] ===========================\n", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);

    if (init_comm() != 0) exit(-1);
    if (HPSS) lane_log("진입차로 시작\n");
    else      lane_log("진출차로 시작\n");

// 2019.07.04 lane_main에서 1번만 실행하도록 수정
    nResult = InitMessageSenderConfig(p);

    if (nResult != MQ_ERR_SUCCESS)
    {
        printf("Program terminated!!!\n");
        return -1;
    }

    hi_comm_ctrl.lane_stat_info.comm_stat = OFF_LINE_STAT; /* OFF-LINE        */
    lane_stat_info_save();                                 /* LANESTATINFO-F 변경     */

    lo_comm_ctrl.port_fd = -1;                             /* initial lcc fd */

    if(lane_comm_open(hi_comm_ctrl.device_nm) < 0)     /* 차로통신 개통    */
    {
       err_log("PORT를 사용할수 없습니다", 1);
       exit(-1);
    }

    while(1)
    {   /* 송수신 Data 종별  D_TBL_ORDER   0x24    13.Table 요구            */
        /*                   D_TBL_DATA    0xC2    14.Table Data            */
        if(hi_comm_ctrl.rcv_ctrl.cmd-NS_TBL_ORDER && hi_comm_ctrl.snd.cmd-NS_TBL_DATA)
        {
            lane_stat_info_save();
        }

        //if (getppid() <= 1) break;
        event_check();

        hi_comm_ctrl.time = time(0);
        tm = localtime(&hi_comm_ctrl.time);
        memcpy(&hi_comm_ctrl.cday, tm, sizeof(struct tm));

        if (hi_comm_ctrl.chk_event & CHK_LISTEN) lcc_accept();   /* APP 접속요구*/
        if (hi_comm_ctrl.chk_event & CHK_DATA) lcc_recv();       /* APP 명령    */

        rdflg = hi_comm_ctrl.chk_event & CHK_PORT;
        lane_comm_proc(rdflg, p);       /* 차로제어 SCHEDULE -------------------------------------------*/

        RemoveTerminatedThread();
#if _LINE_TEST_
        if(line_check_flag == 2) lo_comm_ctrl.stat_flag = L_ERR;  /* 2회선단절*/
#endif

        if(lo_comm_ctrl.stat_flag == L_ERR)
        {
            line_check_flag    = 0;
            hi_comm_ctrl.snd.len = 0;
            hi_comm_ctrl.now_stat_info &= H_ACC_RESND;
lane_log("Lane reconnect");
			RemoveTerminatedThread();
//lane_log("Lane reconnect 1");
            nResult = InitMessageSenderConfig(p);
            if(lane_comm_reopen(pack_f) < 0) break;
            continue;
        }

        lcc_check();                     /* 관리명령 처리 CHECK --------------------------------------------------*/
		
		//if(chk_warr_snd_no()) lane_comm_snd(); /* 전송연번 보증CHECK  */

#if _LINE_TEST_
        if(line_check_flag == 1)
        {
           lcp_ltest();     /* 1 회선테스트 전송 */
           lane_comm_snd();
        }
#endif
    }

    log_close();
    packet_close(pack_f);
    hi_comm_ctrl.lane_stat_info.comm_stat = OFF_LINE_STAT;  /* OFF-LINE          */
    lane_stat_info_save();                                  /* LANESTATINFO-F 변경       */
/*
    memset(&rd, 0x20, sizeof(struct rcv_lane_data));
    sprintf((char *)rd.plz_id, "%.2s", hi_comm_ctrl.write_info.plz_id);
    sprintf((char *)rd.wrk_datetm, "%04d%02d%02d%02d%02d%02d",
            hi_comm_ctrl.cday.tm_year+1900, hi_comm_ctrl.cday.tm_mon+1, hi_comm_ctrl.cday.tm_mday,
            hi_comm_ctrl.cday.tm_hour, hi_comm_ctrl.cday.tm_min, hi_comm_ctrl.cday.tm_sec);
    sprintf((char *)rd.lane_no, "%02d", hi_comm_ctrl.lane_no);
    sprintf((char *)rd.wrk_no, "%4s", hi_comm_ctrl.write_info.wrk_no);
    if(lb_data2atoi(hi_comm_ctrl.write_info.wrkr_no, 4) == 0)
        sprintf((char *)rd.wrkr_no, "9999");
    else
        sprintf((char *)rd.wrkr_no, "%.4s", hi_comm_ctrl.write_info.wrkr_no);
    rd.con_stat = OFF_LINE_STAT;
    sprintf((char *)rd.rcv_snd_no, "%08d", hi_comm_ctrl.write_info.rcv_snd_no);
    sprintf((char *)rd.st_snd_no, "%08d", hi_comm_ctrl.write_info.strt_snd_no);
    sprintf((char *)rd.ed_snd_no, "%08d", hi_comm_ctrl.write_info.end_snd_no);
    sprintf((char *)rd.buf, "00000000750099");
    MessageSender(NS_LANE_STAT, (char *)&rd, NULL, NULL);
*/
    exit(0);
}

/*
 *========================================================================
 *
 *   Function : event_check()
 *   Progrmer : JBY (baarong@neighbor21.co.kr)
 *   Date     : 2018.08.28
 *   Modified :
 *   Remark   : 차로측 통신 PORT 및 하위통신 제어 응용프로그램으로 부터
 *              DATA 입력 EVENT CHECK.(상위 및 차로측 수신감지)
 *
 *========================================================================
*/
int event_check()
{
    fd_set    rfds;
#if _LINE_TEST_
    struct    timeval    timeval;
#endif
    int       nfds,    nfound;
    int       i;

    nfds = 0;

    FD_ZERO(&rfds);

    for (i=0; i<2; i++)
    {
        if (hi_comm_ctrl.hi_comm_iffd[i] < 0) continue;
        FD_SET(hi_comm_ctrl.hi_comm_iffd[i], &rfds);
        if (nfds <= hi_comm_ctrl.hi_comm_iffd[i]) nfds = hi_comm_ctrl.hi_comm_iffd[i] + 1;
    }
    if (lo_comm_ctrl.port_fd >= 0)
    {
        FD_SET(lo_comm_ctrl.port_fd, &rfds);
        if (nfds <= lo_comm_ctrl.port_fd) nfds = lo_comm_ctrl.port_fd + 1;
    }
    hi_comm_ctrl.chk_event = 0;

    timeval.tv_sec  = _LINE_TEST_SEC;
    timeval.tv_usec = 0;

#if _LINE_TEST_
/* SELECT OPTION 0(EVENT 발생시까지 대기) */
    nfound = select(nfds, &rfds, NULL, NULL, &timeval);
#else
    nfound = select(nfds, &rfds, NULL, NULL, NULL);
#endif

    if (nfound <= 0)
    {
#if _LINE_TEST_
        /* 1 회선테스트 전송 2 회선단절 */
        if(!hi_comm_ctrl.chk_event)  line_check_flag++;
        else line_check_flag = 0;
#endif
        return (0);
    }

    if (hi_comm_ctrl.hi_comm_iffd[0] >= 0 && FD_ISSET(hi_comm_ctrl.hi_comm_iffd[0], &rfds))
    {
        hi_comm_ctrl.chk_event |= CHK_LISTEN;
    }
    
    if (hi_comm_ctrl.hi_comm_iffd[1] >= 0 && FD_ISSET(hi_comm_ctrl.hi_comm_iffd[1], &rfds))
    {
        hi_comm_ctrl.chk_event |= CHK_DATA;
    }
    
    if (lo_comm_ctrl.port_fd >= 0 && FD_ISSET(lo_comm_ctrl.port_fd, &rfds))
    {
        hi_comm_ctrl.chk_event |= CHK_PORT;
    }

#if _LINE_TEST_
    /* 1 회선테스트 전송 2 회선단절 */
    if(!hi_comm_ctrl.chk_event) line_check_flag++;
    else line_check_flag = 0;
#endif

    return (1);
}

/*
 *========================================================================
 *
 *   Function : cmp_seq_no()
 *   Progrmer : JBY (baarong@neighbor21.co.kr)
 *   Date     : 2018.08.28
 *   Modified :
 *   Remark   : 전송연번의 순서 CHECK
 *
 *========================================================================
*/
int cmp_seq_no(seq_no1, seq_no2)
unsigned short seq_no1;
unsigned short seq_no2;
{
    ushort    diff, half;

    half = 65535 / 2;                       /* ushort max/2  */

    if (seq_no1 > seq_no2)
    {
        diff = seq_no1 - seq_no2;               /* 전송연번의 차 */
        if (diff >= half) return (-1);      /* seq_no1 < seq_no2 */
        else return (1);                    /* seq_no1 > seq_no2 */
    }
    else if (seq_no1 < seq_no2)
    {
        diff = seq_no2 - seq_no1;
        if (diff >= half) return (1);       /* seq_no1 > seq_no2 */
        else return (-1);                   /* seq_no1 < seq_no2 */
    }
    else return (0);                        /* seq_no1 = seq_no2 */
}

 /*
 *========================================================================
 *
 *   함 수 명 : chk_warr_snd_no()
 *   작 성 자 : 오효섭 (catacath@nate.com)
 *   작 성 일 : 2012.10.04
 *   수 정 일 :
 *   설    명 : 보증전송연번 송신 여부 CHECK
 *
 *========================================================================
*/
chk_warr_snd_no()
{
    static time_t c_clock = 0;
    int    diff;
	struct tm *t;

    if (!(hi_comm_ctrl.now_stat_info & H_LANE_CLS))    /* IF 차로 폐쇄중 */
        return (0);
    if (hi_comm_ctrl.snd.len > 0 || hi_comm_ctrl.lane_stat_info.comm_stat != CONNECT_STAT) return (0);
    if (hi_comm_ctrl.lane_stat_info.lst_rev_snd_no == 0) return (0);
    if ((hi_comm_ctrl.time - c_clock) <= (2*60)) return (0);

    /*-------------------------------------------------------------------*/
    /* 최송수신 연번과 보증연번의 차이가 200이상이면  차로측에 보증해준다*/
    /*-------------------------------------------------------------------*/
    if (hi_comm_ctrl.lane_stat_info.lst_rev_snd_no > hi_comm_ctrl.lane_stat_info.warr_snd_no)
        diff = hi_comm_ctrl.lane_stat_info.lst_rev_snd_no - hi_comm_ctrl.lane_stat_info.warr_snd_no;
    else
        diff = hi_comm_ctrl.lane_stat_info.warr_snd_no - hi_comm_ctrl.lane_stat_info.lst_rev_snd_no;
    c_clock = hi_comm_ctrl.time;

    t = localtime(&c_clock);
	
    //if (diff < 200) return (0);

    //hi_comm_ctrl.warr_snd_no = hi_comm_ctrl.lane_stat_info.lst_rev_snd_no - 50;    /* 보증지시전송연번 */
    //hi_comm_ctrl.lane_stat_info.warr_cmd_snd_no = hi_comm_ctrl.warr_snd_no;
    //hi_comm_ctrl.snd.len = snd_warr_snd_no(0);
//lane_log("보증지시Chk [%02d : %02d : %02d]\n", t->tm_hour, t->tm_min, t->tm_sec);
	if( t->tm_hour == 23 && t->tm_min == 30 )
	{
//lane_log("보증지시 Go [%02d : %02d : %02d]\n", t->tm_hour, t->tm_min, t->tm_sec);
		hi_comm_ctrl.warr_snd_no = hi_comm_ctrl.lane_stat_info.lst_rev_snd_no - 50;    /* 보증지시전송연번 */
        hi_comm_ctrl.lane_stat_info.warr_cmd_snd_no = hi_comm_ctrl.warr_snd_no;
        hi_comm_ctrl.snd.len = snd_warr_snd_no(0);
	}

    return (hi_comm_ctrl.warr_snd_no);
}

/*
 *========================================================================
 *
 *   Function : init_comm()
 *   Progrmer : JBY (baarong@neighbor21.co.kr)
 *   Date     : 2018.08.24
 *   Modified :
 *   Remark   : 차로통신을 위한 정보의 초기화 처리
 *              1. 기초사항 READ & CHECK.
 *              2. 차로상태정보 READ & CHECK.
 *              3. 상위통신 관리 프로그램과 INTERFACE CHANNEL OPEN.
 *
 *========================================================================
*/
int init_comm()
{
    struct write_info wi;

    char   port[32];
    int  typ = 0;
    unsigned int max_lane;
#if _LCL_ADDR_CHK
    unsigned int i, j;
#endif
    int    isr;
    /* 1. 기초사항 READ, 차로번호 CHECK */
    e_memcpy(wi.plz_id, hi_comm_ctrl.write_info.plz_id);
//lane_log("PLZ_ID[%.2s]\n", wi.plz_id);
    isr = lb_freadx(&wi, FNO_BASICTBL, &hi_comm_ctrl.bas_tbl, FFIRST);
//lane_log("isr[%d]\n", isr);
    if (isr < 0){
        err_log("기초사항-F을 읽을 수 없습니다.", 1);
    }

    memset(port, 0x0, sizeof(port));

    max_lane = MAX_LANE;

    if (hi_comm_ctrl.lane_no > max_lane){
        err_log("입력 차로번호가 최대차로 보다 큽니다.", 1);
        return (-1);
    }
//lane_log("LANE NO[%d]\n", hi_comm_ctrl.lane_no);
#ifdef _LANETYPE_
    typ = hi_comm_ctrl.bas_tbl.lane_info[hi_comm_ctrl.lane_no-1].lane_type[0] - '0';
    if(typ == 1 || typ == 4) /* X=1:개방식상, 2:개방식하, 4:폐쇄식입구, 8:폐쇄식출구 */
        typ = 0;
    else typ = 1;
    hi_comm_ctrl.plz_typ = typ;            /* 개방식/폐쇄식*/
#endif
    hi_comm_ctrl.asc_des_div = lb_data2atoi(hi_comm_ctrl.bas_tbl.lane_info[hi_comm_ctrl.lane_no-1].io_div, 2);/* 상하행 구분 */
    e_memcpy(hi_comm_ctrl.device_nm, hi_comm_ctrl.bas_tbl.lane_info[hi_comm_ctrl.lane_no-1].port);
    hi_comm_ctrl.device_nm[sizeof(hi_comm_ctrl.bas_tbl.lane_info[hi_comm_ctrl.lane_no-1].port)] = '\0';
//lane_log("PORT NO[%.6s]\n", hi_comm_ctrl.bas_tbl.lane_info[hi_comm_ctrl.lane_no-1].port);
#if _LCL_ADDR_CHK
    memset(lcl_addr, 0x00, sizeof(lcl_addr));
    for(i=0, j=0; i< sizeof(hi_comm_ctrl.bas_tbl.lane_info[hi_comm_ctrl.lane_no-1].ip_addr); i++){
        if(hi_comm_ctrl.bas_tbl.lane_info[hi_comm_ctrl.lane_no-1].ip_addr[i] != ' '){
              lcl_addr[j] = hi_comm_ctrl.bas_tbl.lane_info[hi_comm_ctrl.lane_no-1].ip_addr[i];
              j++;
        }
    }
#else
    memset(lcl_addr, 0x00, sizeof(lcl_addr));
    memcpy(lcl_addr, "ANY_ADDRESS", strlen("ANY_ADDRESS"));
#endif

    //InitThread_Before();

    /* 2. 차로상태정보 READ & CHECK */
    lane_stat_info_read();

    if(lb_file_exist_chk(&wi, FNO_LANESTATINFO))
    {
        err_log("차로상태-F을 읽을 수 없습니다.", 1);
        return (-1);
    }
    if (hi_comm_ctrl.lane_stat_info.lst_rev_snd_no == 0)    /* 최종수신전송연번 = NONE */
    {
        /* 최종수신 연번 = NONE    */
        /* 차로정보 복구처리        */
        lane_stat_info_restore();
        lane_stat_info_save();
    }

    /* 3. 도메인(상위) 통신을 위한 SOCKET */
    if (lcc_open() != 0)
    {
        err_log("차로관리 명령 수신 채널 오픈 오류", 1);
        return (-1);
    }

#ifdef _ORACLE_ //기존 ORACLE 사용
    shm_id = shmget(TB_ORAC, sizeof(struct oracon), IPC_CREAT|0644);
    oracon = (struct oracon *)shmat(shm_id, 0, 0);
    if(oracon == NULL)
    {
        err_log("오라클 Connection 상태 SHM 오류", 1);
        return (-1);
    }

    db_connect();
    oracon->flag = oraconn;
#endif

    return (0);
}

/*
 *========================================================================
 *
 *   Function : SIG_term()
 *   Progrmer : JBY (baarong@neighbor21.co.kr)
 *   Date     : 2018.08.28
 *   Modified :
 *   Remark   : signal handler(SIGTERM)
 *
 *========================================================================
*/
void SIG_term()
{
    struct rcv_lane_data rd;

    hi_comm_ctrl.lane_stat_info.comm_stat = OFF_LINE_STAT;      /* OFF-LINE    */
    lane_stat_info_save();
/*
    memset(&rd, 0x20, sizeof(struct rcv_lane_data));
    sprintf((char *)rd.plz_id, "%.2s", hi_comm_ctrl.write_info.plz_id);
    sprintf((char *)rd.wrk_datetm, "%04d%02d%02d%02d%02d%02d",
            hi_comm_ctrl.cday.tm_year+1900, hi_comm_ctrl.cday.tm_mon+1, hi_comm_ctrl.cday.tm_mday,
            hi_comm_ctrl.cday.tm_hour, hi_comm_ctrl.cday.tm_min, hi_comm_ctrl.cday.tm_sec);
    sprintf((char *)rd.lane_no, "%02d", hi_comm_ctrl.lane_no);
    sprintf((char *)rd.lane_no, "%02d", hi_comm_ctrl.lane_no);
    sprintf((char *)rd.wrk_no, "%4s", hi_comm_ctrl.write_info.wrk_no);
    if(lb_data2atoi(hi_comm_ctrl.write_info.wrkr_no, 4) == 0)
        sprintf((char *)rd.wrkr_no, "9999");
    else
        sprintf((char *)rd.wrkr_no, "%.4s", hi_comm_ctrl.write_info.wrkr_no);
    rd.con_stat = OFF_LINE_STAT;
    sprintf((char *)rd.rcv_snd_no, "%08d", hi_comm_ctrl.write_info.rcv_snd_no);
    sprintf((char *)rd.st_snd_no, "%08d", hi_comm_ctrl.write_info.strt_snd_no);
    sprintf((char *)rd.ed_snd_no, "%08d", hi_comm_ctrl.write_info.end_snd_no);
    sprintf((char *)rd.buf, "000000008A000099");
    MessageSender(NS_LANE_STAT, (char *)&rd, NULL, NULL);
*/
    exit(0);
}
