// file: rs485.c
// serial port communiction
// rs485
// tc400 communication protocol
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <string.h>
#include <time.h>

#define FALSE  -1
#define TRUE   0

#define uint16 unsigned short
#define uint8 unsigned char

const uint16 genpoly16 = 0x8408;

// crc table
const uint16 crc16_256_table[256] =
{
    0x0000,0x1189,0x2312,0x329B,0x4624,0x57AD,0x6536,0x74BF,
    0x8C48,0x9DC1,0xAF5A,0xBED3,0xCA6C,0xDBE5,0xE97E,0xF8F7,
    0x1081,0x0108,0x3393,0x221A,0x56A5,0x472C,0x75B7,0x643E,
    0x9CC9,0x8D40,0xBFDB,0xAE52,0xDAED,0xCB64,0xF9FF,0xE876,
    0x2102,0x308B,0x0210,0x1399,0x6726,0x76AF,0x4434,0x55BD,
    0xAD4A,0xBCC3,0x8E58,0x9FD1,0xEB6E,0xFAE7,0xC87C,0xD9F5,
    0x3183,0x200A,0x1291,0x0318,0x77A7,0x662E,0x54B5,0x453C,
    0xBDCB,0xAC42,0x9ED9,0x8F50,0xFBEF,0xEA66,0xD8FD,0xC974,
    0x4204,0x538D,0x6116,0x709F,0x0420,0x15A9,0x2732,0x36BB,
    0xCE4C,0xDFC5,0xED5E,0xFCD7,0x8868,0x99E1,0xAB7A,0xBAF3,
    0x5285,0x430C,0x7197,0x601E,0x14A1,0x0528,0x37B3,0x263A,
    0xDECD,0xCF44,0xFDDF,0xEC56,0x98E9,0x8960,0xBBFB,0xAA72,
    0x6306,0x728F,0x4014,0x519D,0x2522,0x34AB,0x0630,0x17B9,
    0xEF4E,0xFEC7,0xCC5C,0xDDD5,0xA96A,0xB8E3,0x8A78,0x9BF1,
    0x7387,0x620E,0x5095,0x411C,0x35A3,0x242A,0x16B1,0x0738,
    0xFFCF,0xEE46,0xDCDD,0xCD54,0xB9EB,0xA862,0x9AF9,0x8B70,
    0x8408,0x9581,0xA71A,0xB693,0xC22C,0xD3A5,0xE13E,0xF0B7,
    0x0840,0x19C9,0x2B52,0x3ADB,0x4E64,0x5FED,0x6D76,0x7CFF,
    0x9489,0x8500,0xB79B,0xA612,0xD2AD,0xC324,0xF1BF,0xE036,
    0x18C1,0x0948,0x3BD3,0x2A5A,0x5EE5,0x4F6C,0x7DF7,0x6C7E,
    0xA50A,0xB483,0x8618,0x9791,0xE32E,0xF2A7,0xC03C,0xD1B5,
    0x2942,0x38CB,0x0A50,0x1BD9,0x6F66,0x7EEF,0x4C74,0x5DFD,
    0xB58B,0xA402,0x9699,0x8710,0xF3AF,0xE226,0xD0BD,0xC134,
    0x39C3,0x284A,0x1AD1,0x0B58,0x7FE7,0x6E6E,0x5CF5,0x4D7C,
    0xC60C,0xD785,0xE51E,0xF497,0x8028,0x91A1,0xA33A,0xB2B3,
    0x4A44,0x5BCD,0x6956,0x78DF,0x0C60,0x1DE9,0x2F72,0x3EFB,
    0xD68D,0xC704,0xF59F,0xE416,0x90A9,0x8120,0xB3BB,0xA232,
    0x5AC5,0x4B4C,0x79D7,0x685E,0x1CE1,0x0D68,0x3FF3,0x2E7A,
    0xE70E,0xF687,0xC41C,0xD595,0xA12A,0xB0A3,0x8238,0x93B1,
    0x6B46,0x7ACF,0x4854,0x59DD,0x2D62,0x3CEB,0x0E70,0x1FF9,
    0xF78F,0xE606,0xD49D,0xC514,0xB1AB,0xA022,0x92B9,0x8330,
    0x7BC7,0x6A4E,0x58D5,0x495C,0x3DE3,0x2C6A,0x1EF1,0x0F78
};

