/* gpibInteract.c */
/***********************************************************************
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory, and the Regents of the University of
* California, as Operator of Los Alamos National Laboratory, and
* Berliner Elektronenspeicherring-Gesellschaft m.b.H. (BESSY).
* gpibCore is distributed subject to a Software License Agreement
* found in file LICENSE that is included with this distribution.
***********************************************************************/
/*      Author:                 John Winans
 *        Origional Author:	Ned D. Arnold
 *      Date:                   9/23/91
 * Modification Log:
 * -----------------
 * .01  06-19-91        nda     initial coding & debugging
 * .02  09-23-91        jrw     added proper multi-link support
 * .03  03-02-01        mrk	Converted to 3.14
 */
/* gpibInteract.c - allows interactive GPIB message transmissions  */
/*
 * gpibInteract allows the user to interactively set up gpib messages,
 * submit them to the gpibOwn Queue, and display the result. Five
 * gpib messages can be defined at a time.
 */
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <limits.h>
#include <math.h>
#include <epicsThread.h>
#include <epicsEvent.h>
#include <epicsTime.h>
#include <alarm.h>
#include <cvtTable.h>
#include <dbDefs.h>
#include <dbAccess.h>
#include <devSup.h>
#include <drvSup.h>
#include <link.h>
#include <dbCommon.h>
#include "drvGpibInterface.h"

#define GPIBREAD        1
#define GPIBWRITE       2
#define GPIBCMD         3
#define  MAX_MSG_LENGTH  80
#define  LIST_SIZE	10
int replyIsBack;	/* reset by sender, set by replyReceived() */
extern int ibDebug;
int GIDebug = 0;
int GITime = 60;	/* shell-setable DMA timeout value */
static int firstTime = 1;
static int hexmode = 1;
static epicsEventId msgReply;
typedef struct gpibIntCmd {
    struct dpvtGpibHead head;
    char type;	/* R, W, C for read, write, command */
    char cmd[MAX_MSG_LENGTH];	/* string to send to instrument */
    char resp[MAX_MSG_LENGTH + 32];	/* place for response if a GPIB read */
    long count;	/* used for counting */
    char busy;	/* used by timing routine */
}   gpibIntCmd;
static struct gpibIntCmd gpibIntCmds[LIST_SIZE];
static int gpibWork();
static int sendMsg();
static void configMsg();
static int getInt();
static int getChar();
static int getString();
static int showGpibMsg();
static int timingStudy();
static void hexDump(char *string, int indent);

int GI(void)
{
    unsigned char ans;
    int cnt;
    if (firstTime) {
	firstTime = 0;
	msgReply = epicsEventCreate(epicsEventEmpty);
	for (cnt = 0; cnt < LIST_SIZE; cnt++) {
	    memset(&gpibIntCmds[cnt], 0, sizeof(gpibIntCmd));
	    gpibIntCmds[cnt].head.workStart = gpibWork;
	    gpibIntCmds[cnt].head.secondary = -1;
	    gpibIntCmds[cnt].type = 'R';
	}
    }
    ans = 0;	/* set loop not to exit */
    printf("\n\n");
    while ((ans != 'q') && (ans != 'Q')) {
	printf("\n\nInteractive GPIB Program\n");
	printf("Select a function:\n");	/* main menu */
	printf("  'C' to configure send message content\n");
	printf("  'D' to display transmit & receive messages\n");
	printf("  'H' enable hex dump listings of message responses\n");
	printf("  'h' disable hex dump listings of message responses\n");
	printf("  'S' to send & receive a message one time\n");
	printf("  'T' to do timing on messages\n");
	printf("  'R' to turn on the GPIB debugging flag\n");
	printf("  'r' to turn off the GPIB debugging flag\n");
	printf("  'Q' to quit program\n");
	printf(">");
	ans = 0;	/* clear previous selection */
	getChar(&ans);
	switch (ans) {
	case 'c':	/* configure message contents */
	case 'C':
	    configMsg();
	    break;
	case 'h':	/* disable hex dumping */
	    hexmode = 0;
	    break;
	case 'H':	/* enable hex dumping */
	    hexmode = 1;
	    break;
	case 's':
	case 'S':	/* one shot: send one message */
	    sendMsg();
	    break;
	case 't':
	case 'T':	/* Timing analysis */
	    timingStudy();
	    break;
	case 'd':
	case 'D':	/* Display message contents */
	    for (cnt = 1; cnt < LIST_SIZE; cnt++)	/* for each message */
		showGpibMsg(cnt);
	    printf("\nHit CR to return to Main Menu ...");
	    getChar(&ans);	/* dummy input */
	    break;
	case 'q':
	case 'Q':	/* quit */
	    break;
	case 'r':	/* turn off ibDebug */
	    ibDebug = 0;
	    break;
	case 'R':	/* turn on ibDebug */
	    ibDebug = 1;
	    break;
	}	/* end case */
    }	/* end of while ans== q or Q  */
    return (0);
}/* end of main */

