#define	    IDENT	"1D02"
/*
** +--------------------------------------------------------------+
** |                  Paul Scherrer Institute                     |
** |                     SINQ Division				  |
** |                                                              |
** | This software may be used freely by non-profit organizations.|
** | It may be copied provided that the name of P.S.I. and of the |
** | author is included. Neither P.S.I. nor the author assume any |
** | responsibility for the use of this software outside of P.S.I.|
** +--------------------------------------------------------------+
**
** Module Name  . . . . . . . . : [...SinqHM]SinqHM_srv_main.c
**
** Author   . . . . . . . . . . : D. Maden
** Date of creation . . . . . . : Oct 1996
**
** Updates:
**  1A01  1-Oct-1996 DM	    Initial version.
**  1B01 16-Jul-1997 DM	    Flush fibre-optic FIFO on SQHM_ZERO and SQHM_WRITE.
**  1C01 24-Jun-1998 DM	    Add TOF support.
**  1C06 10-Aug-1999 DM	    Add instrument dependent conditional code.
**---------------------------------------------------------------------------
**  SinqHM_srv_main.c is the main program of a SINQ Histogram Memory process
**  which will run in a front-end processor and perform the necessary
**  operations for a SINQ multi-detector.
**
**  Communication between SinqHM_srv and the client is via TCP/IP protocol.
**
**  To compile and link this program for VxWorks on PSS123, use:
**
	    ccvx '-DINST="<instr>"' SinqHM_srv_main.c
	    ccvx '-DINST="<instr>"' SinqHM_srv_routines.c
	    ccvx '-DINST="<instr>"' SinqHM_srv_filler.c
	    ccvx '-DINST="<instr>"' SinqHM_srv_server.c
** where
	    ccvx = ccppc -O0			\
		    -mcpu=603			\
		    -I${WIND_BASE}/target/h	\
		    -fno-builtin		\
		    -fno-for-scope		\
		    -nostdinc			\
		    -DCPU=PPC603		\
		    -D_GNU_TOOL			\
		    -gdwarf -c -Wimplicit
** and
**	    <instr> = DMC, SANS, FOCUS, HRPT, TRICS or POLDI

    ldppc -o SinqHM_srv.o -r SinqHM_srv_main.o	    \
			     SinqHM_srv_routines.o  \
			     SinqHM_srv_filler.o    \
			     SinqHM_srv_server.o

**  and, on the target,
**
**   -> ld < /home/pss123/aco/maden/wind/SinqHM/SinqHM_srv.o
**   -> taskSpawn "SinqHM_srv", 100, 8, 20000, SinqHM_srv, <port>
**							   ^
**							   |
**				      default = 2400 ------+
**
**====================================================================
*/
#include    "vxWorks.h"
#include    "taskLib.h"
#include    "msgQLib.h"
#include    "sockLib.h"

#include    <stdlib.h>
#include    <stdio.h>
#include    <string.h>
#include    <unistd.h>
#include    <errno.h>
#include    <netinet/in.h>
#include    <arpa/inet.h>
#include    <sys/types.h>
#include    <sys/socket.h>
#include    <setjmp.h>
#include    <time.h>

