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

#include <float.h>
#include <limits.h>

#include "MathDefs.h"
#include "Token.h"

#include "Vec3D.h"

#define _LAUFKRAN_CPP
#include "modell.h"
#include "laufkran.h"

#include "sfx.h"
#include "transp.h"
#include "baustein.h"
#include "ereignis.h"
#include "arbplan.h"
#include "route.h"
#include "realzeit.h"

//---------------------------------------------------------
// Klasse typLaufkran
//---------------------------------------------------------

typLaufkran::typLaufkran (unsigned n, unsigned b)
 : typBaustein            (n, b),

   //***** gewaehlte Vorgabewerte:
   ObjFahrGeschwindigkeit (60.0),  // in m/min !!
   ObjFahrBeschleunigung  (0.0),   // in m/min2 !! (war 7200)
   ObjFahrVerzoegerung    (0.0),   // in m/min2 !! (war 3600)
   ObjFahrweg             (0.0),
   ObjHubGeschwindigkeit  (0.0),   // in m/min !!  (war 30)
   ObjHubBeschleunigung   (0.0),   // in m/min2 !! (war 5040)
   ObjHubVerzoegerung     (0.0),   // in m/min2 !! (war 2520)
   ObjHubweg              (0.0),
   ObjQuerweg             (0.0),
   //*****Ende Vorgabewerte

   ObjLaufGeo             (NULL),
   ObjHakenGeo            (NULL),

   ObjFahrRichtung        (EX),
   ObjQuerRichtung        (EY),
   ObjZielKoord           (),
   ObjGabelKoord          (),

   ObjFindeMeldungFunktion (typLaufkran::findeFifoMeldung)
 { }

typLaufkran::~typLaufkran () {
  if (ObjHakenGeo) delete ObjHakenGeo;
  if (ObjLaufGeo)  delete ObjLaufGeo;
}

/*virtual*/ void typLaufkran::typTGSlesen (InputFile& file) {
  typBaustein::typTGSlesen (file);
  ObjFahrRichtung = ObjGeometry.DirToSpace (ObjFahrRichtung).unit ();
  ObjQuerRichtung = ObjGeometry.DirToSpace (ObjQuerRichtung).unit ();
  TGBPermPattern* myTGBPattern = MySFXModel->TGBLibrary ()->findPerm (ObjBasistyp, ObjVariante);
  const DynArray<GeoObject>& go (myTGBPattern->AllGeometries ());
  go.First ();
  if (go.Next ()) {
    ObjLaufGeo = new GeoObject ();
    ObjLaufGeo->copyContents (*(go ()));
    ObjLaufGeo->setParent (ObjGeometry);
    ObjLaufGeo->setSelectionToMum ();
    Vec3D v (ObjPalPlaetze[0]->Position ().X (), ObjPalPlaetze[0]->Position ().Y (), ObjGeometry.Pos ().Z ());
    ObjLaufGeo->setPos (ObjGeometry.PtToLocal (v), 0);
    if (go.Next ()) {
      ObjHakenGeo = new GeoObject ();
      ObjHakenGeo->copyContents (*(go ()));
      ObjHakenGeo->setParent (*ObjLaufGeo);
      ObjHakenGeo->setSelectionToMum ();
      v = Vec3D (ObjPalPlaetze[0]->Position ());
      ObjHakenGeo->setPos (ObjLaufGeo->PtToLocal (v), 0);
    }
  }

  if (ObjPalPlaetze.Count () != 1)
    sim_error ("LAUFKRAN", "typLaufkran::typTGSlesen",
               "Laufkranvariante mit != 1 Palettenplatz", ObjVariante, FATAL);
}

/*virtual*/ void typLaufkran::typTTPlesen (InputFile& file) {
  SFXToken tok;

  tok.nextToken (file);
  ObjAufnahmezeit = SimTime (atof (tok ()) * SimTime::SecondsPerMinute ());

  tok.nextToken (file);
  ObjFahrGeschwindigkeit = atof (tok ());

  file.jump (200); //EOL

  ObjHubGeschwindigkeit = ObjFahrGeschwindigkeit / 2;   //**Vorgabewert!
  ObjPhysKapazitaet = ObjKapazitaet = 1;
  ObjGabelKoord = ObjPalPlaetze [0]->Position ();
}

/*virtual*/ ostream& typLaufkran::typTTPschreiben (ostream& ostrm) const {
  ostrm.precision (2);
  ostrm << "TTP> ";
  Id_Schreiben (ostrm);
  ostrm << " ";
  ostrm.width (6);
  ostrm << ObjAufnahmezeit.inMinutes () << " ";
  ostrm.width (6);
  ostrm << ObjFahrGeschwindigkeit << " ";
  ostrm.width (6);
  ostrm << ObjHubGeschwindigkeit << endl;
  return ostrm;
}

/*virtual*/ void typLaufkran::typTBVlesen (InputFile& file) {
  typBaustein::typTBVlesen (file);

  if ((ObjZielBaustein->Basistyp () == tVerteilwagen)
      || (ObjZielBaustein->Basistyp () == tLaufkran))
    sim_error ("LAUFKRAN", "typLaufkran::typTBVlesen",
               "Veknuepfung mit Verteilwagen oder Laufkran nicht erlaubt", ObjNummer, FATAL);
}

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

