/************************************************************************/
/* Hahn-Meitner-Institut Berlin                                         */
/* Bereich Strukturforschung                                            */
/* Abteilung NI / BENSC                                                 */
/* Glienicker Strasse 100                                               */
/* D-14109 Berlin (Wannsee)                                             */
/* Software:K. Emmelmann, Tel.(030)8062-2588, Mail: klaus@dvwbs5.hmi.de */
/************************************************************************/
/* Datei: SEL.C								*/
/* Teil von: KWS.C							*/
/* Anwendung bei: V4 - KWS                                              */
/* Titel: Treiberprogramm fuer Geschwindigkeitsselektor DORNIER         */
/* Version: 1.0         vom: 30.1.96                                    */
/************************************************************************/
/*
Legende
=======

30.1.96 Emmelmann   Implementierung
1.2.96 Emmelmann   Statusdekodierung eingebaut
27.2.96 Emmelmann   Strom wird auch im Status geliefert


Das Handbuch sagt Empfangsterminator ist Linefeed.


Jeder Befehl erzeugt ein Echo; argumentlose Befehle werden im Echo
zusaetzlich mit einer Zahl versehen, deren Bedeutung unbekannt ist.


FEHLERMELDUNGEN, die bekannt sind:
****************************************

	1. Solldrehzahl zu klein !
	2. Solldrehzahl zu gross !
	3. derzeit keine Ausfuehrung moeglich !
	4. Solldrehzahl in einem kritischen Bereich
		- return to m
	5. HOCHFAHREN 1
	6. 

zu 3:	Abfrage der Istgeschwindigkeit ist waehrend des Hochfahrens aus dem 
	Stillstand nur ueber Lesen des Gesamtstatus moeglich 
zu 4:	es gibt verbotene Geschwindigkeitsbereiche
zu 5:	beim Uebergang von Stop in den gestarteten Zustand

Zeitliches Protokollverhalten
-----------------------------
	Wird ein Befehl bearbeitet, werden alle weiteren ueber die serielle
	Schnittstelle einlaufenden Befehle ignoriert -  auch nicht gepuffert !

	Max. Befahlsrate: 1 Befehl / 5 Sekunden

Geraete-Verhalten
-----------------
Der Startbefehl faehrt den Selektor auf die Minimalgeschwindigkeit (3100U/min)

Das Protokoll: fuer jeden Befehl erzeugt der Selektor-PC ein Echo !

Host	->	Selektor
------------------------
REM (disabled)	Remote-Betrieb einschalten
		Rueckmeldung: ENABLED / Host nicht betriebsbereit!
		
TTY (disabled)	Local-Betrieb einschalten
		Rueckmeldung: DISABLED

???		Sende Status (Status ist nicht fest und geht von der
		Interpretation der Kennworte aus !)

TIM (disabled)	Setzen des Zeitintervalles zur Time-Out-Ueberwachung des PCs
		????

SST		Start des Selektors und Ausregeln der Drehzahl (Nur wenn der
		Selektor steht !); liefert als Rueckmeldung die Solldrehzahl

SDR nnnnn	Vorgabe einer Soll-Drehzahl und geregelten Betrieb einschalten.
		3100 => nnnnn <=28300 U/min; liegt der Wert in einem kritischen
		Bereich (geht aus Setup-Menue hervor), kommt eine entsprechende
		Rueckmeldung

FDR		Sende die Solldrehzahl (Format 'RPM n')

STB		Leerlauf ohne Regelung

BRE		Beginne eine Verlustleistungsmessung - ist bei Statusabruf
		lesbar, wenn dort die Betriebsart regeln erscheint !

HAL		Bremse Selektor bis zum Stillstand ab (unterhalb 3000 U/min.)

*/     
#include stdio.h
#include descrip.h
#include iodef.h
#include errno.h
#include lnmdef.h

#define FALSE 0
#define TRUE !FALSE

#define OK	0L
#define NOT_OK !OK