static int timingStudy(void)
{
    struct gpibIntCmd *pCmd[LIST_SIZE];
    int nCmds = 0;
    int inInt, reps;
    epicsTimeStamp startTime, endTime;
    double delta;
    int Reps, i, j;
    for (i = 0; i < LIST_SIZE; i++) {
	printf("\nEnter Message# to Send (1 thru 5, <cr> to terminate list) > ");
	if (!getInt(&inInt))
	    break;
	if (inInt <= 0 || inInt >= LIST_SIZE) {
	    printf("Illegal message number\n");
	    continue;
	}
	if (!gpibIntCmds[inInt].head.pibLink) {
	    printf("Message no properly configured\n");
	    continue;
	}
	pCmd[nCmds] = &gpibIntCmds[inInt];
	pCmd[nCmds]->busy = 0;	/* mark message 'not in queue' */
	pCmd[nCmds]->count = 0;
	++nCmds;
    }
    if (nCmds == 0)
	return (-1);
    printf("Enter the number of messages to send > ");
    if (!getInt(&inInt))
	return (0);	/* if no entry, return to main menu */
    if (inInt < 0)
	return (0);
    reps = inInt;
    Reps = reps;
    epicsTimeGetCurrent(&startTime);
    while (reps) {
	for (i = 0; i < nCmds && reps; i++) {
	    if (!(pCmd[i]->busy)) {
		pCmd[i]->count++;
		pCmd[i]->busy = 1;	/* mark the xact as busy */
		(*(drvGpib.qGpibReq)) (&pCmd[i]->head, IB_Q_LOW);
		reps--;
		if (reps % 10000 == 0) {
		    printf("Timing study in progress with %d messages left,"
			   "current status:\n",
			   reps);
		    for (j = 0; j < nCmds; j++) {
			if (pCmd[j] != NULL) {
			    printf("Message %d transmissions: %ld\n",
				   j, pCmd[j]->count);
			} else {
			    j = nCmds;
			}
		    }
		}
	    }
	}
	epicsEventMustWait(msgReply);
    }
    epicsTimeGetCurrent(&endTime);
    delta = epicsTimeDiffInSeconds(&endTime, &startTime);
    if (delta == 0.0)
	delta = 1.0;
    printf("Total transmit and receiving time = %f Sec\n", delta);
    printf("Messages/second = %f\n", Reps / delta);
    for (i = 0; i < nCmds; i++) {
	printf("Message %d transmissions: %ld\n", i, pCmd[i]->count);
    }
    return (0);
}

static int sendMsg(void)
{
    struct gpibIntCmd *pCmd;
    int inInt;	/* input integer from operator */
    int msgNum;	/* index to array of messages */
    double seconds;	/* # of seconds since message was sent */
    double maxSeconds = 8.0;	/* # of seconds to wait for reply  */
    printf("\nEnter Message # to Send (1 thru 5) > ");
    if (!getInt(&inInt))
	return (-1);	/* if no entry, return to main menu */
    if ((inInt >= LIST_SIZE) || (inInt < 0))
	return (-1);
    msgNum = inInt;
    pCmd = &gpibIntCmds[msgNum];	/* assign pointer to desired entry */
    if (!pCmd->head.pibLink) {
	printf("Message # not properly configured\n");
	return (0);
    }
    replyIsBack = FALSE;
    seconds = 0.0;
    (*(drvGpib.qGpibReq)) (&pCmd->head, IB_Q_LOW);	/* queue the msg */
    while (!replyIsBack && (seconds < maxSeconds)) {	/* wait for reply msg */
	epicsThreadSleep(.1);
	seconds += .1;
    }
    if (replyIsBack)
	showGpibMsg(msgNum);
    else
	printf("No Reply Received ...\n");
    return (0);
}