/*virtual*/ unsigned typLaufkran::setStrategie (unsigned stratNr) {
  ObjStrategie = stratNr;
  switch (ObjStrategie) {
    case 0:  ObjFindeMeldungFunktion = typLaufkran::findeFifoMeldung; break;
    case 1:  ObjFindeMeldungFunktion = typLaufkran::findeKurzwegMeldung; break;
    case 2:  ObjFindeMeldungFunktion = typLaufkran::findeWartezeitMeldung; break;
    case 3:  ObjFindeMeldungFunktion = typLaufkran::findeMinAvisIstMeldung; break;
    case 4:  ObjFindeMeldungFunktion = typLaufkran::findeMitnahmeMeldung; break;
//    case 5:  ObjFindeMeldungFunktion = typLaufkran::findeTramMeldung; break;
    default: sim_error ("LAUFKRAN", "typLaufkran::setStrategie",
                        "Strategie ist nicht vorhanden, setzte FIFO",
                         ObjNummer, WARNING);
             ObjFindeMeldungFunktion = typLaufkran::findeFifoMeldung; break;
  }
  return ObjStrategie;
}

/*virtual*/ void typLaufkran::a_aufnehmen () {
  typMeldung *zM = holeMeldung ();
  ObjZielBaustein->gibTransportEinheit (zM);
  aufnehmenEinleiten (ObjGabelKoord, ObjAufnahmezeit);
}

/*virtual*/  void typLaufkran::e_aufnehmen () {
  typPlatz *p (ObjTransportEinheit->AktPlatz ());
  if (p->Buchung () != ObjTransportEinheit)
    sim_error ("LAUFKRAN", "typLaufkran::e_aufnehmen",
               "TransportEinheit ist nicht auf Platz gebucht", ObjNummer, FATAL);
  p->setBuchung (NULL);
  ObjBuchung--;
  ObjTransportEinheit->positionieren (ObjGabelKoord);
//  zTransportEinheit->faerbeGestalt (default);

  ObjZielBaustein = ObjTransportEinheit->nextBaustein ();
  if (!ObjTransportEinheit->ZielPlatz ())
    ObjTransportEinheit->setZielPlatz (ObjZielBaustein->waehlePlatz (ObjTransportEinheit));
  ObjZielKoord = ObjTransportEinheit->ZielPlatz ()->Position ();

  if (ObjStoerDauer > SimTime (0.0))
    planesofort (ETastoerung, ObjNummer);
  else {
    setZustand (AKTIVIERT);
    if (ObjZielKoord != ObjGabelKoord){
    	if ((ObjGabelKoord.X () == ObjZielKoord.X ()) && (ObjGabelKoord.Y () == ObjZielKoord.Y ()) && (ObjGabelKoord.Z () != ObjZielKoord.Z ()))
          	{
          		planesofort (ETasenken, ObjNummer, ObjZielBaustein->Nummer ());
          	}
          	else if (ObjGabelKoord.Z () != ObjPalPlaetze [0]->Position ().Z ())
          	{
          		planesofort (ETaheben, ObjNummer, ObjZielBaustein->Nummer ());
          	}
          	else
          	{
          		planesofort (ETafahren, ObjNummer, ObjZielBaustein->Nummer ());
          	}
   }
    else  planesofort (ETameldewarten, ObjNummer, 0);
  }
  planesofort (ETeabgeben, ObjTransportEinheit->Meldung ()->VonBaustein ()->Nummer (), ObjNummer);
}

/*virtual*/ void typLaufkran::fahr_takt () {
  if (ObjZustand != FAHREN) return;
  if (ObjTaktSchritte > 0) {
    ObjTaktSchritte--;

    double d;
    Vec3D v;
    if (ObjFahrweg) {
      d = ObjTaktZeit.inMinutes () * ObjFahrGeschwindigkeit;
      if (fabs (ObjFahrweg) > d)  ObjFahrweg -= (d *= Sign (ObjFahrweg));
      else  {
        d = ObjFahrweg;
        ObjFahrweg = 0.0;
      }

      v = Vec3D (ObjFahrRichtung * d);
      bewegen (v);
      ObjGabelKoord += v;
    }

    if (ObjQuerweg) {
      d = ObjTaktZeit.inMinutes () * ObjFahrGeschwindigkeit;
      if (fabs (ObjQuerweg) > d)  ObjQuerweg -= (d *= Sign (ObjQuerweg));
      else  {
        d = ObjQuerweg;
        ObjQuerweg = 0.0;
      }
      v = Vec3D (ObjQuerRichtung * d);
      ObjGabelKoord += v;
      if (ObjLaufGeo) ObjLaufGeo->changePos (ObjGeometry.DirToLocal (v) , 0);
    }

    if (ObjHubweg) {
      d = ObjTaktZeit.inMinutes () * ObjHubGeschwindigkeit;
      if (fabs (ObjHubweg) > d)  ObjHubweg -= (d *= Sign (ObjHubweg));
      else  {
        d = ObjHubweg;
        ObjHubweg = 0.0;
      }
      v = Vec3D (0.0, 0.0, d);
      ObjGabelKoord += v;
      if (ObjHakenGeo) ObjHakenGeo->move (v, 0);
    }

    if (ObjPalPlaetze [0]->TransportEinheit ())
      ObjTransportEinheit->positionieren (ObjGabelKoord);
    if (ObjTaktSchritte)
      planedelta (ObjTaktZeit, ETfahrTakt, ObjNummer,
                  ObjZielBaustein ? ObjZielBaustein->Nummer () : ObjNummer, ObjTaktSchritte);
  }
}