#define	LIMIT_U	3100	/* niedrigste Drehzahl */ 
#define	LIMIT_O	28300

/* Verbotene Drehzahlbereiche (Resonanzgefahr)	*/

#define RESONANZ_1U	3648
#define RESONANZ_1O	4552
#define RESONANZ_2U	5248
#define RESONANZ_2O	6449
#define RESONANZ_3U	9148
#define RESONANZ_3O	9949

#define TOK_BUF_L 25

#define SELE_LUN	0

extern FILE *fs;

static short chan_s=6;

/* *********************************************************************** */
/* ------------------------------------------------------------------------ */
long select_setup()
{
register rc;
$DESCRIPTOR(dev_descr, "TXA1:");
chan_s=SELE_LUN;
if ((rc=sys$assign(&dev_descr, &chan_s, NULL,NULL)) != 1)
	{printf("Error in Selector_SETUP: Ret = %d\n",rc); return(NOT_OK);}
return(OK);
}

/* ------------------------------------------------------------------------ */
/* Function liest ueberprueft das Befehlsecho		*/ 
long sele_test_echo(cmd, lng)
char *cmd[]; 
int lng;
{
char echo[80];
if (sele_read(&echo)!= OK) return(NOT_OK);
if (strncmp(&echo, cmd, lng) != 0)
	{printf("Selector:protocol error%s\n",echo); return(NOT_OK);}
	else
	return(OK);
}	
/* ------------------------------------------------------------------------ */
/* Function prueft die Anzahl der sich im Typeahead-Buffer befindenden Bytes
	und liefert das naechste dort anstehende Zeichen, jedoch ohne es 
	aus dem Puffer zu entnehmen.
*/
long tst_buf_s(bt)
int *bt;
{
int bytes;
register rc;
struct {unsigned short status, bytes;
	int pid; } iosb;
if ((rc=sys$qiow(NULL,chan_s,IO$_SENSEMODE | IO$M_TYPEAHDCNT,&iosb,
	NULL, NULL, &bytes, NULL, NULL, NULL, NULL, NULL)) != 1)
	{print_error(fs,"Error in QIOW"); return(NOT_OK);}
/**ch = (bytes >> 16) & 0xff;*/
*bt = bytes & 0xffff;
return(OK);
}

/* ------------------------------------------------------------------------ */
long sele_read(message)
char *message;          
{
char char_l[8], *ptr;
register rc;
	struct {unsigned short status, bytes;
		int pid; } iosb;
ptr = message;
for (;;)
	{
	if ((rc=sys$qiow(NULL,chan_s,IO$_READVBLK | IO$M_NOECHO,
	&iosb,NULL,
	NULL, char_l, 1, NULL, NULL, NULL, NULL)) != 1)
		{printf("Error in QIOW, READ_MESS: %d\n", rc);return(NOT_OK);}
	*ptr = char_l[0]; 
	/* Terminator (linefeed) ? */
	ptr++;
	if (char_l[0] == '\n') break;
	}
return(OK);
}
/* ------------------------------------------------------------------------ */
/* Pruefe ob der Selektor eine Fehlermeldung erzeugt hat und gib sie aus */

long select_error_msg(err)
long *err;
{
char chr[10], msg[80];
float waiti=3.0;	/* Selektor PC hat 3 Sekunden Reaktionszeit !! */

/* Warte ein wenig - es koennte auf den Befehl hin eine Fehlermeldung erscheinen */
lib$wait(&waiti);

/* Es koennen auch mehrere Zeilen sein */
*err = FALSE;
for (;;)
	{if (tst_buf_s(&chr) != OK) return(NOT_OK);
	if (chr[0] != NULL)
		{
		if (sele_read(&msg) != OK) return(NOT_OK);
		printf("Selektor failure:");
		printf("%s\n\7",msg);
		*err = TRUE;
		}
	else
		return(OK);
	}
}
/* ------------------------------------------------------------------------ */
long sele_write(message)
char message[];
{
char msg[80];
long err;
register rc;
struct
	{
	unsigned short status, bytes;
	int pid; } iosb;

if (select_error_msg(&err) != OK) return(NOT_OK);
if (err == TRUE) return(NOT_OK);

if ((rc=sys$qiow(NULL,chan_s,IO$_WRITEVBLK,&iosb,NULL, NULL,
		message, strlen(message), NULL, NULL, NULL, NULL)) != 1)
	{printf("Error in QIOW: %d\n",rc); return(NOT_OK);}
return(OK);
}

