// $Source: /usr/local/SimSoft/Sfx3D/src/gsl/realzeit.cpp,v $
// $Revision: 1.3 $
// $Author: baum $
// $Date: 2001/12/06 13:26:51 $
//
// Login name of the user who locked the revision
// $Locker:  $
// Tag name used to checkout the file
// $Name:  $
// State assigned to revision
// $State: Exp $
//
//
// $Log: realzeit.cpp,v $
// Revision 1.3  2001/12/06 13:26:51  baum

//
// Revision 1.2  2001/11/29 15:18:09  baum
// -Realzeitsteuerung eingebaut
// -Handsteuerung eingebaut
//

#include "Kalender.h"
#include "List.h"
#include "DynArray.h"

#define _REALZEIT_CPP
#include "realzeit.h"

#include "sfx.h"
#include "serialio.h"
#include "msps.h"
#include "ereignis.h"
#include "sprache.h"
#include "arbplan.h"    //---rudi---18.09.01
#include "modell.h"    //---rudi---18.09.01
#include "quelle.h"    //---rudi---18.09.01

#include <tcl.h>        //---rudi---20.08.01
#include <tk.h>        //---rudi---20.08.01
#include "sfxhla.h"    //---rudi---20.01.03


//extern int Tcl_Invoke TCL_VARARGS_DEF(Tcl_Interp *, arg1);


int rzspar [8];
bool sprachausgabe = false;
double rzszeitschwelle;

DynArray<notiz> rzskalender;
DynArray<notiz> hlakalender; // 20-01-03: Rudi: diese Variable sollte noch in sfxhla.h rein.

Tcl_Interp *RzsInterp;

//---rudi---17.08.01
//static char LesePuffer [64];
//static int  PufferZeiger;
//---rudi---18.08.01 Test
double rzeit = 0.0;
double lastGrantTime = 0.0;
// - - - - - - - - - - - - - - - - - - - - - - - - - - -



// -----------------------------------------------------
// RZS-Kalenderfunktionen
// -----------------------------------------------------

void rzsplaneein(const SimTime& zeit, int typ, int info1, int info2, int info3) {
  if (hla && hlaTimeConstrained) {
    hlakalender.append (*(new notiz (zeit, typ, info1, info2, info3)));
    dprintf ("hlaplaneein---Zeit: %8.2f  ereignis %2i  %2i  %2i %2i \n",zeit, typ, info1, info2,info3);
  } else {
  rzskalender.append (*(new notiz (zeit, typ, info1, info2, info3)));
    dprintf ("rzsplaneein---Zeit: %8.2f  ereignis %2i  %2i  %2i %2i \n",zeit, typ, info1, info2,info3);
  }
  //notiz* n (rzskalender.Last ());
  //dprintf("rzsplaneein---ereignis(%2i): %2i baustein: %2i\n",rzskalender.Count(),n->Typ (),n->Info1());
  //dprintf("RUDI:: rzsplaneein: %2i baustein: %2i\n",ETypZuHla(n->Typ ()),n->Info1());
}


void hlaplaneein(const SimTime& zeit, int typ, int info1, int info2, int info3) {
  hlakalender.append (*(new notiz (zeit, typ, info1, info2, info3)));
  //  dprintf ("hlaplaneein---Zeit: %8.2f  ereignis %2i  i1 %2i  i2 %2i \n",
  //zeit, typ, info1, info2);
  //  notiz* n (hlakalender.Last ());
  //  dprintf("hlaplaneein---ereignis(%2i): %2i baustein: %2i\n",hlakalender.Count(),n->Typ (),n->Info1());
  //  dprintf("RUDI:: hlaplaneein: %2i baustein: %2i\n",ETypZuHla(n->Typ ()),n->Info1());
}

/****************Handsteuerung***********************/
void rzsprint() {
	notiz* n (rzskalender.First ());
	for (; n; n = rzskalender.Next ()){
        dprintf("\nrzsprint::---Notiz(%2i):  baustein:%2i Rückmeldung: %s",rzskalender.Count(), n->Info1(), ereignisstring (n->Typ ()));	
	}
}

