// $Source: /usr/local/SimSoft/Sfx3D/src/gsl/programm.cpp,v $
// $Revision: 1.2 $
// $Author: baum $
// $Date: 2001/11/29 15:18:09 $
//
// 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: programm.cpp,v $
// Revision 1.2  2001/11/29 15:18:09  baum
// -Realzeitsteuerung eingebaut
// -Handsteuerung eingebaut
//

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

#include "Kalender.h"

#define _PROGRAMM_CPP
#include "modell.h"
#include "programm.h"

#include "sfx.h"
#include "transp.h"
#include "baustein.h"
#include "ereignis.h"
#include "realzeit.h"
#include "maschine.h"

_impexp typPrgModul* PrgModul;

#ifdef WIN32
#include <windows.h>
#define libhandle HMODULE
#endif

#ifdef __GNUC__
#include <dlfcn.h>
#define libhandle void*
#endif

//---------------------------------------------------------
// Klasse typDLhandler
//---------------------------------------------------------

class typDLhandler {
  public :
    typDLhandler (char* DLpfad);
    ~typDLhandler ();
    PrgFunc* getPrgFunc (char *fname);
    /*< Rudi: 24-09-03: Deklaration zum Aufruf einer Funktion in der C-Schnittstelle. KWF-Projekt.*/
    PrgFunc_typBaustein getPrgFunc_typBaustein (char *fname);
    PrgFunc_void getPrgFunc_void (char *fname);// miro 09.10.2003 13:35

  private:
    // deklariert, aber nicht implementiert
    typDLhandler ();
    typDLhandler (const typDLhandler&);
    typDLhandler& operator = (const typDLhandler&);
     
    libhandle DLModul;
}; // typDLhandler

typDLhandler::typDLhandler (char* DLpfad)
 : DLModul (NULL) {
  if (!DLpfad)	return;
  char dlname[255];
  strcpy (dlname, DLpfad);

#ifdef WIN32
  strcat (dlname, ".dll");
  DLModul = LoadLibrary (dlname);
#endif
#ifdef __GNUC__
  strcat (dlname, ".so");
  DLModul = dlopen (dlname, RTLD_NOW);
#endif
  if (DLModul)	dprintf ("Modell-Library = %s\n", dlname);
  else		    dprintf ("Keine Modell-Library\n");
}

typDLhandler::~typDLhandler () {
  if (DLModul) {
#ifdef WIN32
  FreeLibrary(DLModul);
#endif
#ifdef __GNUC__
  dlclose (DLModul);
#endif
  }
}

/**
 * Aufruf einer void-Funktion, die in der C-Schnittstelle implementiert ist.
 *
 * @param fname Funtionsname
 *
 */
PrgFunc* typDLhandler::getPrgFunc (char *fname) {
  char tmpname[255];
  PrgFunc *tmpFunc = NULL;

  if (DLModul) {
#ifdef WIN32
	/*<rudi: Hinweis: GetProcAddress muss angepasst werden, wenn DEF-Datei bei der DLL-Generierung benutzt wird.
		Befehl DUMPBIN /EXPORTS dateiname.dll liefert Informationen einer DLL-Datei.*/
  #ifdef __BORLANDC__
    sprintf (tmpname, "@%s$qv", fname);
  #else	// defined _MSC_VER
    sprintf (tmpname, "?%s@@YAXXZ", fname);
  #endif
  tmpFunc = ((PrgFunc*) GetProcAddress(DLModul, tmpname));
#endif
#ifdef __GNUC__
  sprintf (tmpname, "%s__Fv", fname);
  tmpFunc = ((PrgFunc*) dlsym (DLModul, tmpname));
#endif
  }
  return tmpFunc;
}


/**
 * Aufruf einer Funktion, die in der C-Schnittstelle implementiert ist
 * und typBaustein* zurueckgibt.
 *
 * @param fname Funtionsname
 *
 * @returns typBaustein*
 *
 * @author rudi - fuer KWF-Projekt
 *
 */
PrgFunc_typBaustein typDLhandler::getPrgFunc_typBaustein (char *fname) {
  char tmpname[255];
  PrgFunc_typBaustein tmpFunc = NULL;

  if (DLModul) {
#ifdef WIN32
	/*<rudi: Hinweis: GetProcAddress muss angepasst werden, wenn DEF-Datei bei der DLL-Generierung benutzt wird.
		Befehl DUMPBIN /EXPORTS dateiname.dll liefert Informationen einer DLL-Datei.*/
  #ifdef __BORLANDC__
    sprintf (tmpname, "@%s$qp19typTransportEinheit", fname); // siehe DUMPBIN!!!
  #else	// defined _MSC_VER
    sprintf (tmpname, "?%s@@YAXXZ", fname); // muss noch angepasst werden
  #endif
  tmpFunc = ((PrgFunc_typBaustein) GetProcAddress(DLModul, tmpname));

#endif
#ifdef __GNUC__
  sprintf (tmpname, "%s__Fv", fname);
  tmpFunc = ((PrgFunc_typBaustein) dlsym (DLModul, tmpname));
#endif
  }
  return tmpFunc;
}

