/************************************************************
**
**     Object name : rasp-lcw-api
**
**     Public      : WsLcHandleOpen
**                   WsLcHandleClose
**                   
**                   WsLcADStart
**                   WsLcADStop
**
**                   WsLcDataRead
**
**
**     Description
**     -- -- -- -- -- -- -- --
**     RASP-LC無線版を利用するためのAPI
**
**
**     History
**     -- -- -- -- -- -- -- --
**     2012.11.07 K.Takagi 新規作成
**                         RASP-24をベースに移植
**	   2012.11.26 L.Okano  APIをファームに合わせて修正
**	   2012.12.05 L.Okano  RASP-24との重複を避けるためマクロおよびAPI名をLC用に変更
**						   WsControlClientを削除し、WsLcADStart及びWsLcADStop内部に組み込み
**     2012.12.06 K.Takagi ライブラリの統合
**                           ファイル名を変更
**                           librasp-lcw.hのインクルードを削除
**                           sendtoの引数の型違いを修正
**
**
**     Copyright (c) 2012
**     System In Frontier,Inc.
**     All rights reserved. 
**
**     当社の許可なしに複製および転載を禁ず
**
*/

/*=========================================================*/
/*               Special Macro Definition                  */
/*=========================================================*/

#ifdef WIN32

#pragma warning(disable:4996)

#endif

#undef DEBUG

/*=========================================================*/
/*                   Include Files                         */
/*=========================================================*/

#ifdef WIN32

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "WinSockVersion.h"

#ifdef _DEBUG
#define DEBUG
#endif

#else

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/types.h>
//#include <sys/io.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <sys/select.h>

#endif


/* 固有のヘッダファイル */
/*
#include "librasp-lcw.h"
*/

/*=========================================================*/
/*                   Macro Definition                      */
/*=========================================================*/
#define LC_LOW24BIT_MODE /* 32bit符号拡張モードで動作させる場合 */

#define LC_FRAME_SIZE			696
#define LC_SAMPLE_NUM			58
#define LC_CH_NUM				4
#define LC_LOCAL_BUF_SIZE		(LC_SAMPLE_NUM * sizeof(int) * LC_CH_NUM)

#define LC_CMD_STRING_LENGTH 32

#define WS_LC_SEND_PORT               8000
#define WS_LC_RECV_PORT               8001
#define WS_LC_CMD_CONTINUATION_AD_START  0x1
#define WS_LC_CMD_AD_STOP                0x2

#ifdef DEBUG

#define DEBUG_PRINT printf

#else

#define DEBUG_PRINT(...)  

#endif

#ifdef DEBUG_LOG

#define LOG_PRINT printf

#else

#define LOG_PRINT(...)  

#endif

/*=========================================================*/
/*                 Structure Declaration                   */
/*=========================================================*/

/*=========================================================*/
/*                    Private Valiable                     */
/*=========================================================*/
static char local_buf[LC_LOCAL_BUF_SIZE];

union data32
{
	unsigned char c[4];
	int val;
};

/*=========================================================*/
/*                    Public Valiable                      */
/*=========================================================*/

/*=========================================================*/
/*                   Private Functions                     */
/*=========================================================*/

/*----------------------------------------------------------*/
/*                          共通                            */
/*----------------------------------------------------------*/

/************************************************************
**
**  Public  : 
**
**  History : 2012.11.08 K.Takagi 新規作成
**			  2012.11.26 L.Okano  UDPソケット用に変更
**
**  Description
**  -----------
**  AD制御とデータ取得用のハンドルの作成
**
*/