// -----------------------------------------------------
// Die modifizerten Funktionen
// planeein, planesofort und waslos
// -----------------------------------------------------

/*
 * Beim Einplanen werden bei eingeschalteter RZS diverse
 * Endeereignisse abgefangen und in einen seperaten Kalender
 * eingegliedert. Diese werden dann von der angeschlossenen
 * Modelltechnik erwartet.
 */

void planeein (double zeit, int typ, int info1, int info2, int info3) {
  planeein (SimTime (zeit * SimTime::SecondsPerMinute ()), typ, info1, info2, info3);
}

void planeein (const SimTime& zeit, int typ, int info1, int info2, int info3) {
  if (!rzs) {
      notiz::planeein (zeit, typ, info1, info2, info3);
  } else {
      switch (typ) {
         case ETeabgeben    : RzsSenden (info1, typ, 1);
                              rzsplaneein (zeit, ETbereit, info1, info2, info3);
                              break;
         case ETeausschl    : 
         case ETeaufnehmen  : 
         case ETearbeiten   : 
         case ETedrehen     : 
         case ETefahren     : 
         case ETefoerdern   : rzsplaneein (zeit, typ, info1, info2, info3);
	                      break;
         default            : notiz::planeein (zeit, typ, info1, info2, info3); 
	                      break;
      }
   }
}

void planedelta (double zeit, int typ, int info1, int info2, int info3) {
  planedelta (SimTime (zeit * SimTime::SecondsPerMinute ()), typ, info1, info2, info3);
}

void planedelta (const SimTime& zeit, int typ, int info1, int info2, int info3) {
  planeein (Modellzeit() + zeit, typ, info1, info2, info3);
}

void planesofort (int typ, int info1, int info2, int info3) {
  if (!rzs)
    if (hla) {
      switch (typ) 
	{
	case ETeabgeben    :
	case ETeausschl    : 
	case ETeaufnehmen  : 
	case ETearbeiten   : 
	case ETedrehen     : 
	case ETefahren     : 
	case ETefoerdern   : planeein (Modellzeit (), typ, info1, info2, info3);
	  break;
	default            : notiz::planesofort (typ, info1, info2, info3); 
	  break;
	}
    }
    else
    notiz::planesofort (typ, info1, info2, info3);
  else {
    switch (typ) {
      case ETeabgeben   :
      case ETeausschl   :
      case ETeaufnehmen :
      case ETearbeiten  :
      case ETedrehen    :
      case ETefahren    :
      case ETefoerdern  : planeein (Modellzeit (), typ, info1, info2, info3);
                          break;
      default           : notiz::planesofort (typ, info1, info2, info3);
                          break;
    }
  }
}




// - - - - - - - - - - - - - - - - - - - - - - - - - - -

/*
 * waslos() prueft zunaechst, ob die Modelltechnik oder Realzeit-
 * simulation eingeschaltet ist. Wenn nicht dann wird die aktuelle
 * Notiz wie gewohnt weitergeschaltet.
 * Ist die Realzeitsimulation oder die Modelltechnik eingeschaltet,
 * wird die aktuelle Notiz erst weitergeschaltet, wenn die Ereignis-
 * zeit der naechsten Notiz mit der Systemzeit des Rechners ueberein-
 * stimmt. Sonst wird eine 0 zurueckgegeben.
 * Bei eingeschalteter Modelltechnik wird zudem gepueft, ob eine
 * Nachricht empfangen wurde, diese ggf. aus dem RZS-Kalender aus-
 * gegliedert, die Modellzeit des Ereignisses auf die echte Zeit
 * angepasst und dieses Ereignis dann in den normalen Kalender ein-
 * gegliedert.
 */