/*virtual*/ void typLaufkran::a_heben () {
  ObjFahrweg = 0.0;
  ObjQuerweg = 0.0;
  ObjHubweg = ObjPalPlaetze [0]->Position ().Z () - ObjGabelKoord.Z ();
  double fahrZeit = Zeit_ermitteln (fabs (ObjHubweg), ObjHubGeschwindigkeit,
                                    ObjHubBeschleunigung, ObjHubVerzoegerung);
  setZustand (FAHREN);
//  faerbeGestalt (SimFenster->HigFarbe ());
  planedelta (fahrZeit, ETeheben, ObjNummer, 
              ObjZielBaustein ? ObjZielBaustein->Nummer () : ObjNummer);
  if (MySFXModel->Cycle ()) {
    ObjTaktZeit = (ClassFahrSchritt * SimTime::SecondsPerMinute ()) / 
                   ObjHubGeschwindigkeit;
    ObjTaktSchritte = (unsigned) ((fahrZeit - (ObjTaktZeit.inMinutes () / ClassTaktFaktor)) / ObjTaktZeit.inMinutes ());
    if (ObjTaktSchritte > 0)  fahr_takt ();
  }
  if (((!MySFXModel->Cycle ()) || (ObjTaktSchritte == 0)) && (ObjHubweg != 0.0)) {
    ObjGabelKoord = Vec3D (ObjGabelKoord.X (), ObjGabelKoord.Y (), ObjPalPlaetze [0]->Position ().Z ());
    Vec3D v (ObjGabelKoord);
    if (ObjHakenGeo) ObjHakenGeo->setPos (ObjLaufGeo->PtToLocal (v), 0);
    if (ObjPalPlaetze [0]->TransportEinheit ())  ObjTransportEinheit->positionieren (ObjGabelKoord);
  }
}

/*virtual*/ void typLaufkran::e_heben () {
  if (ObjGabelKoord.Z () != ObjPalPlaetze [0]->Position ().Z ()) {
    ObjGabelKoord = Vec3D (ObjGabelKoord.X (), ObjGabelKoord.Y (), ObjPalPlaetze [0]->Position ().Z ());
    Vec3D v (ObjGabelKoord);
    if (ObjHakenGeo) ObjHakenGeo->setPos (ObjLaufGeo->PtToLocal (v), 0);
    if (ObjPalPlaetze [0]->TransportEinheit ())  ObjTransportEinheit->positionieren (ObjGabelKoord);
  }
  if (ObjZielBaustein) {
    if (ObjZielKoord != ObjGabelKoord)  
      planesofort (ETafahren, ObjNummer, ObjZielBaustein->Nummer ());
    else {
      setZustand (AKTIVIERT);
      if (ObjPalPlaetze [0]->TransportEinheit () && ObjTransportEinheit->ZielPlatz ())  {
        ObjZielBaustein = NULL;
        planesofort (ETameldewarten, ObjNummer, 0);
      }
      else planesofort (ETaktivieren, ObjNummer, ObjNummer);
    }
  }
  else {
    setZustand (RUHE);
    planesofort (ETaktivieren, ObjNummer, ObjNummer);
  }
}

/*virtual*/ void typLaufkran::a_fahren () {
  ObjHubweg = 0.0;
  Vec3D delta (ObjZielKoord - ObjGabelKoord);
  Vec3D v (delta.projOn (ObjFahrRichtung));
  ObjFahrweg = v.len ();
  if (!v.hasSameDir (ObjFahrRichtung))  ObjFahrweg *= -1;
  v = delta.projOn (ObjQuerRichtung);
  ObjQuerweg = v.len ();
  if (!v.hasSameDir (ObjQuerRichtung))  ObjQuerweg *= -1;

  double fahrZeit = Zeit_ermitteln (fabs (ObjFahrweg), ObjFahrGeschwindigkeit,
                                    ObjFahrBeschleunigung, ObjFahrVerzoegerung);
  double querZeit = Zeit_ermitteln (fabs (ObjQuerweg), ObjFahrGeschwindigkeit,
                                    ObjFahrBeschleunigung, ObjFahrVerzoegerung);
  double zeit = (querZeit > fahrZeit) ? querZeit : fahrZeit;

  setZustand (FAHREN);
//  faerbeGestalt (SimFenster->HigFarbe ());
  planedelta (zeit, ETefahren, ObjNummer, ObjZielBaustein->Nummer ());
  if (MySFXModel->Cycle ()) {
    ObjTaktZeit = (ClassFahrSchritt * SimTime::SecondsPerMinute ()) / ObjFahrGeschwindigkeit;
    ObjTaktSchritte = (unsigned) ((zeit - (ObjTaktZeit.inMinutes () / ClassTaktFaktor)) / ObjTaktZeit.inMinutes ());
    if (ObjTaktSchritte > 0)  fahr_takt ();
  }
  if (((!MySFXModel->Cycle ()) || (ObjTaktSchritte == 0)) &&  (ObjQuerweg || ObjFahrweg)) {
    delta = (ObjZielKoord - ObjGabelKoord);
    positionieren (ObjGeometry.Pos () + delta.projOn (ObjFahrRichtung));
    ObjGabelKoord = Vec3D (ObjZielKoord.X (), ObjZielKoord.Y (), ObjPalPlaetze [0]->Position ().Z ());
    if (ObjLaufGeo)
      ObjLaufGeo->setPos (ObjLaufGeo->Pos () + ObjGeometry.DirToLocal ((v = delta.projOn (ObjQuerRichtung))), 0);
    if (ObjPalPlaetze [0]->TransportEinheit ())  ObjTransportEinheit->positionieren (ObjGabelKoord);
  }
}