WS_HANDLE_t *WsLcHandleOpen(char *ip_addr)
{
    int status;
    WS_HANDLE_t *h_ws;

#ifdef CONV_INET_ADDR_MODE
    in_addr_t server_ip_addr;
#else
    struct in_addr server_ip_addr;
#endif

#ifdef WIN32
    {
        WSADATA wsa_data;
        
        /* 初期化 */
        status = WSAStartup(MAKEWORD(WIN_SOCK_MAJOR_VERSION, WIN_SOCK_MINOR_VERSION), &wsa_data);
        if(status != 0) 
        {
            printf("WSAStartup Error. (%d)\n", status);
            return NULL;
        }
    }
#endif

    /* 制御サーバのIPアドレスを変換 */
#ifdef CONV_INET_ADDR_MODE
    server_ip_addr = inet_addr(ip_addr);
    if(server_ip_addr == -1)
    {
        printf("Server IP Address Error.\n");
        printf("  %s ??\n", ip_addr);
        goto HANDLE_OPEN_ERR1;
    }
#else
    status = inet_aton(ip_addr, &server_ip_addr);
    if(status == 0)
    {
        printf("Server IP Address Error.\n");
        printf("  %s ??\n", ip_addr);
        goto HANDLE_OPEN_ERR1;
    }
#endif

    /* ハンドル用のメモリを確保 */
    h_ws = (WS_HANDLE_t *)malloc(sizeof(WS_HANDLE_t));
    if(h_ws == NULL)
    {
        printf("Memory Allocate Error.\n");
        goto HANDLE_OPEN_ERR1;
    }
    memset(h_ws, 0, sizeof(WS_HANDLE_t));
    
    
    /* 制御用ソケットの作成 */
    h_ws->control_server_info.socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(h_ws->control_server_info.socket_fd == -1)
    {
        printf("Control Socket Create Error.\n");
        goto HANDLE_OPEN_ERR2;
    }
    
    /* 制御用ソケットの構造体を初期化 */
    h_ws->control_server_info.socket_addr_size = sizeof(struct sockaddr_in);
    h_ws->control_server_info.socket_addr.sin_family      = AF_INET;
	h_ws->control_server_info.socket_addr.sin_port        = htons(WS_LC_SEND_PORT);
	
#ifdef CONV_INET_ADDR_MODE
    h_ws->control_server_info.socket_addr.sin_addr.s_addr = server_ip_addr;
#else
    h_ws->control_server_info.socket_addr.sin_addr.s_addr = server_ip_addr.s_addr;
#endif

    
    /* データソケットの作成 */
    h_ws->data_server_info.socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(h_ws->data_server_info.socket_fd == -1)
    {
        printf("Data Socket Create Error.\n");
        goto HANDLE_OPEN_ERR2;
    }
 
    /* データソケットの構造体を初期化 */
    h_ws->data_server_info.socket_addr_size = sizeof(struct sockaddr_in);
    h_ws->data_server_info.socket_addr.sin_family      = AF_INET;
	h_ws->data_server_info.socket_addr.sin_port        = htons(WS_LC_RECV_PORT);
	h_ws->data_server_info.socket_addr.sin_addr.s_addr = INADDR_ANY;

	/* データサーバに接続 */
	status = bind(h_ws->data_server_info.socket_fd, (struct sockaddr *)&(h_ws->data_server_info.socket_addr),
			sizeof(h_ws->data_server_info.socket_addr));

	if(status == -1)
	{
	    printf("Data Socket Connection Error.\n");
	    goto HANDLE_OPEN_ERR3;
	}
    
    /* 正常終了 */
    return h_ws;
    
 HANDLE_OPEN_ERR3:
    close(h_ws->control_server_info.socket_fd);
	close(h_ws->data_server_info.socket_fd);
 
 HANDLE_OPEN_ERR2:
    free(h_ws);
    
 HANDLE_OPEN_ERR1:
    

#ifdef WIN32
    /* 終了処理 */
    WSACleanup();
#endif


    return NULL;
}


/************************************************************
**
**  Public  : 
**
**  History : 2012.11.07 K.Takagi 新規作成
**			  2012.11.28 L.Okano  RASP-LCに終了コマンドを送信する部分を削除
**
**  Description
**  -----------
**  サーバとの接続を終了しハンドルを削除
**
*/

int WsLcHandleClose(WS_HANDLE_t *h_ws)
{
    if(h_ws == NULL)
    {
        return 0;
    }
    
    /* AD制御用ソケットを閉じる */
    close(h_ws->control_server_info.socket_fd);
    
    /* ハンドル用のメモリを開放 */
    free(h_ws);

#ifdef WIN32
    /* 終了処理 */
    WSACleanup();
#endif

    return 0;
}

/************************************************************
**
**  Public  : 
**
**  History : 2012.11.07 K.Takagi 新規作成
**			  2012.11.28 L.Okano  符号拡張したフレームサイズを返すように修正
**
**  Description
**  -----------
**  1フレームあたりのバイト数を取得
**
*/

int WsLcGetFrameSize(WS_HANDLE_t *h_ws, int *frame_size)
{
    if(frame_size == NULL)
    {
        return -1;
    }
    *frame_size = LC_LOCAL_BUF_SIZE;

    return 0;
}

/************************************************************
**
**  Public  : 
**
**  History : 2010.10.18 K.Takagi 新規作成
**			  2012.12.06 L.Okano  コマンド送信部分を組み込み
**            2012.12.06 K.Takagi sendtoの引数の型違いを修正
**
**  Description
**  -----------
**  AD開始
**
*/