bool waslos () {
  // **** Mit eingeschalteter Modelltechnik (RZS) ? ****
  if (rzs) {
    if (hla && hlaRemoteControl && hlaTimeRegulating)
      if (MySFXHla->GetInteractionCounter() > 0) {
      	dprintf ("RUDI:: Anzahl der Interactions: %i\n",  MySFXHla->GetInteractionCounter());
			MySFXHla->VerarbeiteSfxInteractions ();
		}
  		
    notiz *zAktNachf = ++LCursor<notiz> (notiz::ClassKalender);

    //int von, typ, par;

    if (hla && hlaTimeConstrained)
      if (MySFXHla->GetGrantTime() >= zAktNachf->Zeit ().inSeconds()) {
	dprintf ("HlaGrantTime ist : %f\n", MySFXHla->GetGrantTime());
	return notiz::waslos ();
      }

    if (hla && hlaTimeConstrained) {
      //dprintf ("Frage nach timeAdvance fuer hlaTimeConstrained: %f\n",zAktNachf->Zeit ().inSeconds());
      // SendNextEventRequest (zAktNachf->Zeit ().inSeconds());

      if ((zAktNachf->Zeit ().inSeconds() - MySFXHla->GetGrantTime()) > 1.0)
	MySFXHla->SendNextEventRequest (zAktNachf->Zeit ().inSeconds());
      //CreateThread(NULL,0,SendNextEventRequest,(LPDWORD)zAktNachf->Zeit ().inSeconds(),0,&dwThreadId);
      else if ((zAktNachf->Zeit ().inSeconds() - MySFXHla->GetGrantTime()) < 1.0)
	MySFXHla->SendNextEventRequest (MySFXHla->GetGrantTime() + 0.1);      
    }
	
    if (!rzskalender.isEmpty () || !hlakalender.isEmpty()) {
      int von, typ, par;
      int iLesen;
      if (hla && hlaTimeConstrained) {
	//iLesen = Lesen (&von, &typ, &par);
	while (Tcl_DoOneEvent (TCL_ALL_EVENTS|TCL_DONT_WAIT) != 0);
	
	while (MySFXHla->GetInteractionCounter() > 0) {
	  Tcl_DoOneEvent(0);
	  if (MySFXHla->Lesen (&von, &typ, &par)) {
	    notiz* n = NULL;
	    for (n = hlakalender.First(); n; n = hlakalender.Next ()){
          if ((typ == n->Typ ()) && (von == n->Info1 ()))
		break;
	    }
        if (n) {
	      //double lookahead = 1.1;
	      n->setZeit (SimTime (((MySFXHla->GetGrantTime()-1.1)/60) *  SimTime::SecondsPerMinute ()));
	      dprintf ("RUDI:: eine Zeit auf %f geaendert\n", \
		       ((MySFXHla->GetGrantTime()-1.1)/60) *  SimTime::SecondsPerMinute ());
	    }
	    if (typ == ETbereit)
	      n->setTyp (ETeabgeben);
	    hlakalender.remove (*n)->planeein ();
	  }
	}
      } else {
	if (hla && hlaRzsEcho) 
	  iLesen = MySFXHla->Lesen (&von, &typ, &par);
	else
	  iLesen = RzsLesen (&von, &typ, &par);
      
	if (iLesen) {
	  if (hla && hlaTimeConstrained)
	    dprintf("RUDI:: hlaTimeConstrained von: %2i, typ: %2i, par: %2i\n", von,typ,par);
	  notiz* n = NULL;
	  if (hla && hlaTimeConstrained) {
	    for (n = hlakalender.First(); n; n = hlakalender.Next ()){
	      if ((typ == n->Typ ()) && (von == n->Info1 ()))
		break;
	    }
	  } else {
	    //notiz* n (rzskalender.First ());
	    for (n = rzskalender.First(); n; n = rzskalender.Next ()){
	      if ((typ == n->Typ ()) && (von == n->Info1 ()))
		break;
	    }
	  }
	  
	  if (n) {
	    // von der Notiz wird die Zeit auf die Modellzeit gesetzt,
	    // da das Ereignis zurueckkam und eingeplant werden muss.
	    if (hla && hlaTimeConstrained) {
	      // An dieser Stelle sollte LookAhead plus extra_time Abgezogen werden, um
	      // die visuelle Zeit synchron zu halten !!!
	      //double lookahead = 1.1;
	      n->setZeit (SimTime (((MySFXHla->GetGrantTime()-1.1)/60) *  SimTime::SecondsPerMinute ()));
	      //n->setZeit (SimTime ((GetGrantTime()/60) *  SimTime::SecondsPerMinute ()));
	      dprintf ("RUDI:: eine Zeit auf %f geaendert\n", \
		       ((MySFXHla->GetGrantTime()-1.1)/60) *  SimTime::SecondsPerMinute ());
	      /*
		dprintf ("RUDI:: eine Zeit auf %f geaendert\n", \
		(MySFXHla->GetGrantTime()/60) *  SimTime::SecondsPerMinute ());
	      */
	    } else {
          n->setZeit (Modellzeit ());
	    }
          if (typ == ETbereit)
            n->setTyp (ETeabgeben);
	    if (hla && hlaTimeConstrained) {
	      hlakalender.remove (*n)->planeein ();
	    } else {
	      n->planeein ();
	      MySFXHla->SendInteraction(von,typ,par);
	      rzskalender.remove (*n);
	    }

	    // Da der Meldung in Liste eine Zeit zugewiesen wurde, kann die Information
	    // ueber HLA geschickt werden, um von einem Empfaenger bearbeitet werden
	    // zu koennen.
	    
	    //if (hla && hlaTimeRegulating) {
	    //HlaSenden (Modellzeit().inSeconds(),zAktNachf->Typ(),zAktNachf->Info1(),zAktNachf->Info2(),zAktNachf->Info3());
	    //dprintf("Frage nach TimeAdvanceGrant: %f\n",Modellzeit().inSeconds());
	    //SendNextEventRequest (Modellzeit().inSeconds());
	    //	  }

        } //********* if(n)************
      }//*********if (RzsLesen (&von, &typ, &par))****
      }
    }//***********if (!rzskalender.isEmpty ())********
    // Diese Abfrage wird gestellt, damit der ZeitVorlauf in der Federation nicht
    // zum Einfrieren der constrained Federates fuehrt.
    if (hla && hlaTimeRegulating) {
      MySFXHla->SendNextEventRequest (Modellzeit().inSeconds());
      //CreateThread(NULL,0,SendNextEventRequest,(Modellzeit ().inSeconds()),0,&dwThreadId);
    }
    if (zAktNachf && zAktNachf->Zeit () <= Modellzeit ()) {
      return notiz::waslos ();
    } else {
      return false;
    }
  } // **** if (rzs) *****
  
    //
    // **** Mit eigeschalteter Echtzeitsimulation ? ****
  else if (echtzeitsim) {
    notiz *zAktNachf = ++LCursor<notiz> (notiz::ClassKalender);
    //dprintf("RUDI:: Zeit Aktuell: %f\n",Modellzeit().inMinutes());
    //dprintf("RUDI:: Zeit von zAktNachf: %f\n",zAktNachf->Zeit ().inMinutes());
    
    if (hla && hlaTimeConstrained) {
      // Simulator schickt an HLA eine Anfrage, ob der Simulator
      // um die Zeit voranschreiten kann, die im Nachfolger
      // drinsteht.
      MySFXHla->SendNextEventRequest (zAktNachf->Zeit ().inMinutes());
      //CreateThread(NULL,0,SendNextEventRequest,(zAktNachf->Zeit ().inSeconds()),0,&dwThreadId);
      //dprintf("RUDI:: GrantTime: %f (Min.) gewaehrt!\n",GetGrantTime());
      
      if (zAktNachf && zAktNachf->Zeit () <= Modellzeit ())
	return notiz::waslos ();
      else
	return false;      
    } else if (hla && (!hlaTimeRegulating || !hlaTimeConstrained)) {
      if (zAktNachf && zAktNachf->Zeit () <= Modellzeit ())
	return notiz::waslos ();
      else
	return false;
    }
    // wenn kein hla geschaltet, dann normale RealzeitSim
    if (!hla){
    if (zAktNachf && zAktNachf->Zeit () <= Modellzeit ())
      return notiz::waslos ();
    else
      return false;
  }
  }
  // normale Simulation
  else {
    notiz *zAktNachf = ++LCursor<notiz> (notiz::ClassKalender);
    int ic = 0;
    //
    // Als erste Abfrage erfolgt, ob sich in der Ereignisliste noch eine Meldung
    // befindet, die vor der naechsten timeAdvance-Abfrage verarbeitet werden kann.
    
    //if (hla && hlaTimeConstrained && !hlaTimeRegulating) {
    if (hla && hlaTimeConstrained) {
      /*
      ic = MySFXHla->GetInteractionCounter();
      if (ic > 0) {
	    dprintf ("Interaction bekommen ---!\n");
		MySFXHla->VerarbeiteInteractions();
      }
	  */
      if (MySFXHla->GetGrantTime() >= zAktNachf->Zeit ().inSeconds()) {
	//dprintf ("HlaGrantTime ist : %f\n",GetGrantTime());
	    return notiz::waslos ();
      }
    }
    

    if (hla && (hlaTimeConstrained || hlaTimeRegulating)) {
      // Simulator schickt an HLA eine Anfrage, ob der Simulator
      // um die Zeit voranschreiten kann, die im Nachfolger
      // drinsteht.
      MySFXHla->SendNextEventRequest (zAktNachf->Zeit ().inSeconds());
      //CreateThread(NULL,0,SendNextEventRequest,(zAktNachf->Zeit ().inSeconds()),0,&dwThreadId);
      //dprintf("RUDI:: GrantTime: %f (Sec.) gewaehrt!\n",MySFXHla->GetGrantTime());

      ic = MySFXHla->GetInteractionCounter();
      if (ic > 0) {
	    	dprintf ("Interaction bekommen!\n");
			MySFXHla->VerarbeiteInteractions();
      }

      if (MySFXHla->GetGrantTime() >= zAktNachf->Zeit ().inSeconds())
		return notiz::waslos ();
      else
		return false;

      //lastGrantTime = GetGrantTime ();
    } else if (hla && (!hlaTimeRegulating || !hlaTimeConstrained) && hlaAsynchronousDelivery) {
      MySFXHla->Tick(MySFXHla->GetTickMin(), MySFXHla->GetTickMax());
      ic = MySFXHla->GetInteractionCounter();
      if (ic > 0) {
			dprintf ("Interaction bekommen!\n");
			MySFXHla->VerarbeiteInteractions();
      }
      return notiz::waslos ();
    } else if (hla && !hlaTimeRegulating && !hlaTimeConstrained && !hlaAsynchronousDelivery) {
      sim_error ("HLA", "waslos()", \
		 "Keine Zeiteinsteigenschaft eingestellt!", 0, FATAL);
    } else if (!hla) // normal
      return notiz::waslos ();
  }
}