/* -------------------------------------------------------------------------- */
/*
	Ein-/ Ausschalten von Stoerungsmeldungen vom Selektor an den Host
	Diese Funktion ist z.Z. nur vom Selektor-PC aktivierbar 
*/
long select_msg(tim)
long tim;
{
int sts;
long err;
char cmd[20] = "TIM ";
sprintf(&cmd+4,"%5u\0",tim);
sts = sele_write(cmd);
if (sele_test_echo(&cmd,3)!= OK) lib$stop(-1);
if (select_error_msg(&err) != OK) return(NOT_OK);
if (err == TRUE) return(NOT_OK);
return(OK);
}
/* -------------------------------------------------------------------------- */
/*	Einschaltung der Fremdsteuerung des Selektors
	Diese Funktion ist z.Z. nur vom Selektor-PC aktivierbar 
*/
long select_remote()
{
int sts;
long err;
char cmd[] = "REM";
sts = sele_write(cmd);
if (sele_test_echo(&cmd,3)!= OK) lib$stop(-1);
if (select_error_msg(&err) != OK) return(NOT_OK);
if (err == TRUE) return(NOT_OK);
}
/* -------------------------------------------------------------------------- */
/*	Ausschaltung der Fremdsteuerung des Selektors - 
	ein Steuerung kann dann nur noch ueber den PC des Geraetes
	erfolgen.
	Diese Funktion ist z.Z. nur vom Selektor-PC aktivierbar 
*/
long select_local()
{
int sts;
long err;
char cmd[] = "TTY";
sts = sele_write(cmd);
if (sele_test_echo(&cmd,3)!= OK) lib$stop(-1);
if (select_error_msg(&err) != OK) return(NOT_OK);
if (err == TRUE) return(NOT_OK);
}
/* -------------------------------------------------------------------------- */
/*	Starten des Selektors nach Stillstand auf die Minimaldrehzahl
*/
long select_start()
{
int sts;
long err;
char cmd[] = "SST";
sts = sele_write(cmd);
if (sele_test_echo(&cmd,3)!= OK) lib$stop(-1);
if (select_error_msg(&err) != OK) return(NOT_OK);
if (err == TRUE) return(NOT_OK);
}
/* -------------------------------------------------------------------------- */
/*	Bremsen des Selektors bis zum Stillstand; eine
	Drehzahlanzeige unterhalb 3000 U/min ist nicht moeglich.
*/
long select_stop()
{
int sts;
long err;
char cmd[] = "HAL";
sts = sele_write(cmd);
if (sele_test_echo(&cmd,3)!= OK) lib$stop(-1);
if (select_error_msg(&err) != OK) return(NOT_OK);
if (err == TRUE) return(NOT_OK);
}
/* -------------------------------------------------------------------------- */
/*	Einschalten des geregelten Betriebes mit dem Setzen eines Sollwertes
	der Drehzahl.
	Aenderungen werden spontan uebernommen und ausgefuehrt.
	Neben dem absoluten Minimum und Maximum existieren verbotene
	Drehzahlbereiche, die z.Z. noch nicht exakt bekannt sind.
*/
long select_set_rpm(rpm)
int rpm;
{
int sts;
long err;
char cmd[] = "SDR", s_dat[10] = "SDR 00000";
if ((rpm < LIMIT_U) || (rpm > LIMIT_O))
	{
	print_error(fs,"nominal value out of allowed range");
	return;
	} 
/* Resonanzbereiche sind auch verboten */
if (
((rpm >= RESONANZ_1U) && (rpm <= RESONANZ_1O)) ||
((rpm >= RESONANZ_2U) && (rpm <= RESONANZ_2O)) ||
((rpm >= RESONANZ_3U) && (rpm <= RESONANZ_3O)))
	{
	print_error(fs,"nominal value within inhibited range");
	return;
	} 
sprintf(&s_dat+4,"%5u",rpm);
sts = sele_write(s_dat);
if (sele_test_echo(&cmd,3)!= OK) lib$stop(-1);
if (select_error_msg(&err) != OK) return(NOT_OK);
if (err == TRUE) return(NOT_OK);
}
/* -------------------------------------------------------------------------- */
/*	Leerlauf des Selektors einschalten; weder Antrieb noch
	Verzoegerung erfolgen - eine anschliessende Abbremsung oder
	ein Uebergang zum geregelten Betrieb ist moeglich.
*/
long select_set_idle()
{
int sts;
long err;
char cmd[] = "STB";
sts = sele_write(cmd);
if (sele_test_echo(&cmd,3)!= OK) lib$stop(-1);
if (select_error_msg(&err) != OK) return(NOT_OK);
if (err == TRUE) return(NOT_OK);
}
/* -------------------------------------------------------------------------- */
/*	Starten der Messung der elektrischen Verlustleistung des
	Selektors. Anscheinend wird waehrend der Zeit die
	Regelung deaktiviert.
	Nach Abschluss der Messung erfolgt ein Uebergang in den\
	geregelten Berieb.
	Das Messergebnis wird in den Statusinformationsblock eingetragen.
*/
long select_read_power()
{
int sts;
long err;
char cmd[] = "BRE";
sts = sele_write(cmd);
if (sele_test_echo(&cmd,3)!= OK) lib$stop(-1);
if (select_error_msg(&err) != OK) return(NOT_OK);
if (err == TRUE) return(NOT_OK);
}
/* -------------------------------------------------------------------------- */
/*	Lesen der aktuellen Drehzahl des Selektors */
long select_read_rpm(rpm)
int *rpm;
{
int sts, tmp;
long err;
char status[80], cmd[] = "FDR";
sts = sele_write(cmd);
if (sele_test_echo(&cmd,3)!= OK) lib$stop(-1);

sts = sele_read(&status);
sscanf(&status+4,"%d",&tmp);
*rpm = tmp;
if (select_error_msg(&err) != OK) return(NOT_OK);
if (err == TRUE) return(NOT_OK);
}