/**
 * Ermoeglicht den Aufruf einer Funktion mit BstNr als Parameter, die in der C-Schnittstelle implementiert ist
 * und bei der Initialisierung eines Modells nicht eingebunden wird.
 *
 * @param name Name der Funktion, BstNr
 *
 * @return void
 *
 * @author miro - fuer KWF-Projekt 09.10.2003 13:34
 *
 */
PrgFunc_void typDLhandler::getPrgFunc_void (char *fname) {
  char tmpname[255];
  PrgFunc_void tmpFunc = NULL;

  if (DLModul) {
#ifdef WIN32
	/*<rudi: Hinweis: GetProcAddress muss angepasst werden, wenn DEF-Datei bei der DLL-Generierung benutzt wird.
		Befehl DUMPBIN /EXPORTS dateiname.dll liefert Informationen einer DLL-Datei.*/
  #ifdef __BORLANDC__
    sprintf (tmpname, "@%s$qi", fname); // siehe DUMPBIN!!!
  #else	// defined _MSC_VER
    sprintf (tmpname, "?%s@@YAXXZ", fname); // muss noch angepasst werden
  #endif
  tmpFunc = ((PrgFunc_void) GetProcAddress(DLModul, tmpname));

#endif
#ifdef __GNUC__
  sprintf (tmpname, "%s__Fv", fname);
  tmpFunc = ((PrgFunc_void) dlsym (DLModul, tmpname));
#endif
  }
  return tmpFunc;
}


//---------------------------------------------------------
// Klasse UserProg
//---------------------------------------------------------

enum UserProgType { PROGRAM, EVENT };

class UserProg {
  public:
    UserProg (typBaustein*, PrgFunc*, char* stackaddr);
    ~UserProg ();

    static void removeAll ();
    
    void start (UserProgType type);

    PrgFunc     *Func;
    typBaustein *PrgBst;
    ExeContxt    Contxt;
    int          Aktiv;

  private:

    static DynArray<UserProg> ClassAllProgs;

    // deklariert aber nicht implementiert
    UserProg ();
    UserProg (const UserProg&);
    UserProg& operator = (const UserProg&);
};	// UserProg

/*static*/ DynArray<UserProg> UserProg::ClassAllProgs (10);

/*static*/ void UserProg::removeAll () {
  while (ClassAllProgs.Last ()) delete ClassAllProgs.Last ();
}

UserProg::UserProg (typBaustein* bst, PrgFunc* func, char* stackaddr)
 : Func   (func),
   PrgBst (bst),
   Contxt (stackaddr),
   Aktiv  (1) {
  ClassAllProgs.append (*this);
}

UserProg::~UserProg () {
  ClassAllProgs.remove (*this);
}

void UserProg::start (UserProgType type) {
  if (PrgModul->ObjGlobalContxt.save ())  return;

  int etyp, einfo1, einfo2, einfo3;
  if (type == EVENT)  {
    etyp   = notiz::AktTyp ();
    einfo1 = notiz::AktInfo1 ();
    einfo2 = notiz::AktInfo2 ();
    einfo3 = notiz::AktInfo3 ();
    if (MySFXModel->PrgDebug ())
      dprintf ("Event %d %s gestartet auf Bst = %d (%s)\n", etyp, ereignisstring(etyp), PrgBst->Nummer (), PrgBst->Name().str());
  }
  else
    if (MySFXModel->PrgDebug ())  dprintf ("Programm gestartet auf Bst = %d\n", PrgBst->Nummer ());

  PrgModul->ObjAktuell = this;
  (*Func) ();	// Aufruf der Funktion aus der DLL
  PrgModul->ObjAktuell = NULL;
  Aktiv = 0;

  if (type == EVENT)  {
    if (MySFXModel->PrgDebug ())  dprintf ("Event %d %s beendet auf Bst = %d (%s)\n", etyp, ereignisstring(etyp), PrgBst->Nummer (), PrgBst->Name().str());
    planesofort (etyp, einfo1, einfo2, einfo3);
  }
  else {
    if (MySFXModel->PrgDebug ())  dprintf ("Programm beendet auf Bst %d\n", PrgBst->Nummer ());
    planesofort (ETaktivieren, PrgBst->Nummer (), PrgBst->Nummer ());
  }
  PrgModul->ObjGlobalContxt.restore ();
}

//---------------------------------------------------------
// struct ProgInfo
//---------------------------------------------------------

struct ProgInfo {
  int Nr;
  PrgFunc* Func;