// ------------------------------------------------------------------------
// Kommunikationsfunktionen
// ------------------------------------------------------------------------

/*
 * Innerhalb der Kommunikationsfunktionen kann ggf.
 * ausgewdhlt werden, welche Methode zur Ansteuerung 
 * einer Modellanlage benutzt werden soll, z.B. MSPS oder 
 * SIEMENS SPS, usw.
 */

void RzsInit () {
  switch (rzspar [1]) {
    case  0 : MspsInit (); break;
    // case  1 : SiemensSPSInit(); break;
    //    case  2 : HlaInit(); break;
    default : break;
  }
}

void RzsSenden (int BausteinNr, int ETyp, int Parameter) {
  if (rzs) {
    if (hla && hlaRzsEcho && hlaTimeRegulating) {
      //dprintf("in if-Anw. fuer hla von RzsSenden");
      MySFXHla->SendUpdate(BausteinNr,ETyp,Parameter);
    } else {
    switch (rzspar [1]) {
      case  0 : MspsSenden (BausteinNr, ETyp, Parameter); break;
    // case  1 : SiemensSPSSenden (BausteinNr, ETyp, Parameter); break;
      case  2 : MySFXHla->SendUpdate (BausteinNr, ETyp, Parameter); break;
      default : break;
    }
  }
  }
  if (sprachausgabe && (rzs || echtzeitsim))
    switch (ETyp) {
      case ETaeinschl : SprichEinschleusen (); break;
      case ETausschl  : SprichAusschleusen (); break;
    }
}