/* -------------------------------------------------------------------------- */
/*	Auslesen des kompletten Statusblockes des Selektors.
	Der Informationseintrag 'Verlustleistung' ist anscheinend nur dann 
	aktuell, wenn zuvor eine Verlustleistungsmessung aktiviert wurde.
	Argumente:
rm	=	Betriebsstatus
		[REG == regelnd]
		[STB == im Leerlauf]
		[BRE == bremsend]
nom_rpm	=	Soll der Drehzahl (U/min)
cur_rpm	=	Ist der Drehzahl (U/min)
pwr	=	Verlustleistung (W)
curr	=	elektr. Stomstaerke (A)
rot_temp=	Temperatur des Rotors (C)
cont_temp=	Temperatur des Gehaeuses (C)
inl_temp=	Temperatur Kuehlwasser-Einlass
outl_temp=	Temperatur Kuehlwasser-Abfluss
cool_wat=	Kuehlwasser-Durchfluss (l/min)
vacuum	=	Betriebsvakuum (mbar)
accel	=	Rotationsbeschleunigung (g)
komm	=	Kommunikation zwischen Selektor-PC und Host 
		'ENABLED' oder "DISABLED'
dat	=	Tagesdatum dd.m.yyyy
tim	=	Tageszeit hh.mm.s

*/
long select_read_status(rm,nom_rpm, cur_rpm, pwr, curr, rot_temp, cont_temp,
	inl_temp, outl_temp, cool_wat, vacuum, accel, komm, dat, tim)