  ProgInfo (int nr, PrgFunc* func)
    : Nr (nr), Func (func) {}
};

//---------------------------------------------------------
// struct EventInfo
//---------------------------------------------------------

struct EventInfo {
  int Etyp;
  int BstNr;
  PrgFunc* Func;

  EventInfo (int etyp, int bstnr, PrgFunc* func)
    : Etyp (etyp), BstNr (bstnr), Func (func) {}
};

//---------------------------------------------------------
// standard prog
//---------------------------------------------------------

extern void prog_0 ();

//---------------------------------------------------------
// Klasse typPrgModul
//---------------------------------------------------------

// class typPrgModul
int typPrgModul::ClassCreated;

typPrgModul::typPrgModul (char* dllname, char* stackaddr)
 : ObjDLhandler    (new typDLhandler (dllname)),
   ObjGlobalContxt (stackaddr),
   ObjAktuell      (NULL),
   ObjProgArray    (1),
   ObjEventArray   (0) {
  if (ClassCreated++ != 0)
    sim_error ("PROGRAMM", "typPrgModul::typPrgModul", "ist bereits vorhanden",
                0, FATAL);
  ObjProgArray.append (*(new ProgInfo (0, prog_0)));  // Standard-Programm
}

typPrgModul::~typPrgModul () {
  if (!ClassCreated)
    sim_error ("PROGRAMM", "typPrgModul::~typPrgModul", "ist bereits geloescht",
                0, FATAL);
  if (MySFXModel->PrgDebug ()) dprintf ("PrgModul geloescht\n");
  delete ObjDLhandler;
  while (ObjProgArray.Last ())  delete ObjProgArray.removeLast ();
  while (ObjEventArray.Last ()) delete ObjEventArray.removeLast ();
  ClassCreated--;
  UserProg::removeAll ();
}

typBaustein* typPrgModul::getProgBst () const {
  if (!ObjAktuell)
    sim_error ("PROGRAMM", "getProgBst", "kein Programmbaustein vorhanden",
               0, FATAL);
  return ObjAktuell->PrgBst;
}

int typPrgModul::callPrgFunc (char *name) {
  PrgFunc* func = ObjDLhandler->getPrgFunc (name);
  if (func) (*func) ();
  return (func) ? 1 : 0;
}


/**
 * Ermoeglicht den Aufruf einer Funktion, die in der C-Schnittstelle implementiert ist
 * und bei der Initialisierung eines Modells nicht eingebunden wird.
 *
 * @param name Name der Funtion
 *
 * @return typBaustein* Hauptklasse typBaustein wird genutzt, weil somit auf andere Bausteinklassen gecastet werden kann.
 *
 * @author rudi - fuer KWF-Projekt
 *
 */
typBaustein* typPrgModul::callPrgFunc_typBaustein (char *name, typTransportEinheit *zTE) {
  PrgFunc_typBaustein func = ObjDLhandler->getPrgFunc_typBaustein (name);

  if (func)
   	return func(zTE);

  sim_error ("PROGRAMM", "callPrgFunc_typBaustein", "Aufruf einer Funktion in der C-Schnittstelle fehlgeschlagen!", 0, false);
  return NULL;
}

/**
 * Ermoeglicht den Aufruf einer Funktion mit BstNr als Parameter, die in der C-Schnittstelle implementiert ist
 * und bei der Initialisierung eines Modells nicht eingebunden wird.
 *
 * @param name Name der Funktion, BstNr
 *
 * @return void
 *
 * @author miro - fuer KWF-Projekt 09.10.2003 13:34
 *
 */
int typPrgModul::callPrgFunc_void (char *name, int BstNr) {
  PrgFunc_void func = ObjDLhandler->getPrgFunc_void (name);

  if (func)
   	return func(BstNr);

  sim_error ("PROGRAMM", "callPrgFunc_void", "Aufruf einer Funktion in der C-Schnittstelle fehlgeschlagen!", 0, false);
  return NULL;
}


void typPrgModul::installProg (int nr) {
  if (getProg (nr)) return;
  char fname [255];
  sprintf (fname, "prog_%d", nr);
  PrgFunc* func = ObjDLhandler->getPrgFunc (fname);
  if (func)  ObjProgArray.append (*(new ProgInfo (nr, func)));
  else  sim_error ("PROGRAMM", "installProg", "Programm nicht gefunden",
                   nr, FATAL);
}

int typPrgModul::removeProg (int nr) {
  ProgInfo* p;
  for (ObjProgArray.First (); (p = ObjProgArray ()) != NULL; ObjProgArray++)
    if (p->Nr == nr)  {
      delete ObjProgArray.remove (*p);
      return 1;
    }
  return 0;
}