// LSB-first transmission, the genpoly need be inversed.
// CRC-16 CCITT
// X^16 + X^12 + X^5 + 1

uint16 cal_crc(uint8 *data, int data_len, uint16 init_val_of_crc)
{
    uint16 crc = init_val_of_crc;

    while (data_len-- >0)
    {
        crc ^= *data ++;
        crc = (crc >> 8) ^ crc16_256_table[crc & 255];

    }

    return crc;
}


int open_dev(char *Dev)
{
    int    fd = open( Dev, O_RDWR); 
    if (-1 == fd)    
    {             
        perror("Can't Open Serial Port");
        return -1;        
    }
    if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
    {
         printf("Unable set to NONBLOCK mode");
        return -1;
    }
    return fd;
}

// set serial speed
int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300, \
                    B38400, B19200, B9600, B4800, B2400, B1200, B300, };
int name_arr[] = { 38400, 19200, 9600, 4800, 2400, 1200, 300, 38400, \
                   19200, 9600, 4800, 2400, 1200, 300, };
void set_speed(int fd, int speed)
{
    int i; 
    int status; 
    struct termios Opt;
    
    tcgetattr(fd, &Opt); 
    for ( i=0; i<sizeof(speed_arr)/sizeof(int); i++) 
    { 
        if (speed == name_arr[i]) 
        {     
            tcflush(fd, TCIOFLUSH);     
            cfsetispeed(&Opt, speed_arr[i]);  
            cfsetospeed(&Opt, speed_arr[i]);   
            status = tcsetattr(fd, TCSANOW, &Opt);  
            if (status != 0) 
            {        
                perror("tcsetattr fd1");  
                return;     
            }    
            tcflush(fd,TCIOFLUSH);   
        }  
    }
}

// set serial port data, stop and crc bit.
int set_parity(int fd, int databits, int stopbits, int parity)
{ 
    struct termios options; 
    if ( tcgetattr(fd, &options) != 0)
    { 
        perror("SetupSerial 1");     
        return(FALSE);  
    }
    options.c_cflag &= ~CSIZE; 
    
    //  data bit
    switch (databits)
    {   
        case 7:        
            options.c_cflag |= CS7; 
        break;
        case 8:     
            options.c_cflag |= CS8;
        break;   
        default:    
            fprintf(stderr,"Unsupported data size\n");
        return (FALSE);  
    }
    
    switch (parity) 
    {   
        case 'n':
        case 'N':    
            options.c_cflag &= ~PARENB;   // Clear parity enable
            options.c_iflag &= ~INPCK;    // Enable parity checking 
            options.c_lflag &= 0;  // add comment it's very important to set this parameter.
            options.c_oflag  &= ~OPOST;   /*Output*/
        break;  
        
        case 'o':   
        case 'O':     
            options.c_cflag |= (PARODD | PARENB); // set odd parity  
            options.c_iflag |= INPCK;             // disnable parity checking 
        break;  
        
        case 'e':  
        case 'E':   
            options.c_cflag |= PARENB;     // enable parity     
            options.c_cflag &= ~PARODD;    // change into even     
            options.c_iflag |= INPCK;      // disnable parity checking
        break;
        
        case 'S': 
        case 's':  // as no parity
            options.c_cflag &= ~PARENB;
            options.c_cflag &= ~CSTOPB;break;  
        
        default:   
            fprintf(stderr,"Unsupported parity\n");    
        return (FALSE);  
    }  
    
    // set stop bit  
    switch (stopbits)
    {   
        case 1:    
            options.c_cflag &= ~CSTOPB;  
        break;  
        case 2:    
            options.c_cflag |= CSTOPB;  
        break;
        default:    
            fprintf(stderr,"Unsupported stop bits\n");  
        return (FALSE); 
    } 
    // Set input parity option
    if (parity != 'n')   
        options.c_iflag |= INPCK; 
        tcflush(fd,TCIFLUSH);
        options.c_cc[VTIME] = 150; // set time out for 15 seconds   
        options.c_cc[VMIN] = 0;       // update the options and do it now
    if (tcsetattr(fd,TCSANOW,&options) != 0)   
    { 
        perror("SetupSerial 3");   
        return (FALSE);  
    } 
    return (TRUE);  
}