/*virtual*/ void typLaufkran::e_fahren() {
  Vec3D v (ObjZielKoord.X (), ObjZielKoord.Y (), ObjPalPlaetze [0]->Position ().Z ());
  if (ObjGabelKoord != v) {
    Vec3D delta (ObjZielKoord - ObjGabelKoord);
    positionieren (ObjGeometry.Pos () + delta.projOn (ObjFahrRichtung));
    ObjGabelKoord = v;
    if (ObjLaufGeo)
      ObjLaufGeo->setPos (ObjLaufGeo->Pos () + ObjGeometry.DirToLocal ((v = delta.projOn (ObjQuerRichtung))), 0);
    if (ObjPalPlaetze [0]->TransportEinheit ())  ObjTransportEinheit->positionieren (ObjGabelKoord);
  }

  if (ObjStoerDauer > SimTime (0.0))
    planesofort (ETastoerung, ObjNummer);
  else {
    if (ObjZielKoord != ObjGabelKoord)  
      planesofort (ETasenken, ObjNummer, ObjZielBaustein->Nummer ());
    else {
      if (ObjPalPlaetze [0]->TransportEinheit () && 
          ObjTransportEinheit->ZielPlatz ())  {
        setZustand (AKTIVIERT);
        ObjZielBaustein = NULL;
        planesofort (ETameldewarten, ObjNummer, 0);
      }
      else {
        setZustand (RUHE);
        planesofort (ETaktivieren, ObjNummer, ObjNummer);
      }
    }
  }
}

/*virtual*/ void typLaufkran::a_senken () {
  ObjFahrweg = 0.0;
  ObjQuerweg = 0.0;
  ObjHubweg = ObjZielKoord.Z () - ObjGabelKoord.Z ();
  double fahrZeit = Zeit_ermitteln (fabs (ObjHubweg), ObjHubGeschwindigkeit,
                                    ObjHubBeschleunigung, ObjHubVerzoegerung);
  setZustand (FAHREN);

//  faerbeGestalt (SimFenster->HigFarbe ());
  planedelta (fahrZeit, ETesenken, ObjNummer, ObjZielBaustein->Nummer ());
  if (MySFXModel->Cycle ()) {
    ObjTaktZeit = (ClassFahrSchritt * SimTime::SecondsPerMinute ()) / ObjHubGeschwindigkeit;
    ObjTaktSchritte = (unsigned) ((fahrZeit - (ObjTaktZeit.inMinutes () / ClassTaktFaktor)) / ObjTaktZeit.inMinutes ());
    if (ObjTaktSchritte > 0)  fahr_takt ();
  }
  if (((!MySFXModel->Cycle ()) || (ObjTaktSchritte == 0)) &&  (ObjHubweg != 0.0)) {
    ObjGabelKoord = ObjZielKoord;
    Vec3D v (ObjGabelKoord);
    if (ObjHakenGeo) ObjHakenGeo->setPos (ObjLaufGeo->PtToLocal (v), 0);
    if (ObjPalPlaetze [0]->TransportEinheit ())  ObjTransportEinheit->positionieren (ObjGabelKoord);
  }
}

/*virtual*/ void typLaufkran::e_senken () {
  if (ObjZielKoord != ObjGabelKoord) {
    ObjGabelKoord = ObjZielKoord;
    Vec3D v (ObjGabelKoord);
    if (ObjHakenGeo) ObjHakenGeo->setPos (ObjLaufGeo->PtToLocal (v), 0);
    if (ObjPalPlaetze [0]->TransportEinheit ())  ObjTransportEinheit->positionieren (ObjZielKoord);
  }
//  faerbeGestalt (SimFenster->BstFarbe ());
  if (ObjPalPlaetze [0]->TransportEinheit () && ObjTransportEinheit->ZielPlatz ())  {
    setZustand (AKTIVIERT);
    ObjZielBaustein = NULL;
    planesofort (ETameldewarten, ObjNummer, 0);
  }
  else {
    setZustand (RUHE);
    planesofort (ETaktivieren, ObjNummer, ObjNummer);
  }
}

