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

#include <float.h>
#include "Array.h"
#include "Vec3D.h"

#define _MASCHINE_CPP
#include "modell.h"
#include "maschine.h"

#include "sfx.h"
#include "transp.h"
#include "baustein.h"
#include "platz.h"
#include "ereignis.h"
#include "programm.h"
#include "regalver.h"
#include "poolver.h"
#include "arbplan.h"
#include "realzeit.h"

//---------------------------------------------------------
// Klasse typPrgBaustein
//---------------------------------------------------------

typPrgBaustein::typPrgBaustein (unsigned n, unsigned b) 
 : typBaustein   (n,b),
   ObjProgramm   (NULL),
   ObjPrgArbZeit ()
 { }

/*virtual*/ typPrgBaustein::~typPrgBaustein ()
 { }

/*virtual*/ void typPrgBaustein::typTGSlesen (InputFile& file) {
  typBaustein::typTGSlesen (file);
  if (ObjPalPlaetze.Count () != 1)
    sim_error ("MASCHINE", "typPrgBaustein::typTGSlesen",
               "PrgBaustein mit != 1 Palettenplatz", ObjNummer, FATAL);
}

const SimTime& typPrgBaustein::PRG_ArbZeit () const {
  return ObjPrgArbZeit;
}
    
void typPrgBaustein::setPRG_ArbZeit (const SimTime& time) {
  ObjPrgArbZeit = time;
}

/*virtual*/ void typPrgBaustein::e_aufnehmen() {
  typPlatz *p (ObjTransportEinheit->AktPlatz ());
  if (p->Buchung () != ObjTransportEinheit)
    sim_error ("MASCHINE", "typPrgBaustein::e_aufnehmen",
               "TransportEinheit ist nicht auf Platz gebucht", ObjNummer, FATAL);
  p->setBuchung (NULL);
  ObjBuchung--;
  ObjTransportEinheit->positionieren (p->Position ());
  setZustand (PRGWARTEN);
  if (ObjStoerDauer > SimTime (0.0))
    planesofort (ETastoerung, ObjNummer);
  else
    planesofort (ETaktivieren, ObjNummer, ObjNummer);
  planesofort (ETeabgeben, ObjZielBaustein->Nummer (), ObjNummer);
}

/*virtual*/ void typPrgBaustein::a_arbeiten () {
  setZustand (ARBEITEN);
  if (ObjPrgArbZeit >= SimTime (0.0))
    planedelta (ObjPrgArbZeit, ETearbeiten, ObjNummer, 0);
  else
    planedelta (ObjTransportEinheit->Vorgang ()->Dauer (), ETearbeiten, ObjNummer, 0);
  if (MySFXModel->RealTime ())  RzsSenden (ObjNummer, ETaarbeiten, notiz::AktInfo2 ());
}

/*virtual*/ void typPrgBaustein::e_arbeiten () {
  ObjPrgArbZeit = SimTime (-1.0);
  ObjTransportEinheit->schalteVorgang ();
  setZustand (PRGWARTEN);
  if (ObjStoerDauer > SimTime (0.0))
    planesofort (ETastoerung, ObjNummer);
  else
    planesofort (ETaktivieren, ObjNummer, ObjNummer);
}

bool typPrgBaustein::hasProgramm () const {
  return ObjProgramm;
}

//---------------------------------------------------------
// Klasse typMaschine
//---------------------------------------------------------

typMaschine::typMaschine (unsigned n, unsigned b)
 : typPrgBaustein (n, b),
   ObjBetreuer    (NULL),
   ObjVerwalter   (NULL),
   ObjFindeMeldungFunktion (typMaschine::findeFifoMeldung) {
  ObjAvis = new typAvis ();
}

typMaschine::~typMaschine ()
 { }

/*virtual*/ void typMaschine::typTSTlesen (InputFile& file) {
  typBaustein::typTSTlesen (file);
  setStrategie (ObjStrategie);
}

/*virtual*/ unsigned typMaschine::setStrategie (unsigned stratNr) {
  ObjStrategie = stratNr;
  switch (ObjStrategie) {
    case 0:  ObjFindeMeldungFunktion = typMaschine::findeFifoMeldung; 
             break;
    case 1:  ObjFindeMeldungFunktion = typMaschine::findeKOZMeldung; 
             break;
    default: sim_error ("VWAGEN", "typMaschine::setStrategie",
                        "Strategie ist nicht vorhanden, setzte FIFO",
                         ObjNummer, WARNING);
             ObjFindeMeldungFunktion = typMaschine::findeFifoMeldung; 
             break;
  }
  return ObjStrategie;
}

/*virtual*/ typTransportEinheit *typMaschine::gibTransportEinheit (typMeldung* zM) {
  if (zM->TransportEinheit () != ObjTransportEinheit)
    sim_error ("BAUSTEIN", "typMaschine::gibTransportEinheit",
               "falsche TransportEinheit angefordert", ObjNummer, FATAL);
  if (ObjVerwalter) {
    ObjVerwalter->decBelegung();
    ObjVerwalter->Avis ()->decDirIstAnzahl ();
    ObjVerwalter->Meldung_Arbeitsende ();
  }
  decBelegung ();
  ObjTransportEinheit->AktPlatz ()->setTransportEinheit (NULL);
  ObjTransportEinheit = NULL;
  setZustand (AKTIVIERT);
  planesofort (ETaabgeben, ObjNummer, zM->NachBaustein ()->Nummer ());
  return ObjTransportEinheit;
}