int RzsLesen (int *VonBausteinNr, int *ETyp, int *Parameter) {
  switch (rzspar [1]){
    case  0 : return MspsLesen (VonBausteinNr, ETyp, Parameter);
 // case  1 : return SiemensSPSLesen (VonBausteinNr, ETyp, Parameter);
  case  2 : return MySFXHla->Lesen (VonBausteinNr, ETyp, Parameter);
    default : return 0;
  }
}

void RzsExit () {
  while (rzskalender.Last ())
    delete rzskalender.removeLast ();
  switch (rzspar [1]){
    case  0 : MspsExit (); break;
 // case  1 : SiemensSPSExit (); break;
  case  2 : MySFXHla->Exit (); break;
    default : break;
  }
}

//==================================================================
//		HLA-Interface ---rudi---16.08.01
//==================================================================

// -----------------------------------------------------------
// --- Lokal benutzte Funktion:                            ---
// --- Uebersetzt die MSPS-Befehle in Simulator-Ereignisse ---
// -----------------------------------------------------------


// folgende Funktion nur im Test benutzt
int ETypEcho (int order) {
  switch (order) {
  case  1 : return  2;
  case  3 : return  4; 
  case  5 : return  6; 
  case 14 : return 16; 
  case 15 : return 17; 
  case 30 : return 31; 
  case 36 : return 37; 
  case 53 : return 63; 
  case 60 : return 61; 
  default : return  0;
  }
}