/*virtual*/ void typLaufkran::e_abgeben () {
  setZustand (RUHE);
  if (ObjMeldungen.isEmpty ())
    planesofort (ETaheben, ObjNummer, ObjNummer);
  else
    planesofort (ETaktivieren, ObjNummer, ObjNummer);
  aktiviereEingaenge ();
}

/*virtual*/ void typLaufkran::eStoeren () {
  ObjStoerEnde = NULL;
  if (ObjStoerGeschichte) ObjStoerGeschichte->erfassen (0.0);
  switch (ObjAlterZustand) {
    case AUFNEHMEN :  setZustand (AKTIVIERT);
                      if (ObjZielKoord != ObjGabelKoord)
                      {
                        if ((ObjGabelKoord.X () == ObjZielKoord.X ()) && (ObjGabelKoord.Y () == ObjZielKoord.Y ()) && (ObjGabelKoord.Z () != ObjZielKoord.Z ()))
          	{
          		planesofort (ETasenken, ObjNummer, ObjZielBaustein->Nummer ());
          	}
          	else if (ObjGabelKoord.Z () != ObjPalPlaetze [0]->Position ().Z ())
          	{
          		planesofort (ETaheben, ObjNummer, ObjZielBaustein->Nummer ());
          	}
          	else
          	{
          		planesofort (ETafahren, ObjNummer, ObjZielBaustein->Nummer ());
          	}
                      }
                      else
                        planesofort (ETameldewarten, ObjNummer, 0);
                      break;

    case FAHREN    :  setZustand (AKTIVIERT);
                      if (ObjZielKoord != ObjGabelKoord)
                      {
                        if ((ObjGabelKoord.X () == ObjZielKoord.X ()) && (ObjGabelKoord.Y () == ObjZielKoord.Y ()) && (ObjGabelKoord.Z () != ObjZielKoord.Z ()))
          	{
          		planesofort (ETasenken, ObjNummer, ObjZielBaustein->Nummer ());
          	}
          	else if (ObjGabelKoord.Z () != ObjPalPlaetze [0]->Position ().Z ())
          	{
          		planesofort (ETaheben, ObjNummer, ObjZielBaustein->Nummer ());
          	}
          	else
          	{
          		planesofort (ETafahren, ObjNummer, ObjZielBaustein->Nummer ());
          	}
                      }
                      else {
                        if (ObjPalPlaetze [0]->TransportEinheit () && 
                            ObjTransportEinheit->ZielPlatz ())  {
                          ObjZielBaustein = NULL;
                          planesofort (ETameldewarten, ObjNummer, 0);
                        }
                        else {
                          setZustand (RUHE);
                          planesofort (ETaktivieren, ObjNummer, ObjNummer);
                        }
                      }
                      break;

    default        :  setZustand (ObjAlterZustand); // RUHE or POLLING!!!
                      planesofort (ETaktivieren, ObjNummer, ObjNummer);
                      aktiviereEingaenge ();
                      aktiviereAusgaenge ();
                      break;
  }
}

/*virtual*/ bool typLaufkran::ausrichtenAufBaustein (typBaustein* zielBst) {
  if ((ObjZustand == RUHE) || (ObjZustand == POLLING)) {
    ObjZielBaustein = zielBst;
    ObjZielKoord = UebernahmePos (*ObjZielBaustein);
    if (ObjZielKoord != ObjGabelKoord) {
      setZustand (AKTIVIERT);
      if ((ObjGabelKoord.X () == ObjZielKoord.X ()) && (ObjGabelKoord.Y () == ObjZielKoord.Y ()) && (ObjGabelKoord.Z () != ObjZielKoord.Z ()))
          	{
          		planesofort (ETasenken, ObjNummer, ObjZielBaustein->Nummer ());
          	}
          	else if (ObjGabelKoord.Z () != ObjPalPlaetze [0]->Position ().Z ())
          	{
          		planesofort (ETaheben, ObjNummer, ObjZielBaustein->Nummer ());
          	}
          	else
          	{
          		planesofort (ETafahren, ObjNummer, ObjZielBaustein->Nummer ());
          	}
    }
    return true;
  }
  return false;
}

/*virtual*/ bool typLaufkran::hasKapazitaet (typTransportEinheit*) const {
  return (ObjBelegung + ObjBuchung) < ObjKapazitaet;
}

/*virtual*/ typPlatz* typLaufkran::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 typLaufkran::UebernahmePos (const typBaustein& von) const {
  return von.UebergabePos (*this);
}

/*virtual*/ Vec3D typLaufkran::UebergabePos (const typBaustein& nach) const {
  return nach.UebernahmePos (*this);
}

/*virtual*/ const double& typLaufkran::setGeschwindigkeit (const double& wert) {
  if (wert <= 0.0)
    sim_error ("LAUFKRAN", "typLaufkran::setGeschwindigkeit",
               "negative Fahrgeschwindigkeit", ObjNummer, FATAL);
  return ObjFahrGeschwindigkeit = wert;
}