static int gpibWork(struct gpibIntCmd * pCmd)
{
    int status;
    if (GIDebug || ibDebug)
	printf("GI's gpibWork() was called for command >%s<\n", pCmd->cmd);
    switch (pCmd->type) {
    case 'w':
    case 'W':	/* write the message to the GPIB listen adrs */
	status = (*(drvGpib.write)) (pCmd->head.pibLink, pCmd->head.device,
		  pCmd->head.secondary, pCmd->cmd, strlen(pCmd->cmd), GITime);
	if (status <= 0)
	    strcpy(pCmd->resp, "GPIB error (while talking)");
	else
	    pCmd->resp[0] = '\0';
	break;
    case 'r':
    case 'R':	/* write the command string */
	status = (*(drvGpib.write)) (pCmd->head.pibLink, pCmd->head.device,
		  pCmd->head.secondary, pCmd->cmd, strlen(pCmd->cmd), GITime);
	if (status <= 0) {
	    strcpy(pCmd->resp, "GPIB error (while talking)");
	    break;
	}
	/* fall thru into the reading protion below */
    case 'I':
    case 'i':
	/* read the instrument  */
	pCmd->resp[0] = 0;	/* clear response string */
	status = (*(drvGpib.read)) (pCmd->head.pibLink, pCmd->head.device,
		pCmd->head.secondary, pCmd->resp, MAX_MSG_LENGTH, GITime, -1);
	if (status <= 0) {
	    strcat(pCmd->resp, "GPIB ERROR (while listening)");
	    break;
	} else if (status > (MAX_MSG_LENGTH - 1)) {	/* check length of resp */
	    printf("GPIB Response length equaled allocated space !!!\n");
	    pCmd->resp[(MAX_MSG_LENGTH)] = '\0';	/* place \0 at end */
	} else {
	    pCmd->resp[status] = '\0';	/* terminate response with \0 */
	}
	break;
    }
    pCmd->busy = 0;
    replyIsBack = TRUE;
    epicsEventSignal(msgReply);
    return (IDLE);
}

static void configMsg()
{
    struct gpibIntCmd *pCmd;
    int msgNum = 1;	/* index to array of messages */
    int inInt;	/* input integer from operator */
    int outInt;
    unsigned char inChar;	/* input char from operator  */
    char inString[MAX_MSG_LENGTH];	/* input string from operator */
    printf("\nEnter Message # to Configure (1 thru 5) [%d] > ", msgNum);
    if (getInt(&inInt)) {
	if ((inInt >= LIST_SIZE) || (inInt < 0))
	    return;
	msgNum = inInt;
    }
    pCmd = &gpibIntCmds[msgNum];	/* assign pointer to desired entry */
    printf("\n\n Configuring Send Message # %1.1d  .... \n", msgNum);
/* Prompt the Operator with the current value of each parameter If
 * only a <CR> is typed, keep current value, else replace value with
 * entered value
 */
    printf("\nenter Enter Link # [%2.2d] > ", (int) pCmd->head.link);
    if (getInt(&inInt))
	pCmd->head.link = inInt;
    if (pCmd->head.secondary == IBNOSECONDARY) {
	outInt = pCmd->head.device;
    } else {
	outInt = pCmd->head.device * 100 + pCmd->head.secondary;
    }
    printf("\nenter address # [%d] > ", outInt);
    if (getInt(&inInt)) {
	if (inInt >= 100) {
	    pCmd->head.device = inInt / 100;
	    pCmd->head.secondary = inInt % 100;
	} else {
	    pCmd->head.device = inInt;
	    pCmd->head.secondary = IBNOSECONDARY;
	}
    }
    pCmd->head.pibLink = (*(drvGpib.getLink))
	(pCmd->head.link, pCmd->head.device, pCmd->head.secondary);
    if (!pCmd->head.pibLink) {
	printf("link or device does not exist\n");
	return;
    }
    printf("\nenter command type R, W, C, I [%c] > ", pCmd->type);
    if (getChar(&inChar) == 1)
	pCmd->type = inChar;
    if ((pCmd->type != 'I') && (pCmd->type != 'i')) {
	if (hexmode) {
	    printf("\nEnter string to send (no quotes)\n");
	    hexDump(pCmd->cmd, 0);
	} else
	    printf("\nenter string to send (no quotes) [%.80s] > ", pCmd->cmd);
	if (getString(inString) == 1)
	    strcpy(pCmd->cmd, inString);
    } else {
	pCmd->cmd[0] = 0;
    }
    pCmd->resp[0] = 0;	/* clear response string */
    showGpibMsg(msgNum);
    return;
}