/**
 * Folgende Funktionen nur zur Initialisierung und zum Loeschen der HLA-Verbindung.
 */
void HlaInit (Tcl_Interp* interp,const char* fedName, const char* fExecName, double lookAhead, double tickMin, double tickMax, const char* fedFileName, const char* pClass, DynArray<char> &pParList,const char* sClass, DynArray<char> &sParList) {

  MySFXHla = new SFXHla (interp,fedName,fExecName,lookAhead,tickMin,tickMax,fedFileName,pClass,pParList,sClass,sParList);
  
}

void HlaExit () {
  delete MySFXHla;
}

void HLA_SendInteractionWithClassAndParName (double EreignisZeit, int ETyp, int BausteinNr, int ArbPlNr, int Menge, const char* className, const char* parName) {
  char buf [255];
  MySFXHla->SendInteractionWithClassAndParName (EreignisZeit,ETyp,BausteinNr,ArbPlNr,Menge,className,parName);
}

void HLA_SendInteractionMessageWithClassAndParName (const char* iaMessage, const char* className, const char* parName) {
  MySFXHla->SendInteractionMessageWithClassAndParName (iaMessage,className,parName);
}

const char* HLA_GetReceivedInteraction () {
  return MySFXHla->GetReceivedInteraction ();
}
  
void HLA_PublishObject (const char* className, int bstNr, DynArray<char> &attribs) {
  MySFXHla->RegisterObject (className, bstNr, attribs);
}

void HLA_SubscribeObject (const char* className, DynArray<char> &attribs) {
  MySFXHla->SubscribeObject (className, attribs);
}