const double& typLaufkran::setFahrBeschleunigung (const double& wert) {
  if (wert <= 0.0)
    sim_error ("LAUFKRAN", "typLaufkran::setFahrBeschleunigung",
               "negative Fahrbeschleunigung", ObjNummer, FATAL);
  return ObjFahrBeschleunigung = wert;
}

const double& typLaufkran::setFahrVerzoegerung (const double& wert) {
  if (wert <= 0.0)
    sim_error ("LAUFKRAN", "typLaufkran::setFahrVerzoegerung",
               "negative Fahrverzoegerung", ObjNummer, FATAL);
  return (ObjFahrVerzoegerung = wert);
}

const double& typLaufkran::setHubGeschwindigkeit (const double& wert) {
  if (wert <= 0.0)
    sim_error ("LAUFKRAN", "typLaufkran::setHubGeschwindigkeit",
               "negative Hubgeschwindigkeit", ObjNummer, FATAL);
  return ObjHubGeschwindigkeit = wert;
}

const double& typLaufkran::setHubBeschleunigung (const double& wert) {
  if (wert <= 0.0)
    sim_error ("LAUFKRAN", "typLaufkran::setHubBeschleunigung",
               "negative Hubbeschleunigung", ObjNummer, FATAL);
  return ObjHubBeschleunigung = wert;
}

const double& typLaufkran::setHubVerzoegerung (const double& wert) {
  if (wert <= 0.0)
    sim_error ("LAUFKRAN", "typLaufkran::setHubVerzoegerung",
               "negative Hubverzoegerung", ObjNummer, FATAL);
  return (ObjHubVerzoegerung = wert);
}

/*virtual*/ void typLaufkran::individuellAktivieren (typBaustein* vonWem) {
  if ((ObjZustand == RUHE) || (ObjZustand == POLLING) || (vonWem == this)
      || ((ObjZustand == WARTEDP) && (ObjZielBaustein == vonWem))) {
    if (ObjPalPlaetze [0]->TransportEinheit ())  {
      if (!ObjTransportEinheit->ZielPlatz ())
        if (ObjTransportEinheit->setZielPlatz (ObjZielBaustein->waehlePlatz (ObjTransportEinheit))) {
          ObjZielKoord = ObjTransportEinheit->AktPlatz ()->Position ();
          if (ObjZielKoord != ObjGabelKoord)
          {
          	if ((ObjGabelKoord.X () == ObjZielKoord.X ()) && (ObjGabelKoord.Y () == ObjZielKoord.Y ()) && (ObjGabelKoord.Z () != ObjZielKoord.Z ()))
          	{
          		planesofort (ETasenken, ObjNummer, ObjZielBaustein->Nummer ());
          	}
          	else if (ObjGabelKoord.Z () != ObjPalPlaetze [0]->Position ().Z ())
          	{
          		planesofort (ETaheben, ObjNummer, ObjZielBaustein->Nummer ());
          	}
          	else
          	{
          		planesofort (ETafahren, ObjNummer, ObjZielBaustein->Nummer ());
          	}
          }
          else  planesofort (ETameldewarten, ObjNummer, 0);
        }
        else  planesofort (ETameldewarten, ObjNummer, 0);
      return;
    }
    else setZustand (RUHE);

    typMeldung* zM = findeAusfuehrbareMeldung ();
    if (zM) {
      ObjTransportEinheit = zM->TransportEinheit ();
      ObjZielBaustein = zM->VonBaustein ();
      ObjZielKoord = ObjTransportEinheit->AktPlatz ()->Position ();
      if (ObjZielKoord != ObjGabelKoord) {
        setZustand (AKTIVIERT);
        if ((ObjGabelKoord.X () == ObjZielKoord.X ()) && (ObjGabelKoord.Y () == ObjZielKoord.Y ()) && (ObjGabelKoord.Z () != ObjZielKoord.Z ()))
        {
         	planesofort (ETasenken, ObjNummer, ObjZielBaustein->Nummer ());
        }
        else if (ObjGabelKoord.Z () != ObjPalPlaetze [0]->Position ().Z ())
        {
          	planesofort (ETaheben, ObjNummer, ObjZielBaustein->Nummer ());
        }
        else
        {
          	planesofort (ETafahren, ObjNummer, ObjZielBaustein->Nummer ());
        }
      }
      else  wartenaufnehmen (ObjTransportEinheit, ObjZielBaustein);
    }
    else
      if (ObjEingaenge.Count () == 1) {
        ObjZielBaustein = ObjEingaenge.First ();
        Vec3D ziel = ObjZielBaustein->Position ();
        ObjZielKoord = Vec3D (ziel.X (), ziel.Y (), 
                         ObjPalPlaetze [0]->Position ().Z ());
        if (ObjZielKoord != ObjGabelKoord) {
          setZustand (AKTIVIERT);
          if ((ObjGabelKoord.X () == ObjZielKoord.X ()) && (ObjGabelKoord.Y () == ObjZielKoord.Y ()) && (ObjGabelKoord.Z () != ObjZielKoord.Z ()))
          	{
          		planesofort (ETasenken, ObjNummer, ObjZielBaustein->Nummer ());
          	}
          	else if (ObjGabelKoord.Z () != ObjPalPlaetze [0]->Position ().Z ())
          	{
          		planesofort (ETaheben, ObjNummer, ObjZielBaustein->Nummer ());
          	}
          	else
          	{
          		planesofort (ETafahren, ObjNummer, ObjZielBaustein->Nummer ());
          	}
        }
        else if (!(ObjMeldungen.isEmpty ())) setZustand (POLLING);
        else setZustand (RUHE);
      }
      else
        if (!(ObjMeldungen.isEmpty ())) setZustand (POLLING);
        else setZustand (RUHE);
  }
}