int WsLcADStart(WS_HANDLE_t *h_ws)
{
    int status;
	char buffer[LC_CMD_STRING_LENGTH];
    
    /* A/D開始のコマンドを送信 */
	sprintf(buffer, "%d", WS_LC_CMD_CONTINUATION_AD_START);
    DEBUG_PRINT("Send : %s\n", buffer);
    /*
	status = sendto(h_ws->control_server_info.socket_fd, buffer, strlen(buffer), 0, 
					(struct sockaddr_in *)&h_ws->control_server_info.socket_addr, 
					sizeof(h_ws->control_server_info.socket_addr));
    */
	status = sendto(h_ws->control_server_info.socket_fd, 
                    buffer,
                    strlen(buffer), 
                    0, 
					(struct sockaddr *)&h_ws->control_server_info.socket_addr, 
					sizeof(struct sockaddr) );
	DEBUG_PRINT("Status : 0x%08x\n", status);
   
    return status;
}


/************************************************************
**
**  Public  : 
**
**  History : 2010.10.18 K.Takagi 新規作成
**			  2012.12.06 L.Okano  コマンド送信部分を組み込み
**            2012.12.06 K.Takagi sendtoの引数の型違いを修正
**
**  Description
**  -----------
**  AD停止
**
*/

int WsLcADStop(WS_HANDLE_t *h_ws)
{
    int status;
    int last_status;
    
    int  io_stat;
#ifdef WIN32
    u_long rest_size;
#else
    int  rest_size;
#endif
    int  read_size;
    char dummy[1024];
	char buffer[LC_CMD_STRING_LENGTH];
    
    /* A/D停止のコマンドを送信 */
    last_status = 0;
	sprintf(buffer, "%d", WS_LC_CMD_AD_STOP);
    DEBUG_PRINT("Send : %s\n", buffer);
    /*
	status = sendto(h_ws->control_server_info.socket_fd, buffer, strlen(buffer), 0, 
					(struct sockaddr_in *)&h_ws->control_server_info.socket_addr, 
					sizeof(h_ws->control_server_info.socket_addr));
    */
	status = sendto(h_ws->control_server_info.socket_fd, buffer, strlen(buffer), 0, 
					(struct sockaddr *)&h_ws->control_server_info.socket_addr, 
					sizeof(struct sockaddr));
	DEBUG_PRINT("Status : 0x%08x\n", status);
    
    /* データ破棄 */
    io_stat = ioctl(h_ws->data_server_info.socket_fd, FIONREAD, &rest_size);
    while((io_stat != -1) && (rest_size > 0))
    {
        read_size = read(h_ws->data_server_info.socket_fd, dummy, 1024);
        
        if(read_size < 0)
        {
            break;
        }
        
        io_stat = ioctl(h_ws->data_server_info.socket_fd, FIONREAD, &rest_size);
    }
    
    return last_status;
}

/************************************************************
**
**  Public  : 
**
**  History : 2012.11.08 K.Takagi 新規作成
**			  2012.11.27 L.Okano  ファームの仕様に併せて変更
**								  ヘッダのチェック＆除去の部分を削除
**
**  Description
**  -----------
**  データ取得関数
**  
**
**
*/

int WsLcDataRead(WS_HANDLE_t *h_ws, char *buffer, size_t read_req_size)
{
    int read_size;
    int offset;
	union data32 sample_data;
	int *user_buf;
	char *ptr;
    int i;

    LOG_PRINT("Called %s.\n", __func__);

    /* 転送サイズ分のデータを取得 */
    offset = 0;
    read_req_size = LC_FRAME_SIZE;
    while(read_req_size > 0)
    {
		/* サーバーからデータを取得 */
		read_size = recv(h_ws->data_server_info.socket_fd, &local_buf[offset], read_req_size, 0);
		
        /* エラー判定 */
        if(read_size < 0)
        {
            break;
        }
        
        read_req_size -= read_size;
        offset += read_size;
    }
    
    /* エラー */
    if(read_req_size > 0)
    {
		printf("read_req_size : 0x%08zx\n", read_req_size);
		return read_size;
    }

	/* 24bit -> 32bit データ変換 */
	ptr = local_buf;
	user_buf = (int *)buffer;

	for(i = 0; i < (LC_SAMPLE_NUM*LC_CH_NUM); i++)
	{
		sample_data.val = 0;

		sample_data.c[1] = *ptr++;
		sample_data.c[2] = *ptr++;
		sample_data.c[3] = *ptr++;
#ifdef LC_LOW24BIT_MODE
		user_buf[i] = sample_data.val >> 8; /* 32bit中の下位24bitにデータを格納（32bit符号拡張動作） */
#else
		user_buf[i] = sample_data.val; /* 32bit中の上位24bitにデータを格納（下位8bitは0x00） */
#endif
	}
  
	return LC_LOCAL_BUF_SIZE;
}

/* EOF */