/*
 * getInt(pInt) ****************************************************
 *
 * gets a line of input from STDIN (10 characters MAX !!!!) scans the line
 * for an integer.
 *
 * Parm1 : pointer to an integer
 *
 * Returns (0) if a <CR> was hit, *pInt left unchanged. Returns (1) if a new
 * value was written to *pvalue. Returns (2) if a entry was not a valid
 * integer entry
 *
 */
static int getInt(pInt)
int *pInt;
{
    char input[10];
    int inval;
    gets(input);
    if (input[0] == 0)	/* if only a <CR> */
	return (0);
    else {
	if (sscanf(input, "%d", &inval) == 1) {	/* if a valid entry ... */
	    *pInt = inval;
	    return (1);
	} else	/* if not a valid entry, return 2 */
	    return (2);
    }
}
/*
 * int getChar(pChar) **************************************************
 *
 * gets a line of input from STDIN (10 characters MAX !!!!) scans the line
 * for a character entry
 *
 * Parm1 : pointer to an unsigned character variable
 *
 * Returns(0) if a <CR> was hit, *pChar left unchanged. Returns(1) if a new
 * value was written to *pChar
 *
 */
static int getChar(pChar)
unsigned char *pChar;
{
    char input[10];
    unsigned char inval;
    gets(input);
    if (input[0] == 0)
	return (0);
    else {
	sscanf(input, "%c", &inval);
	*pChar = inval;
	return (1);
    }
    
}
/*
 * int getString(pString) ****************************************************
 *
 * gets a line of input from STDIN (100 characters MAX !!!!) .
 * If length is greater than 0, copies string into pString
 *
 * Parm1 : pointer to an character string
 *
 * Returns : 0 if a <CR> was hit, *pvalue left unchanged Returns : 1 if a
 * new value was written to *pvalue
 *
 */
static int getString(pString)
char *pString;
{
    char input[100];
    char *in;
    gets(input);
    if (input[0] == 0)
	return (0);
    else {
	in = input;
	while (*in != '\0') {
	    switch (*in) {
	    case '\\':	/* meta-character parser */
		++in;
		switch (*in) {
		case 'n':	/* \n = linefeed */
		case 'l':	/* \l = lenefeed too */
		    *pString = '\n';
		    break;
		case 'r':	/* \r = cariage return */
		    *pString = '\r';
		    break;
		case 't':	/* \t = tab character */
		    *pString = '\t';
		    break;
		default:	/* anything else is just the character after
				 * the \ */
		    *pString = *in;
		}
		break;
	    default:
		*pString = *in;
	    }
	    ++in;
	    ++pString;
	}
	*pString = '\0';
	return (1);
    }
}

/* utility fuinction to print a hex dump */
static void hexDump(char *string, int indent)
{
    char ascBuf[17];	/* for the ascii xlation part of the dump */
    int strLength;
    unsigned int j, x;
    if (!(strLength = strlen(string))) {
	printf("(null)\n");
	return;
    }
    j = 0;
    while (j < strLength) {
	if (j) {
	    x = indent;
	    while (x--)
		printf(" ");
	}
	printf("%2.2X: ", j);	/* print relative address */
	x = 0;
	while ((x < 16) && (j < strLength)) {
	    printf("%2.2X ", string[j]);
	    ascBuf[x] = string[j];
	    if (!((ascBuf[x] >= 0x20) && (ascBuf[x] <= 0x7e)))
		ascBuf[x] = '.';
	    ++x;
	    ++j;
	}
	ascBuf[x] = '\0';
	while (x++ <= 16)
	    printf("   ");
	printf("  *%s*\n", ascBuf);
    }
    return;
}
/*
 * showGpibMsg(msgNum) ***********************************************
 *
 *
 * Print the gpib message contents onto the screen.
 * msgNum selects the message to be displayed
 *
 */
static int showGpibMsg(msgNum)
int msgNum;
{
    struct gpibIntCmd *pCmd = &gpibIntCmds[msgNum];
    printf("\nMessage #%1.1d : ", msgNum);
    printf("Link=%d primary address=%d secondary address=%d Type=%c\n",
	pCmd->head.link, pCmd->head.device, pCmd->head.secondary, pCmd->type);
    if (hexmode) {
	printf("Cmd: ");
	hexDump(pCmd->cmd, 5);
	printf("Res: ");
	hexDump(pCmd->resp, 5);
    } else {
	printf("             Command String  : %.40s\n", pCmd->cmd);
	printf("             Response String : %.40s\n", pCmd->resp);
    }
    return (0);
}