typMeldung* typLaufkran::findeAusfuehrbareMeldung () {
  return ObjFindeMeldungFunktion (this);
}

/*static*/  typMeldung* typLaufkran::findeFifoMeldung (typLaufkran* who) {
  typTransportEinheit* zBuch = who->ObjPalPlaetze [0]->Buchung ();
  for (LCursor<typMeldung> c (who->ObjMeldungen); c; c++) {
    typTransportEinheit* zTE = c ()->TransportEinheit ();
    if (!zBuch || (zTE == zBuch)) {
      if (!zTE->BuchPlatz ()) {
        typBaustein* zNext = zTE->nextBaustein ();
        if (zNext->hasKapazitaet (zTE))  zTE->setBuchPlatz (zNext->waehlePlatz (zTE));
      }
      if (((who->ObjEingaenge.Count () > 1) || (who->ObjAusgaenge.Count () > 1)) && (!zTE->BuchPlatz ())) continue;
      if (!zBuch)  zTE->setZielPlatz (who->waehlePlatz (zTE));
      return &(who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (c))));
    }
  }
  return NULL;
}

/*static*/ typMeldung* typLaufkran::findeKurzwegMeldung (typLaufkran* who) {
  typTransportEinheit* zBuch = who->ObjPalPlaetze [0]->Buchung ();
  typMeldung* zM;

  for (unsigned i = who->ObjMeldungen.Count (); i; i--) {
    Vec3D min (DBL_MAX, DBL_MAX, DBL_MAX);
    LCursor<typMeldung> c (who->ObjMeldungen, 1); // toEnd
    for (unsigned j = 1; j <= i; c--, j++) {
      Vec3D v (c ()->TransportEinheit ()->AktPlatz ()->Position () - who->ObjGabelKoord);
      if (v.len () < min.len ()) {
        min = v;
        zM = c ();
      }
    }
    typTransportEinheit* zTE = zM->TransportEinheit ();
    if (!zBuch || (zTE == zBuch)) {
      if (!zTE->BuchPlatz ()) {
        typBaustein* zNext = zTE->nextBaustein ();
        if (zNext->hasKapazitaet (zTE))  zTE->setBuchPlatz (zNext->waehlePlatz (zTE));
      }
      if (((who->ObjEingaenge.Count () > 1) || (who->ObjAusgaenge.Count () > 1)) && (!zTE->BuchPlatz ())) continue;
      if (!zBuch)  zTE->setZielPlatz (who->waehlePlatz (zTE));
      return &(who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (*zM))));
    }
    who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (*zM)));
  }
  return NULL;
}

/*static*/ typMeldung* typLaufkran::findeWartezeitMeldung (typLaufkran* who) {
  typTransportEinheit* zBuch = who->ObjPalPlaetze [0]->Buchung ();
  typMeldung* zM = NULL;

  for (unsigned i = who->ObjMeldungen.Count (); i; i--) {
    double maxWarteZeit = -1.0;
    LCursor<typMeldung> c (who->ObjMeldungen, 1); // toEnd
    for (unsigned j = 1; j <= i; c--, j++) {
      typBaustein* bst = c ()->VonBaustein ();
      double aktWarteZeit = bst->ZustandZeit ();
      if (bst->Zustand () == WARTEN &&
          aktWarteZeit > who->ObjWartezeitSchwelle &&
          aktWarteZeit > maxWarteZeit) {
        maxWarteZeit = aktWarteZeit;
        zM = c ();
      }
    }
    if (zM) {
      typTransportEinheit* zTE = zM->TransportEinheit ();
      if (!zBuch || (zTE == zBuch)) {
        if (!zTE->BuchPlatz ()) {
          typBaustein* zNext = zTE->nextBaustein ();
          if (zNext->hasKapazitaet (zTE))  zTE->setBuchPlatz (zNext->waehlePlatz (zTE));
        }
        if (((who->ObjEingaenge.Count () > 1) || (who->ObjAusgaenge.Count () > 1)) && (!zTE->BuchPlatz ())) continue;
        if (!zBuch)  zTE->setZielPlatz (who->waehlePlatz (zTE));
        return &(who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (*zM))));
      }
      who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (*zM)));
    }
    else break;
  }
  return typLaufkran::findeFifoMeldung (who);
}