#include    <signal.h>		/* Get POSIX signal definitions. */
#include    <timers.h>
#include    <sys/mman.h>	/* Get POSIX mem mgmnt definitions. */
#include    <sys/wait.h>	/* Get POSIX wait definitions. */
/*
**====================	Global Definitions =====================================
*/
#include    "SinqHM_def.h"
#include    "SinqHM_gbl.h"
/*

**=============================================================================
**  Local routines.
**
**    None
*/
/*==============================================================================

**  Main line program
**  ------------------
**============================================================================*/
  int SinqHM_srv (int port, int use_level_gen, char *base_addr,
		  int arg4, int arg5, int arg6, int arg7, int arg8, int arg9,
		  int suspend) {
/*============================================================================*/
    struct req_buff_struct	    req_buff;
    struct rply_buff_struct	    rply_buff;

    int			i, j, is, status, cntr;
    int			keep_going = OK;
    int			bytes_to_come, n_bytes_got;
    char		*p_addr;
    int			cl_port;
    int			do_send;
    int			cnct_skt;	/* Connection socket */
    int			rw_skt;		/* Read/write socket */
    struct sockaddr_in	lcl_sockname;
    struct sockaddr_in	rmt_sockname;
    int			rmt_sockname_len;
    int			pkt_size;
    time_t		timer;

    sigset_t		proc_sigmask;
    struct sigaction	sig_action;

/*============================================================================
*/
    if (suspend != 0) {
      printf ("\nSinqHM_srv: Suspending ...\n");
      taskSuspend (0);	    /* We've been told to suspend ourself, ..
			    **  .. presumably for debug reasons.
			    */
      printf ("\nSinqHM_srv: Suspension ended!\n");
    }
/*============================================================================
**	Perform the initialisation of global variables ...
*/
    StrJoin (Instr_name, sizeof (Instr_name), INST, "");     /* Save the instr.
							     ** name in a global
							     */
    StrJoin (Def_ident, sizeof (Def_ident), SINQHM_DEF_ID, "");
    StrJoin (Sqhm_main_ident, sizeof (Sqhm_main_ident), IDENT, "");
    StrJoin (Sqhm_main_date,  sizeof (Sqhm_main_date),  __DATE__, ", ");
    StrJoin (Sqhm_main_date,  sizeof (Sqhm_main_date),  Sqhm_main_date, __TIME__);
    SinqHM_server_setup_id ();
    SinqHM_filler_setup_id ();
    SinqHM_routines_setup_id ();
    time (&Sqhm_start_time);	/* Initialise the start time */

    StrJoin (Tsk_name[0], sizeof (Tsk_name[0]), taskName (get_pid ()), "");

    Dbg_mask = 0;
    Dbg_lev0 = 0;
    Dbg_lev1 = 1;
    Dbg_lev2 = 0;
    Dbg_lev3 = 0;
    Cfgn_done = 0;	  /* Force a configuration before we can do anything. */

    free_HM_memory (NULL);  /* Release memory (possibly) in case ..
			    ** .. of a restart of the program */
    Parent_pid = get_pid ();
    Filler_pid    = 0;
    for (i = 0; i <= MAX_CLIENTS; i++) {
      Child_pid[i] = 0;
      Child_exit_status[i] = 0;
    }

    if (Sem_Filler == NULL) {
      Sem_Filler = semBCreate (SEM_Q_FIFO, SEM_FULL);
    }

    if (Sem_Server == NULL) {
      Sem_Server = semBCreate (SEM_Q_FIFO,SEM_FULL);
    }
/*
**	Debug hint - set Use_sem_tmo to zero to disable semaphore time-outs
**		     during debug sessions. This will stop FILLER falling off.
*/
    Sem_tmo = 10;	/* 10 second time-out by default */
    Use_sem_tmo = 0;

    setupLevelGenerator (use_level_gen, base_addr);
/*============================================================================
**	Clear the process signal mask to let in all signals (actually, on
**	PPC VxWorks this seems to cause all signals to get ignored!) and then
**	declare action routine for SIGUSR1. Our children signal with SIGUSR1
**	just before they exit in a more or less controlled manner. If the
**	child gets deleted from the shell, it will not signal.
*/
    Cnt_SIGUSR1 = 0;

    status = sigemptyset (&proc_sigmask);
    status = sigprocmask (SIG_SETMASK, &proc_sigmask, NULL);
    status = sigemptyset (&sig_action.sa_mask);
    sig_action.sa_handler = catch_signal;
    sig_action.sa_flags   = 0;

    if (sigaction (SIGUSR1, &sig_action, NULL) == -1) {
      getErrno (&My_errno);
      perror ("sigaction error:");
      exit (0);
    }
/*============================================================================
**		Get a port number to use. If the argument "port" is not
**		a valid port number (not in range 128 to 32767), use PORT_BASE.
*/
    Port_base = PORT_BASE;
    if ((port >= 128) && (port < 32768)) Port_base = port;
/*============================================================================
**	Create a TCP/IP socket for receiving connection requests, bind it and
**	set it to listen.
*/
    cnct_skt = socket (AF_INET, SOCK_STREAM, 0);
    if (cnct_skt == -1)
      failInet_port ("\nhmRoot%d -- Cnct-Socket socket error", Port_base);
    status = 1;
    setsockopt(cnct_skt, SOL_SOCKET, SO_REUSEADDR, &status, sizeof(int));

    lcl_sockname.sin_family = AF_INET;
    lcl_sockname.sin_port   = htons (Port_base);
    lcl_sockname.sin_addr.s_addr = 0;
    status = bind (cnct_skt, (struct sockaddr *) &lcl_sockname,
					  sizeof (lcl_sockname));
    if (status != 0) failInet_port ("\nhmRoot%d -- Cnct-Socket bind error",
					    Port_base);
    status = listen (cnct_skt, 5);
    if (status != 0) failInet_port ("\nhmRoot%d -- Cnct-Socket listen error",
					    Port_base);
/*
**	    Clients must now connect to the Connect-port and send either a
**	    connect, configure, deconfigure, status or exit request. A
**	    successful configure request must precede any connection request.
**
**	    When a configure request arrives, the histogram data structures are
**	    initialised, the histogram filler process (FILLER) is spawned and
**	    connection requests are enabled. A status reply is sent to the
**	    client and the connection closed.
**
**	    When a connection request arrives, a child will be spawned to
**	    handle the connection. The child will be told which port to use
**	    and he will release a semaphore when the port is ready. The parent
**	    then sends the client a reply to tell him which port to use. The
**	    connection is then closed to await further connections.
*/
    setup_inet_info (Inet_host, sizeof (Inet_host), /* Find out who we are */
		     Inet_addr, sizeof (Inet_addr));
    printf ("\n%s: Program  Ident = \"%s\". Started on %s (%s).\n",
		    Tsk_name[0], IDENT, Inet_host, Inet_addr);
    printf ("%s: Protocol Ident = \"%s\"\n", Tsk_name[0], SINQHM_DEF_ID);

#ifndef INST
    #error  Define the target instrument via the symbol "INST"!
#endif

    if (strcmp (INST, "DMC") == 0) {
      printf ("%s: Instrument = \"%s\"\n", Tsk_name[0], "DMC");
      Lwl_hdr_daq_mask = LWL_HDR_NRL_MASK;
      Lwl_hdr_daq_soll = 0;
    }else if (strcmp (INST, "SANS") == 0) {
      printf ("%s: Instrument = \"%s\"\n", Tsk_name[0], "SANS");
      Lwl_hdr_daq_mask = LWL_HDR_NRL_MASK;
      Lwl_hdr_daq_soll = 0;
    }else if (strcmp (INST, "FOCUS") == 0) {
      /*
      Lwl_hdr_daq_mask = LWL_HDR_NRL_MASK | LWL_HDR_SYNC2_MASK;
      Lwl_hdr_daq_soll = 0;
      */
      Lwl_hdr_daq_mask = 0x00f00000;
      Lwl_hdr_daq_soll = 0x00200000;
      printf ("%s: Instrument = \"%s\"\n", Tsk_name[0], "FOCUS");
    }else if (strcmp (INST, "HRPT") == 0) {
      printf ("%s: Instrument = \"%s\"\n", Tsk_name[0], "HRPT");
      Lwl_hdr_daq_mask = LWL_HDR_SYNC0_MASK | LWL_HDR_SYNC2_MASK;
      Lwl_hdr_daq_soll = LWL_HDR_SYNC2_MASK;
    }else if (strcmp (INST, "TRICS") == 0) {
      printf ("%s: Instrument = \"%s\"\n", Tsk_name[0], "TriCS");
      /*
      Lwl_hdr_daq_mask = LWL_HDR_NRL_MASK | LWL_PSD_PWF;
      Lwl_hdr_daq_soll = 0;
      */
      Lwl_hdr_daq_mask = 0x00f00000;
      Lwl_hdr_daq_soll = 0x00e00000;
    }else if (strcmp(INST,"AMOR") == 0) {
      printf ("%s: Instrument = \"%s\"\n", Tsk_name[0], "AMOR");
      Lwl_hdr_daq_mask = LWL_HDR_NRL_MASK | LWL_PSD_PWF;
      Lwl_hdr_daq_soll = 0;
      /*
      Lwl_hdr_daq_mask = 0x00f00000;
      Lwl_hdr_daq_soll = 0x00600000;
      */
    }else if (strcmp (INST, "POLDI") == 0) {
      printf ("%s: Instrument = \"%s\"\n", Tsk_name[0], "POLDI");
      Lwl_hdr_daq_mask = LWL_HDR_NRL_MASK;
      Lwl_hdr_daq_soll = 0;
      printf ("%s: Warning -- Lwl_hdr_daq_mask and Lwl_hdr_daq_soll are "
		    "provisional!\n", Tsk_name[0]);
    }else {
      printf ("%s: Instrument is not defined\n", Tsk_name[0]);
      Lwl_hdr_daq_mask = LWL_HDR_NRL_MASK;
      Lwl_hdr_daq_soll = 0;
    }
    printf ("%s: Lwl_hdr_daq_mask = 0x%08x", Tsk_name[0], Lwl_hdr_daq_mask);
    if ((Lwl_hdr_daq_mask & LWL_HDR_NRL_MASK) != 0) printf (" NRL");
    if ((Lwl_hdr_daq_mask & LWL_HDR_SYNC3_MASK) != 0) printf (" SYNC3");
    if ((Lwl_hdr_daq_mask & LWL_HDR_SYNC2_MASK) != 0) printf (" SYNC2");
    if ((Lwl_hdr_daq_mask & LWL_HDR_SYNC1_MASK) != 0) printf (" SYNC1");
    if ((Lwl_hdr_daq_mask & LWL_HDR_SYNC0_MASK) != 0) printf (" SYNC0");
    printf ("\n%s: Lwl_hdr_daq_soll = 0x%08x", Tsk_name[0], Lwl_hdr_daq_soll);
    if ((Lwl_hdr_daq_soll & LWL_HDR_NRL_MASK) != 0) printf (" NRL");
    if ((Lwl_hdr_daq_soll & LWL_HDR_SYNC3_MASK) != 0) printf (" SYNC3");
    if ((Lwl_hdr_daq_soll & LWL_HDR_SYNC2_MASK) != 0) printf (" SYNC2");
    if ((Lwl_hdr_daq_soll & LWL_HDR_SYNC1_MASK) != 0) printf (" SYNC1");
    if ((Lwl_hdr_daq_soll & LWL_HDR_SYNC0_MASK) != 0) printf (" SYNC0");
    printf ("\n");

    while (keep_going == OK) {
      rmt_sockname_len = sizeof (rmt_sockname);
      rw_skt = accept (cnct_skt, (struct sockaddr *) &rmt_sockname,
						&rmt_sockname_len);
      if (rw_skt == -1) {
        getErrno (&My_errno);
	failInet_port ("\nhmRoot%d -- Cnct-Socket accept error", Port_base);
	/* No return */
      }
      Rw_bytes_got = 0; Rw_bytes_put = 0;

      p_addr = inet_ntoa (rmt_sockname.sin_addr);
      cl_port = ntohs (rmt_sockname.sin_port);
      printf ("\n%s: Connected to Host %s, Port %d.\n",
				Tsk_name[0], p_addr, cl_port);

      bytes_to_come = sizeof (req_buff);   /* Read a command */
      p_addr = (char *) &req_buff;
      while (bytes_to_come > 0) {
	n_bytes_got = recv (rw_skt, p_addr, bytes_to_come, 0);
	if (n_bytes_got <= 0) break;
	Rw_bytes_got += n_bytes_got;
	bytes_to_come -= n_bytes_got;
	p_addr += n_bytes_got;
      }

      if (Cnt_SIGUSR1 > 0) {	    /* Have any children exited? */
	if (Dbg_lev0) printf ("hmRoot%d: Cleaning up children ...", Port_base);
        cleanup_children ();	    /* Yes, update our database. */
	if (Dbg_lev0) printf ("\n  ... cleanup of children done.\n");
      }

      if (n_bytes_got < 0) {
	printf ("\007"
		"%s -- R/W-Socket recv error\n", Tsk_name[0]);
	printf ("%s -- Connection to %s will be closed.\n", Tsk_name[0],
			    inet_ntoa (rmt_sockname.sin_addr));
      }else if (n_bytes_got == 0) {
	printf ("\007"
		"%s -- no command received!\n", Tsk_name[0]);
	printf ("%s -- Connection to %s will be closed.\n", Tsk_name[0],
			    inet_ntoa (rmt_sockname.sin_addr));
      }else if (n_bytes_got != sizeof (req_buff)) {
	printf ("\007"
		"%s -- command packet has wrong number of bytes:"
		" %d instead of %d\n", Tsk_name[0],
		n_bytes_got, sizeof (req_buff));
	printf ("%s -- Connection to %s will be closed.\n", Tsk_name[0],
			    inet_ntoa (rmt_sockname.sin_addr));
      }else if (ntohl (req_buff.bigend) != 0x12345678) {
	printf ("\007"
		"%s -- big-endian/little-endian problem!\n"
		"   Buffer received in non-network byte order!\n", Tsk_name[0]);
      }else {
	do_command (0, rw_skt, 4096, &req_buff, &rply_buff);
      }
      close (rw_skt);
      if (ntohl (req_buff.cmnd) == SQHM_EXIT) keep_going = !OK;
    }
    close (cnct_skt);
    exit (KER__SUCCESS);
  }
/*========================================= End of SinqHM_srv_main.c ========*/