void config_s(int fd)
{
        struct termios oldtio, newtio;
       
        tcgetattr(fd, &oldtio);
        memset(&newtio, 0, sizeof(newtio));
       
        newtio.c_cflag = B9600 | CS8 | CLOCAL | CREAD | CSTOPB;
        newtio.c_cflag &= ~CRTSCTS;
        newtio.c_iflag = IGNPAR | ICRNL;
        newtio.c_iflag &= ~( IXON | IXOFF | IXANY );
        newtio.c_oflag = 0;
        newtio.c_lflag = ICANON;
       
        tcflush(fd, TCIFLUSH);
        tcsetattr(fd, TCSANOW, &newtio);
}



int main(int argc, char **argv)
{
    
    int nread;
    int nwrite;
    
    uint8 rev_buf[512];
    uint8 send_buf[512];
    uint8 user_name[11];

    uint8 rev_tmp[39];

    int send_len;
    int rev_len;

    uint16 send_crc;
    uint16 rev_crc;
    uint16 tmp_crc;

    int i;
    int num;
    int tmp_num;
    time_t seconds;

    FILE *fp = 0;

    int fd;
    char *dev  = "/dev/ttyS0"; 

    // open serial port and set parameters
    
    
    fd = open_dev(dev);
    set_speed(fd,9600);

    config_s(fd);    // it's so important that we must face this.
    
    if (set_parity(fd,8,1,'N') == FALSE)  
    {
        printf("set parity error\n");
        exit (0);
    }
    else 
    {
        printf("set serial ok!\n");
    }
    // -------------------------------------------


    // send cmd common part
    // stx
    send_buf[0] = 0xa5;

    // ch (machine id)
    send_buf[1] = 0x00;
    send_buf[2] = 0x00;
    send_buf[3] = 0x00;
    send_buf[4] = 0x00;

    
    while(1)
    {

        // print menu

        printf("0. exit!\n");
        
        printf("1. get counts.\n");
        printf("2. get date/time.\n");
        printf("3. get device network info.\n");

        printf("4. get person info.\n");
        printf("5. get records.\n");
        printf("6. get fp template.\n");

        printf("7. get configure.\n");

        printf("\nplsease enter num: ");
        scanf("%d", &num);
//        num = 3;


        switch(num)
        {

            // exit
            case 0:
                printf("exiting ..\n");
                close(fd);
                return 0;
            break;

            // get the num of the account
            case 1: 
                // send cmd 0x3c
                // send cmd
                send_buf[5] = 0x3c;

                // send length
                send_buf[6] = 0x00;
                send_buf[7] = 0x00;

                // send_crc
                send_crc = cal_crc(send_buf, 8, -1);
                send_buf[8] = (uint8)(send_crc & 0xFF);
                send_buf[9] = (uint8)(send_crc >> 8);

                // send and rev data

                // send data
                send_len = write(fd, send_buf, 10);
                printf("send len: %d\n", send_len);
                sleep(2);
                // receive data
                if((rev_len=read(fd, rev_buf, 512)) == -1)
                {
                    printf("read error!\n");
                }
                else
                {
                    printf("rev len: %d\n", rev_len);
                    rev_crc = (rev_buf[rev_len-1]*256 + rev_buf[rev_len-2]);
                    tmp_crc = cal_crc(rev_buf, (rev_len-2), -1);

                    if (rev_crc==tmp_crc && rev_buf[6]==0)
                    {
                        printf("ack successful!\n");
                        printf("ack information, as follows:\n");
                        printf("-------------------------------------\n");
                        printf("     staffs: %d\n", ( rev_buf[9]*256*256 + \
                                                      rev_buf[10]*256 + \
                                                      rev_buf[11] ) );
                        printf("         fp: %d\n", ( rev_buf[12]*256*256 + \
                                                      rev_buf[13]*256 + \
                                                      rev_buf[14] ) );
                        printf("    records: %d\n", ( rev_buf[21]*256*256 + \
                                                      rev_buf[22]*256 + \
                                                      rev_buf[23] ) );
                        printf("-------------------------------------\n");
                    }
                    else
                    {
                        printf("ack failed!\n");
                    }

                    printf("\n");
                }

            break;

            
            // get data/time
            case 2:
                // send cmd 0x38
                // send cmd
                send_buf[5] = 0x38;

                // send length
                send_buf[6] = 0x00;
                send_buf[7] = 0x00;

                // send_crc
                send_crc = cal_crc(send_buf, 8, -1);
                send_buf[8] = (uint8)(send_crc & 0xFF);
                send_buf[9] = (uint8)(send_crc >> 8);

                // send and rev data

                // send data
                send_len = write(fd, send_buf, 10);

                printf("send len: %d\n", send_len);
                sleep(3);
                // receive data
                if((rev_len = read(fd, rev_buf, 512))==-1)
                {
                    printf("read error!\n");
                }
                else
                {
                    printf("rev len: %d\n", rev_len);
                    rev_crc = (rev_buf[rev_len-1]*256 + rev_buf[rev_len-2]);
                    tmp_crc = cal_crc(rev_buf, (rev_len - 2), -1);

                    if (rev_crc==tmp_crc && rev_buf[6]==0)
                    {
                        printf("ack successful!\n");
                        printf("ack information, as follows:\n");
                        printf("-------------------------------------\n");
                        // date/time
                        printf("     date/time: 20%d/%02d/%02d \n                  %02d:%02d:%02d\n", \
                                    rev_buf[9] , rev_buf[10], rev_buf[11], \
                                    rev_buf[12], rev_buf[13], rev_buf[14]);
                        printf("-------------------------------------\n");
                    }
                    else
                    {
                        printf("ack failed!\n");
                    }

                    printf("\n");
                }

            break;


            // get device net info
            case 3: 
                // send cmd 0x3a
                // send cmd
                send_buf[5] = 0x3a;

                // send length
                send_buf[6] = 0x00;
                send_buf[7] = 0x00;

                // send_crc
                send_crc = cal_crc(send_buf, 8, -1);
                send_buf[8] = (uint8)(send_crc & 0xFF);
                send_buf[9] = (uint8)(send_crc >> 8);

                // send and rev data

                // send data
                send_len = write(fd, send_buf, 10);

                printf("send len: %d\n", send_len);
                sleep(3);
                // receive data

                if((rev_len=read(fd, rev_tmp, 512)) == -1)
                {
                    printf("read error!\n");
                }
                else
                {
                    printf("rev len: %d\n", rev_len);
                    rev_crc = (rev_tmp[rev_len-1]*256 + rev_tmp[rev_len-2]);
                    tmp_crc = cal_crc(rev_tmp, (rev_len-2), -1);

                    if (rev_crc==tmp_crc && rev_buf[6]==0)
                    {
                        printf("ack successful!\n");
                        printf("ack information, as follows:\n");
                        printf("-------------------------------------\n");
                        // ip addr
                        printf("          ip: %d.%d.%d.%d\n", \
                                    rev_tmp[9] ,rev_tmp[10], \
                                    rev_tmp[11], rev_tmp[12]);
                        // network mask
                        printf("network mask: %d.%d.%d.%d\n", \
                                    rev_tmp[13] ,rev_tmp[14], \
                                    rev_tmp[15], rev_tmp[16]);
                        // mac addr
                        printf("         mac: %02x:%02x:%02x:%02x:%02x:%02x\n", \
                                    rev_tmp[17] ,rev_tmp[18], rev_tmp[19],\
                                    rev_tmp[20], rev_tmp[21], rev_tmp[22]);
                        // gateway
                        printf("     gateway: %d.%d.%d.%d\n", \
                                    rev_tmp[23] ,rev_tmp[24], \
                                    rev_tmp[25], rev_tmp[26]);
                        // server addr
                        printf("      server: %d.%d.%d.%d\n", \
                                    rev_tmp[27] ,rev_tmp[28], \
                                    rev_tmp[29], rev_tmp[30]);
                        // limit
                        printf("       limit: %d\n", rev_tmp[31]);
                        // port
                        printf("        port: %d\n", rev_tmp[32]*256 + rev_tmp[33]);
                        // mode
                        printf("        mode: %d\n", rev_tmp[34]);
                        // dhcp
                        printf("        dhcp: %d\n", rev_tmp[35]);
                        printf("-------------------------------------\n");

                    }
                    else
                    {
                        printf("ack failed!\n");
                    }

                    printf("\n");
                }

            break;


            // get person info.
            case 4:

                // send cmd 0x42
                // send cmd
                send_buf[5] = 0x42;

                // send length
                send_buf[6] = 0x00;
                send_buf[7] = 0x02;

                // data
                send_buf[8] = 0x01;
                send_buf[9] = 0x0c;

                // send_crc
                send_crc = cal_crc(send_buf, 10, -1);
                send_buf[10] = (uint8)(send_crc & 0xFF);
                send_buf[11] = (uint8)(send_crc >> 8);

                // send and rev data

                // send data
                send_len = write(fd, send_buf, 12);

                printf("send len: %d\n", send_len);
                sleep(1);

                // receive data
                if((rev_len=read(fd, rev_buf, 512)) == -1)
                {
                    printf("read error!\n");
                }
                else
                {
                    printf("rev len: %d\n", rev_len);
                    rev_crc = (rev_buf[rev_len-1]*256 + rev_buf[rev_len-2]);
                    tmp_crc = cal_crc(rev_buf, (rev_len-2), -1);

                    if (rev_crc==tmp_crc && rev_buf[6]==0)
                    {
                        printf("ack successful!\n");
                        printf("ack information, as follows:\n");
                        tmp_num = rev_buf[9];
                        printf("-------------------------------------\n");
                        printf("=====================\n");
                        for(i=0; i<tmp_num; i++)
                        {
                            // user no
                            printf("  user no: %d%d%d%d%d\n", \
                                rev_buf[i*27+10], rev_buf[i*27+11], \
                                rev_buf[i*27+12], rev_buf[i*27+13], \
                                rev_buf[i*27+14] );

                            // passwd
                            if((rev_buf[i*27+16]*256 + rev_buf[i*27+17]) != 0)
                            {
                                printf("   passwd: \n");
                            }
                            else
                            {
                                printf("   passwd: %d\n", (rev_buf[i*27+16]*256 + \
                                                            rev_buf[i*27+17]) );
                            }

                            // card no
                            printf("  card no: %d\n", (rev_buf[i*27+18]*256*256 + \
                                                        rev_buf[i*27+19]*256 + rev_buf[i*27+20]));

                            // name
                            strncpy(user_name, &rev_buf[i*27+21], 10);
                            user_name[10] = '\0';
                            printf("     name: %s\n", user_name);

                            // dept
                            printf("     dept: %d\n", rev_buf[i*27+30]);
                            
                            // group
                            printf("    group: %d\n", rev_buf[i*27+31]);

                            // mode
                            printf("     mode: %d\n", rev_buf[i*27+32]);

                            printf("=====================\n");
                        }
                        printf("-------------------------------------\n");

                    }
                    else
                    {
                        printf("ack failed!\n");
                    }

                    printf("\n");
                }

                break;

                // get records
            case 5:
                // send cmd 0x40
                // send cmd
                send_buf[5] = 0x40;

                // send length
                send_buf[6] = 0x00;
                send_buf[7] = 0x02;

                // data
                send_buf[8] = 0x01;
                send_buf[9] = 0x19;

                // send_crc
                send_crc = cal_crc(send_buf, 10, -1);
                send_buf[10] = (uint8)(send_crc & 0xFF);
                send_buf[11] = (uint8)(send_crc >> 8);

                // send and rev data
                // send data
                send_len = write(fd, send_buf, 12);

                printf("send len: %d\n", send_len);
                sleep(1);
                // receive data
                if((rev_len=read(fd, rev_buf, 512)) == -1)
                {
                    printf("read error!\n");
                }
                else
                {
                    printf("rev len: %d\n", rev_len);
                    rev_crc = (rev_buf[rev_len-1]*256 + rev_buf[rev_len-2]);
                    tmp_crc = cal_crc(rev_buf, (rev_len-2), -1);

                    if (rev_crc==tmp_crc && rev_buf[6]==0)
                    {
                        printf("ack successful!\n");
                        printf("ack information, as follows:\n");
                        tmp_num = rev_buf[9];
                        printf("-------------------------------------\n");
                        printf("=====================\n");
                        for(i=0; i<tmp_num; i++)
                        {

                            printf("                    %d\n", i+1);
                            // user no
                            printf("    user no: %d%d%d%d%d\n", \
                                rev_buf[i*14+10], rev_buf[i*14+11],\
                                rev_buf[i*14+12], rev_buf[i*14+13], \
                                rev_buf[i*14+14] );

                            // date/time
                            seconds = rev_buf[i*14+15]*256*256*256 + \
                                      rev_buf[i*14+16]*256*256 + \
                                      rev_buf[i*14+17]*256 + \
                                      rev_buf[i*14+18];
                            printf("  date/time: %d/%02d/%02d \n               %02d:%02d:%02d\n",\
                                gmtime(&seconds)->tm_year+1930, gmtime(&seconds)->tm_mon+1, \
                                gmtime(&seconds)->tm_mday+1, gmtime(&seconds)->tm_hour, \
                                gmtime(&seconds)->tm_min, gmtime(&seconds)->tm_sec);

                            // bac no
                            printf("     bac no: %d\n", rev_buf[i*14+19]);

                            // record type
                            printf("record type: %d\n", rev_buf[i*14+20]);
                            printf("=====================\n");
                        }
                        printf("-------------------------------------\n");

                    }
                    else
                    {
                        printf("ack failed!\n");
                    }

                    printf("\n");
                }

                break;

            // get fp template
            case 6: 

                // send cmd 0x44
                // send cmd
                send_buf[5] = 0x44;

                // send length
                send_buf[6] = 0x00;
                send_buf[7] = 0x06;

                // data
                 send_buf[8] = 0x00;
                 send_buf[9] = 0x00;
                send_buf[10] = 0x00;
                send_buf[11] = 0x00;
                send_buf[12] = 0x01;

                send_buf[13] = 0x01;

                // send_crc
                send_crc = cal_crc(send_buf, 14, -1);
                send_buf[14] = (uint8)(send_crc & 0xFF);
                send_buf[15] = (uint8)(send_crc >> 8);

                // send and rev data

                // send data
                send_len = write(fd, send_buf, 16);

                printf("send len: %d\n", send_len);
                sleep(2);
                // receive data
                if((rev_len=read(fd, rev_buf, 512)) == -1)
                {
                    printf("read error!\n");
                }
                else
                {
                    printf("rev len: %d\n", rev_len);
                    rev_crc = (rev_buf[rev_len-1]*256 + rev_buf[rev_len-2]);
                    tmp_crc = cal_crc(rev_buf, (rev_len-2), -1);

                    if (rev_crc==tmp_crc && rev_buf[6]==0)
                    {
                        printf("ack successful!\n");
                        printf("ack information, as follows:\n");
                        printf("-------------------------------------\n");
                        fp = fopen("template/1_1.anv", "wb");
                        printf("    writing fp template ..\n");
                        fwrite(&rev_buf[9], 1, 338, fp);
                        fclose(fp);
                        printf("-------------------------------------\n");

                    }
                    else
                    {
                        printf("ack failed!\n");
                    }

                    printf("\n");
                }

                break;
           
            default:
                printf("error num!\n");
            break;

        }
    }
    close(fd);  
    return 0;
}