/*static*/ typMeldung* typLaufkran::findeMinAvisIstMeldung (typLaufkran* who) {
  typTransportEinheit* zBuch = who->ObjPalPlaetze [0]->Buchung ();
  typMeldung* zM = NULL;

  for (unsigned i = who->ObjMeldungen.Count (); i; i--) {
    int minAvis = INT_MAX;
    LCursor<typMeldung> c (who->ObjMeldungen, 1); // toEnd
    for (unsigned j = 1; j <= i; c--, j++) {
      int aktAvis = c ()->TransportEinheit ()->Vorgang ()->ZielBaustein ()->Avis ()->DirIstAnzahl ();
      if (aktAvis < minAvis) {
        minAvis = aktAvis;
        zM = c ();
      }
    }
    typTransportEinheit* zTE = zM->TransportEinheit ();
    if (!zBuch || (zTE == zBuch)) {
      if (!zTE->BuchPlatz ()) {
        typBaustein* zNext = zTE->nextBaustein ();
        if (zNext->hasKapazitaet (zTE))  zTE->setBuchPlatz (zNext->waehlePlatz (zTE));
      }
      if (((who->ObjEingaenge.Count () > 1) || (who->ObjAusgaenge.Count () > 1)) && (!zTE->BuchPlatz ())) continue;
      if (!zBuch)  zTE->setZielPlatz (who->waehlePlatz (zTE));
      return &(who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (*zM))));
    }
    who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (*zM)));
  }
  return NULL;
}

/*static*/ typMeldung* typLaufkran::findeMitnahmeMeldung (typLaufkran* who) {
  typTransportEinheit* zBuch = who->ObjPalPlaetze [0]->Buchung ();
  typMeldung* fifoMldg = NULL;
  for (LCursor<typMeldung> c (who->ObjMeldungen); c; c++) {
    typTransportEinheit* zTE = c ()->TransportEinheit ();
    if (!zBuch || (zTE == zBuch)) {
      if (((who->ObjEingaenge.Count () > 1) || (who->ObjAusgaenge.Count () > 1))
          && (!zTE->BuchPlatz ()) && (!zTE->nextBaustein ()->hasKapazitaet (zTE))) continue;
      fifoMldg = &(who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (c))));
      break;
    }
  }

  if (fifoMldg) {
    double minAbstand = DBL_MAX;
    Vec3D fifoDir (fifoMldg->TransportEinheit ()->AktPlatz ()->Position () - who->ObjGabelKoord);
    fifoDir = fifoDir.projOn (who->ObjFahrRichtung);
    LCursor<typMeldung> mitnahme;
    LCursor<typMeldung> c (who->ObjMeldungen);
    for (c++; c; c++) {
      typTransportEinheit* zTE = c ()->TransportEinheit ();
      typBaustein* zNext = zTE->nextBaustein ();
      if ((zBuch || (zTE != zBuch))    // kann dieses TransportEinheit ueberhaupt befoerdert werden????
          && ((who->ObjEingaenge.Count () > 1) || (who->ObjAusgaenge.Count () > 1))
          && (!zTE->BuchPlatz ()) && (!zTE->nextBaustein ()->hasKapazitaet (zTE)))
        continue;
      else {
        Vec3D targetDir (zNext->UebernahmePos (*who) - who->ObjGabelKoord);
        targetDir = targetDir.projOn (who->ObjFahrRichtung);
        if ((targetDir.hasSameDir (fifoDir)) && (targetDir.len () <= fifoDir.len ())) {
          Vec3D fetchDir (zTE->AktPlatz ()->Position () - who->ObjGabelKoord);
          fetchDir = fetchDir.projOn (who->ObjFahrRichtung);
          double aktAbstand = fetchDir.len ();
          if (fetchDir.hasSameDir (fifoDir)
              && (aktAbstand < fifoDir.len ())
              && (aktAbstand < minAbstand)) {
                mitnahme = c;
                minAbstand = aktAbstand;
          }
        }
      }
    }
    if (mitnahme ()) {
      typTransportEinheit* mitnahmeTreffer = mitnahme ()->TransportEinheit ();
      typBaustein* mitnahmeZiel = mitnahmeTreffer->nextBaustein ();
      if (!mitnahmeTreffer->BuchPlatz ())
        mitnahmeTreffer->setBuchPlatz (mitnahmeZiel->waehlePlatz (mitnahmeTreffer));
      if (!zBuch)  mitnahmeTreffer->setZielPlatz (who->waehlePlatz (mitnahmeTreffer));
      return &(who->ObjMeldungen.prepend (*(who->ObjMeldungen.remove (mitnahme))));
    }
  }
  return typLaufkran::findeFifoMeldung (who);
}

/*static*/ typMeldung* typLaufkran::findeTramMeldung (typLaufkran* /*who*/) {
  return NULL;
}

double typLaufkran::Zeit_ermitteln (const double& weg, const double& geschw,
                                  const double& beschl, const double& verz) const {
  if (beschl && verz) {
    double f = (1 / beschl) + (1 / verz);  // Rechenfaktor f. Beschleunigungen
    double v = sqrt (2 * weg / f);         // erreichbare Geschwindigkeit
    if (v > geschw)                        // Trapezrechnung
      return (geschw * f) + ((weg - (geschw * geschw / 2 * f)) / geschw);
    else
      return v * f;                     // Dreieckrechnung
  }
  else
    return weg / geschw;                // Rechteckrechnung
}