char *rm[];
int *nom_rpm, *cur_rpm, *pwr, *rot_temp, *cont_temp, *inl_temp, *outl_temp;
float *cool_wat, *vacuum, *accel, *curr;
char *dat[], *tim[];
short *komm;
{
int cnt, key_id, sl, sts;
long err;
char ena_str[] = "ENABLED";
char dis_str[] = "DISABLED";
char keys[15][9] =
	{
	"Status:\0 ",
	"S_DREH:\0 ",
	"I_DREH:\0 ",
	"P_VERL:\0 ",
	"STROM:\0  ",
	"T_ROT:\0  ",
	"T_GEH:\0  ",
	"T_VOR:\0  ",
	"T_RUECK:\0",
	"DURCHFL:\0",
	"VAKUUM:\0 ",
	"BESCHL:\0 ",
	"KOM: \0   ",
	"DATE: \0  ",
	"TIME: \0  "};

char tok_buf[TOK_BUF_L], *ptr_token, *ptr_src, *ptr, tok_c[] = "/\0";

char status[255], cmd[] = "???";

if (sele_write(cmd) != OK) return(NOT_OK);

if (sele_test_echo(&cmd, 3) == NOT_OK) lib$stop(-1);

if (sele_read(&status) != OK) return(NOT_OK);	/* Und nun der Status */
ptr_src = &status;
for (;;)
	{
	/* Lies Zeichenfolge bis zum naechsten Trennzeichen '/' */
	ptr_token = strtok(ptr_src, &tok_c);
	ptr_src = NULL; /* fuer die weitere Suche notwendig */
	if (ptr_token == NULL) break;
	strcpy(&tok_buf,ptr_token);

	for (key_id = 0; key_id<=14; key_id++)
		{
		/* Ist das Schluesselwort enthalten ? */
		sl = strlen(keys[key_id]);
		if (strncmp(&keys[key_id][0], &tok_buf, sl) == NULL)
			{
			/* Schiebe das Schluesselwort raus */
			for (cnt=0;cnt+sl < TOK_BUF_L; cnt++)
				tok_buf[cnt] = tok_buf[cnt+sl];
			switch (key_id)
				{
				case 0: 
					{
					ptr = rm;
					strcpy(ptr, &tok_buf);
					break;
					}
				case 1:	{sscanf(&tok_buf,"%d",nom_rpm); break;}
				case 2:	{sscanf(&tok_buf,"%d",cur_rpm); break;}
				case 3:	{sscanf(&tok_buf,"%d",pwr); break;}
				case 4:	{sscanf(&tok_buf,"%f",curr); break;}
				case 5:	{sscanf(&tok_buf,"%d",rot_temp); break;}
				case 6:	{sscanf(&tok_buf,"%d",cont_temp); break;}
				case 7:	{sscanf(&tok_buf,"%d",inl_temp); break;}
				case 8:	{sscanf(&tok_buf,"%d",outl_temp); break;}
				case 9:	{sscanf(&tok_buf,"%f",cool_wat); break;}
				case 10:	{sscanf(&tok_buf,"%f",vacuum); break;}
				case 11:	{sscanf(&tok_buf,"%f",accel); break;}
				case 12:
					{
					if (strcmp(&tok_buf, &ena_str) == NULL)
					{*komm = TRUE; break;}
					if (strcmp(&tok_buf, &dis_str) == NULL)
					{*komm = FALSE;break;}
					printf("Invalid Communication value");
					break;
					}
				case 13:
					{
					ptr = dat;
					strcpy(ptr, &tok_buf);
					break;
					}
				case 14:
					{
					ptr = tim;
					strcpy(ptr, &tok_buf);
					break;
					}
				}
			}
		}
	}
if (select_error_msg(&err) != OK) return(NOT_OK);
if (err == TRUE) return(NOT_OK);
return(OK);
}