/*virtual*/ void typMaschine::a_aufnehmen () {
  typBaustein::a_aufnehmen ();
  if (ObjVerwalter) {
    ObjVerwalter->incBelegung();
  }
}

/*virtual*/ void typMaschine::a_meldewarten () {
  setZustand (WARTEN);
  ObjTransportEinheit->Anmelden ();

  if (ObjBetreuer)
    ObjBetreuer->Anforderung_naechste_TE (this);
}

/*virtual*/ bool typMaschine::hasKapazitaet (typTransportEinheit* te) const {
  bool habeKap = (ObjBelegung + ObjBuchung) < ObjKapazitaet;
  return ObjBetreuer ? ObjBetreuer->hasKapazitaet (te) || habeKap : habeKap;
}

/*virtual*/typBaustein* typMaschine::setZielBaustein (typTransportEinheit *zTE) {
  typBaustein* ziel;

  if (ObjBetreuer && !(ObjAvis->isDirAvisOk ()))
    ziel = ObjBetreuer->setZielBaustein (zTE);
  else {
    ziel = this;
    ObjAvis->decIndIstAnzahl ();
    ObjAvis->incDirIstAnzahl ();
    if (ObjVerwalter)  ObjVerwalter->Avis ()->incDirIstAnzahl ();
    (zTE->Baustein ())->Avis ()->decDirIstAnzahl ();
  }
  return ziel;
}

/*virtual*/ typPlatz* typMaschine::waehlePlatz (typTransportEinheit* zTE) {
  typPlatz* p (ObjPalPlaetze [0]);
  if (!p->isVerfuegbar ())
    return (p->Buchung () == zTE) ? p : (typPlatz*) NULL;
  p->setBuchung (zTE);
  ObjBuchung++;
  return p;
}

/*virtual*/ Vec3D typMaschine::UebernahmePos (const typBaustein&) const {
  return (ObjPalPlaetze [0]->Position ());
}

/*virtual*/ Vec3D typMaschine::UebergabePos (const typBaustein&) const {
  return (ObjPalPlaetze [0]->Position ());
}

/*virtual*/ typBaustein* typMaschine::setVerwalter (typBaustein* newVerwalter) {
  if (newVerwalter->Basistyp () != tNcPoolverwalter)
    sim_error ("BAUSTEIN", "typMaschine::setVerwalter",
               "Maschine kann nur von NCPool verwaltet werden", ObjNummer, FATAL);
  return (ObjVerwalter = (typNcPoolverwalter*) newVerwalter);
}

/*virtual*/ typRegalverwalter* typMaschine::setBetreuer (typRegalverwalter* newBetreuer) {
  return (ObjBetreuer = newBetreuer);
}

/*virtual*/ void typMaschine::individuellAktivieren (typBaustein* vonWem) {
  if (!((ObjZustand == RUHE) || ((ObjZustand == WARTEDP) && (ObjZielBaustein == vonWem)) || 
        (ObjZustand == PRGWARTEN)))
    return;

  if (ObjProgramm)
    if (!PrgModul->continueUserProg (ObjProgramm)) {
      ObjProgramm = NULL;
      if (MySFXModel->PrgDebug ()) dprintf ("Programm geloescht auf Bst = %d\n", ObjNummer);
    }
    else return;

  if (ObjBelegung)  return;
  typMeldung* zM = findeAusfuehrbareMeldung ();
  if (zM) {
    ObjTransportEinheit = zM->TransportEinheit ();
    ObjZielBaustein = zM->VonBaustein ();
    wartenaufnehmen (ObjTransportEinheit, ObjZielBaustein);
  }
}

/*virtual*/ typMeldung* typMaschine::findeAusfuehrbareMeldung () {
  return ObjFindeMeldungFunktion (this);
}

/*virtual*/ void typMaschine::wartenaufnehmen (typTransportEinheit* zTE, typBaustein* vonWem) {
  if (vonWem->isUebergabeBereit (zTE, this)) {
    setZustand (AKTIVIERT);
    planesofort (ETprogramm, ObjNummer, zTE->Vorgang ()->ProgNr ());
  }
  else {
    vonWem->UebergabeVorbereiten (zTE);
    setZustand (WARTEDP);
  }
}

/*static*/ typMeldung* typMaschine::findeFifoMeldung (typMaschine* who) { // Strategie 0
  typTransportEinheit* zBuch = who->ObjPalPlaetze [0]->Buchung ();
  for (LCursor<typMeldung> c (who->ObjMeldungen); c; c++) {
    typTransportEinheit* zTE = c ()->TransportEinheit ();
    if (!zBuch || (zTE == zBuch)) {
      if (!zBuch)  zTE->setZielPlatz (who->waehlePlatz (zTE));
      return &(who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (c))));
    }
  }
  return NULL;
}

/*static*/ typMeldung* typMaschine::findeKOZMeldung  (typMaschine* who) { // Strategie 1
  typTransportEinheit* zBuch = who->ObjPalPlaetze [0]->Buchung ();
  typMeldung* zM = NULL;
  typTransportEinheit* zTE;
  double minZeit = DBL_MAX;
  for (LCursor<typMeldung> c (who->ObjMeldungen); c; c++) {
    zTE = c ()->TransportEinheit ();
    if ((!zBuch || (zTE == zBuch))
        && zTE->Vorgang()->Dauer().inMinutes() < minZeit) {
      zM = c ();
      minZeit = zTE->Vorgang()->Dauer().inMinutes();
    }
  }
  if (zM) {
    zTE = zM->TransportEinheit ();
    if (!zBuch)  zTE->setZielPlatz (who->waehlePlatz (zTE));
    who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (*zM)));
  }
  return zM;
}