PrgFunc* typPrgModul::getProg (int nr) {
  ProgInfo* p;
  for (ObjProgArray.First (); (p = ObjProgArray ()) != NULL; ObjProgArray++)
    if (p->Nr == nr)  return p->Func;
  return NULL;
}

void typPrgModul::startProg (int bstnr, int nr, char* stackaddr) {
  PrgFunc* func = getProg (nr);
  if (func)  {
    typPrgBaustein* prgBst = (typPrgBaustein*) baustein [bstnr];
    prgBst->ObjProgramm = new UserProg (prgBst, func, stackaddr);
    prgBst->ObjProgramm->start (PROGRAM);
  }
  else  sim_error ("PROGRAMM", "startProg", "Programm nicht vorhanden",
                   nr, FATAL);
}

void typPrgModul::installEvent (int etyp, int bstnr, char* name) {
  if ((etyp == ETaktivieren) || (etyp == ETwecken))
    sim_error ("PROGRAMM", "installEvent",
               "ETaktivieren und ETwecken nicht erlaubt", name, FATAL);
  if (getEvent (etyp, bstnr)) return;
  PrgFunc* func = ObjDLhandler->getPrgFunc (name);
  if (func)  ObjEventArray.append (*(new EventInfo (etyp, bstnr, func)));
  else  sim_error ("PROGRAMM", "installEvent", "Event nicht gefunden",
                   name, FATAL);
}

int typPrgModul::removeEvent (int etyp, int bstnr) {
  EventInfo* e;
  for (ObjEventArray.First (); (e = ObjEventArray ()) != NULL; ObjEventArray++)
    if ((e->BstNr == bstnr) && (e->Etyp == etyp)) {
      delete ObjEventArray.remove (*e);
      return 1;
    }
  return 0;
}

PrgFunc* typPrgModul::getEvent (int etyp, int bstnr) {
  EventInfo* e;
  for (ObjEventArray.First (); (e = ObjEventArray ()) != NULL; ObjEventArray++)
    if ((e->BstNr == bstnr) && (e->Etyp == etyp))  return e->Func;
  return NULL;
}

int typPrgModul::checkEvent (int etyp, int bstnr, char* stackaddr) {
  typBaustein* bst = baustein[bstnr];
  if ((etyp == ETaktivieren) && bst->ObjEvent)
    if (bst->Zustand () == PRGWARTEN) {
      continueUserProg (bst->ObjEvent);
      return 1;
    }

    else 															// Matthias 2002-03-08; diese + naechste Zeile neu
      if (bst->Zustand () == SCHLAFEN) return 0;		// damit in events schlafen () aufgerufen werden kann
      else sim_error ("PROGRAMM", "checkEvent",
                    "Zustand != PRGWARTEN", bstnr, FATAL);
  else {
    PrgFunc* func = getEvent (etyp, bstnr);
    if (func)
      if (bst->ObjEvent)
        if (bst->ObjEvent->Func != func)
          sim_error ("PROGRAMM", "checkEvent",
                     "Baustein bearbeitet bereits Event", bstnr, FATAL);
        else
          if (bst->ObjEvent->Aktiv)
            sim_error ("PROGRAMM", "checkEvent",
                       "Ereignis waehrend aktivem Event", bstnr, FATAL);
          else {
            delete bst->ObjEvent;
            if (MySFXModel->PrgDebug ()) dprintf ("Event geloescht auf Bst = %d\n", bstnr);
            bst->ObjEvent = NULL;
          }
      else {
        bst->ObjEvent = new UserProg (bst, func, stackaddr);
        bst->ObjEvent->start (EVENT);
        return 1;
      }
  }
  return 0;
}

void typPrgModul::interruptUserProg () {
  UserProg* akt = ObjAktuell;
  if (akt->Contxt.save ()) {	// wiederhergestellt
    if (MySFXModel->PrgDebug ())  dprintf ("fortsetzen auf Bst = %d\n", akt->PrgBst->Nummer ());
    ObjAktuell = akt;
  }
  else {			// gesichert
    if (MySFXModel->PrgDebug ())  dprintf ("unterbrechen auf Bst = %d\n", akt->PrgBst->Nummer ());
    ObjAktuell = NULL;
    ObjGlobalContxt.restore ();
  }
}

int typPrgModul::continueUserProg (UserProg* up) {
  if (!up->Aktiv) {
    delete up;
    return 0;
  }
  if (MySFXModel->PrgDebug ())	dprintf ("continueUserProg auf Bst = %d\n", up->PrgBst->Nummer());
  if (!ObjGlobalContxt.save ()) {	// Global gesichert, Userprog wiederherstellen
     if (MySFXModel->PrgDebug ())	dprintf ("Global gesichert\n");
     up->Contxt.restore ();
  }
  else				// Global wiederhergestellt
    if (MySFXModel->PrgDebug ())	dprintf ("Global fortsetzen\n");
  return 1;
}

/* programm.cpp */

