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

#include "Kalender.h"
#include "DynArray.h"
#include "Queue.h"

#define _KOMM_CPP
#include "modell.h"
#include "komm.h"

#include "sfx.h"
#include "arbplan.h"
#include "ereignis.h"
#include "realzeit.h"


//-----------------------------------------------------
//   struct EingangsDaten
//-----------------------------------------------------

struct EingangsDaten {
  EingangsDaten (typStlElement* zSE);
  ~EingangsDaten () {};
  unsigned ArtikelNr;
  double Menge;
  SimTime Dauer;
};

EingangsDaten::EingangsDaten (typStlElement* zSE)
 : ArtikelNr  (zSE->Artikel ()),
   Menge      (zSE->Menge ()),
   Dauer      (zSE->Zeit ())
 { }

//-----------------------------------------------------
//   Klasse KommJob
//-----------------------------------------------------

class KommBestellzettel;

class KommJob {
  public:
    KommJob (typArbeitsplan* zA, KommBestellzettel* best);
    ~KommJob ();
    bool           isFertig   () const;
    EingangsDaten* isRelevant (typTransportEinheit* zTE) const;

    DynArray<EingangsDaten> Eingaenge;
    KommBestellzettel* Bestellung;
};

KommJob::KommJob (typArbeitsplan* zA, KommBestellzettel* best)
 : Eingaenge (2), Bestellung (best) {
  const DynArray<typStlElement>& e (zA->EingangsListe ());
  for (e.First (); e (); e++)
   for (int anzahl = 1; anzahl <= e()->Anzahl(); anzahl++) //rei 02.06.04
    Eingaenge.append (*(new EingangsDaten (e ())));
}

KommJob::~KommJob () {
  while (Eingaenge.Last ()) delete Eingaenge.removeLast ();
}

bool KommJob::isFertig () const {
  return Eingaenge.isEmpty ();
}

EingangsDaten* KommJob::isRelevant (typTransportEinheit* zTE) const {
  for (Eingaenge.First (); Eingaenge (); Eingaenge++) {
    if ((Eingaenge ()->ArtikelNr == zTE->Artikel ())
        && zTE->Menge () > 0.0)
      return Eingaenge ();
  }
  return NULL;
}

//-----------------------------------------------------
//   Klasse KommBestellzettel
//-----------------------------------------------------

class KommBestellzettel : public ArbeitsplanBestellzettel {
  public:
    KommBestellzettel (typArbeitsplan* arb, unsigned istv, unsigned sollv, unsigned wieoft);
    virtual ~KommBestellzettel ();

    bool     isAbgearbeitet () const;
    KommJob* stornieren ();
    KommJob* erzeugeJob ();
    void     vergissJob ();

    bool           isMeinTeil (typTransportEinheit*) const;
    typTransportEinheit* erzeugeTeil ();
    void           vergissTeil ();

  private:
    Queue<KommJob> ObjJobs;
    Queue<typTransportEinheit> ObjTeile;
    typArbeitsplan* ObjArbeitsplan;
};

KommBestellzettel::KommBestellzettel (typArbeitsplan* arb, unsigned istv, unsigned sollv,
                                            unsigned wieoft)
 : ArbeitsplanBestellzettel (arb->Nummer (), istv, sollv, wieoft),
   ObjJobs (), ObjTeile (), ObjArbeitsplan (arb)
  { }

/*virtual*/ KommBestellzettel::~KommBestellzettel () {
  ObjJobs.removeAll ();
  ObjTeile.removeAll ();
}

bool KommBestellzettel::isAbgearbeitet () const {
  return (!Wieoft () && ObjJobs.isEmpty () && ObjTeile.isEmpty ());
}

KommJob* KommBestellzettel::stornieren () {
  unsigned anz (Wieoft ());
  if (anz)  while (decWieoft ());
  KommJob *zJ1 (ObjJobs.remove ()), *zJ2 (ObjJobs.remove ());
  if (zJ2)  {
    ObjJobs.append (*zJ1);
    return zJ2;
  }
  return anz ? zJ1 : (KommJob*) NULL;
}

KommJob* KommBestellzettel::erzeugeJob () {
  decWieoft ();
  return &(ObjJobs.append (* (new KommJob (ObjArbeitsplan, this))));
}

void KommBestellzettel::vergissJob () {
  ObjJobs.remove ();
}

bool KommBestellzettel::isMeinTeil (typTransportEinheit* zTE) const {
  return (zTE == ObjTeile.First ());
}

typTransportEinheit* KommBestellzettel::erzeugeTeil () {
  return &(ObjTeile.append (* (new typTransportEinheit (ObjArbeitsplan))));
}

void KommBestellzettel::vergissTeil () {
  ObjTeile.remove ();
}

//-----------------------------------------------------
//   Klasse typKommissionierer
//-----------------------------------------------------

typKommissionierer::typKommissionierer (unsigned n, unsigned b)
 : typBaustein       (n, b),
   ObjNeuTempAbgeben (false),
   ObjTransportEinheitWartet (false),
   ObjNeuTemp        (NULL),
   ObjNeuZielBst     (NULL),
   ObjAktJob         (NULL),
   ObjAktArtikel     (NULL),
   ObjJobs           (),
   ObjArbeitsliste   () {
  ObjAvis = new typAvis();
}

typKommissionierer::~typKommissionierer () {
  while (ObjArbeitsliste.Last ())
    delete ObjArbeitsliste.removeLast ();
  while (ObjJobs.Last ()) delete ObjJobs.removeLast ();
  if (ObjAktJob) delete ObjAktJob;
}

/*virtual*/ void typKommissionierer::typTGSlesen (InputFile& file) {
  typBaustein::typTGSlesen (file);
  if (ObjPalPlaetze.Count () != 2)
    sim_error ("KOMM", "typKommissionierer::typTGSlesen",
               "Kommissionierer mit != 2 Palettenplaetzen", ObjVariante, FATAL);
}

/*virtual*/ void typKommissionierer::typTTPlesen (InputFile& file) {
  SFXToken tok;
  tok.nextToken (file);
  ObjAufnahmezeit = SimTime (atof (tok ()) * SimTime::SecondsPerMinute ());
  tok.nextToken (file);
  ObjPhysKapazitaet = ObjKapazitaet = atoi (tok ()); // EingangsKap
  tok.nextToken (file); // AusgangsKap ueberlesen!!!!
  file.jump (100); //EOL
}

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

/*virtual*/ void typKommissionierer::typTSTlesen (InputFile& file) {
  typBaustein::typTSTlesen (file);
  if (ObjStrategie > 1)
    sim_error ("KOMM", "typKommissionierer::typTSTlesen",
               "Strategie nicht implementiert", ObjNummer, FATAL);
}

/*virtual*/ void typKommissionierer::e_aufnehmen () {
  typPlatz *p (ObjTransportEinheit->AktPlatz ());
  if (p->Buchung () != ObjTransportEinheit)
    sim_error ("KOMM", "typKommissionierer::e_aufnehmen",
               "TransportEinheit ist nicht auf Platz gebucht", ObjNummer, FATAL);
  p->setBuchung (NULL);
  ObjBuchung--;
  ObjTransportEinheit->positionieren (ObjPalPlaetze [0]->Position ());
//  ObjTransportEinheit->fearbeGestalt (default);

  if (ObjStoerDauer > SimTime (0.0))
    planesofort (ETastoerung, ObjNummer);
  else {
    setZustand (AKTIVIERT);
    if (findeBestellung (ObjTransportEinheit))
      planesofort (ETaarbeiten, ObjNummer, p->Nummer ());
    else  {
      ObjTransportEinheit->schalteVorgang ();
      setZustand(WARTEN);
      ObjTransportEinheit->Anmelden ();
      ObjZielBaustein = ObjTransportEinheit->Meldung ()->NachBaustein ();
    }
  }
  planesofort (ETeabgeben, ObjZielBaustein->Nummer (), ObjNummer);
}

/*virtual*/ void typKommissionierer::a_arbeiten () {
  if (notiz::AktInfo2 () == 0)  {
    setZustand (ARBEITEN);
    planedelta (ObjTransportEinheit->Vorgang ()->Dauer (), ETearbeiten, ObjNummer, notiz::AktInfo2 ());
  }
  else
    planedelta (ObjNeuTemp->Vorgang ()->Dauer (), ETearbeiten, ObjNummer, notiz::AktInfo2 ());
//  if (rzs) RzsSenden (ObjNummer, ETaarbeiten, 0);
}

/*virtual*/ void typKommissionierer::e_arbeiten () {
  if (notiz::AktInfo2 () == 0)
    if (ObjNeuTempAbgeben)  ObjTransportEinheitWartet = true;
    else planesofort (ETakommissionieren, ObjNummer, notiz::AktInfo2 ());
  else  {
    ObjNeuTemp->schalteVorgang (findeSollVorgang (ObjNeuTemp));
    ObjNeuTemp->Anmelden ();
    ObjNeuZielBst = ObjNeuTemp->Meldung ()->NachBaustein ();
  }
}

/*virtual*/ void typKommissionierer::a_kommissionieren () {
  setZustand (ARBEITEN);
  ObjTransportEinheitWartet = false;
  if (!ObjNeuTemp)  erzeugeNeuTemp ();
  double vorhandeneMenge = ObjTransportEinheit->Menge ();
  if (vorhandeneMenge >= ObjAktArtikel->Menge) {
    ObjTransportEinheit->decMenge (ObjAktArtikel->Menge);
    planedelta (ObjAktArtikel->Dauer.inMinutes () * ObjAktArtikel->Menge,
                ETekommissionieren, ObjNummer, notiz::AktInfo2 ());
    delete ObjAktJob->Eingaenge.remove (*ObjAktArtikel);
    ObjAktArtikel = NULL;
    if (ObjAktJob->isFertig ()) {
      ObjAktJob->Bestellung->vergissJob ();
      delete ObjAktJob;
      ObjAktJob = NULL;
    }
  }
  else {
    ObjTransportEinheit->decMenge (vorhandeneMenge);
    ObjAktArtikel->Menge -= vorhandeneMenge;
    planedelta (ObjAktArtikel->Dauer.inMinutes () * vorhandeneMenge,
                ETekommissionieren, ObjNummer, notiz::AktInfo2 ());
  }
}

/*virtual*/ void typKommissionierer::e_kommissionieren () {
  if (!ObjAktJob)  {
    ObjNeuTempAbgeben = true;
    planesofort (ETaarbeiten, ObjNummer, ObjNeuTemp->AktPlatz ()->Nummer ());
  }

  if (findeBestellung (ObjTransportEinheit))  ObjTransportEinheitWartet = true;
  else  {
    ObjTransportEinheit->schalteVorgang ();
    setZustand(WARTEN);
    ObjTransportEinheit->Anmelden ();
    ObjZielBaustein = ObjTransportEinheit->Meldung ()->NachBaustein ();
  }
}

/*virtual*/ void typKommissionierer::eStoeren () {
  ObjStoerEnde = NULL;
  if (ObjStoerGeschichte) ObjStoerGeschichte->erfassen (0.0);
  switch (ObjAlterZustand) {
    case AUFNEHMEN :  setZustand (AKTIVIERT);
                      if (findeBestellung (ObjTransportEinheit))
                        planesofort (ETaarbeiten, ObjNummer,
                                     ObjTransportEinheit->AktPlatz ()->Nummer ());
                      else  {
                        ObjTransportEinheit->schalteVorgang ();
                        setZustand(WARTEN);
                        ObjTransportEinheit->Anmelden ();
                        ObjZielBaustein = ObjTransportEinheit->Meldung ()->NachBaustein ();
                      }
                      break;

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

/*virtual*/ typTransportEinheit* typKommissionierer::gibTransportEinheit (typMeldung* zM) {
  typTransportEinheit *zTE = zM->TransportEinheit ();
  zTE->AktPlatz ()->setTransportEinheit (NULL);
  if (zTE == ObjTransportEinheit)  {
    setZustand (AKTIVIERT);
    decBelegung ();
    if (ObjBelGeschichte) ObjBelGeschichte->erfassen (ObjBelegung);
    planesofort (ETaabgeben, ObjNummer, ObjZielBaustein->Nummer ());
    ObjTransportEinheit = NULL;
  }
  else  {
    planesofort (ETaabgeben, ObjNummer, ObjNeuZielBst->Nummer ());
    ObjNeuTemp = NULL;
  }
  return zTE;
}

/*virtual*/ void typKommissionierer::a_abgeben () {
  if (baustein [notiz::AktInfo2 ()] == ObjZielBaustein)  setZustand (ABGEBEN);
}

/*virtual*/ void typKommissionierer::e_abgeben () {
  if (baustein [notiz::AktInfo2 ()] == ObjZielBaustein)  {
    setZustand (RUHE);
    ObjZielBaustein = NULL;
  }
  else  {
    ObjNeuZielBst = NULL;
    ObjNeuTempAbgeben = false;
  }
  planesofort (ETaktivieren, ObjNummer, ObjNummer);
  aktiviereEingaenge ();
}

/*virtual*/ unsigned typKommissionierer::AnzahlArbeitsliste () const {
  return ObjArbeitsliste.Count ();
}

/*virtual*/ bool typKommissionierer::hasKapazitaet (typTransportEinheit* zTE) const {
  bool kapok = (ObjBelegung + ObjBuchung < ObjKapazitaet);
  if (!kapok && (zTE != NULL)) kapok = (ObjPalPlaetze [0]->Buchung () == zTE);
  if (kapok && (ObjStrategie == 1)) {
    if (ObjAktJob) return (ObjAktJob->isRelevant (zTE));
    for (LCursor<KommJob> c (ObjJobs); c ; c++)
       if (c ()->isRelevant (zTE)) return true;
    return false;
  }
  return kapok;
}

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

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

/*virtual*/ Vec3D typKommissionierer::UebergabePos (const typBaustein& bst) const {
  return (ObjNeuZielBst == &bst) ? ObjPalPlaetze [1]->Position ()
                                 : ObjPalPlaetze [0]->Position ();
}

/*virtual*/ void typKommissionierer::ArbBestellung (unsigned idnr, unsigned istvorg,
                                                    unsigned sollvorg, unsigned wieoft) {
  if (!wieoft)  return;
  typArbeitsplan* zA = MySFXModel->findWorkSchedule (idnr);
  KommBestellzettel *zBZ = new KommBestellzettel (zA, istvorg, sollvorg, wieoft);
  ObjArbeitsliste.append (*zBZ);
  KommJob* zJ = zBZ->erzeugeJob ();
  ObjJobs.append (*zJ);
  planesofort (ETaktivieren, ObjNummer, ObjNummer);
}

/*virtual*/ void typKommissionierer::ArtBestellung (unsigned idnr, unsigned istvorg,
                                                    unsigned sollvorg, unsigned wieoft) {
  if (!wieoft)  return;
  typArbeitsplan* zA = MySFXModel->findWorkScheduleByArticle (idnr, this);
  KommBestellzettel *zBZ = new KommBestellzettel (zA, istvorg, sollvorg, wieoft);
  ObjArbeitsliste.append (*zBZ);
  KommJob* zJ = zBZ->erzeugeJob ();
  ObjJobs.append (*zJ);
  planesofort (ETaktivieren, ObjNummer, ObjNummer);
}

/*virtual*/ bool typKommissionierer::loescheArbBestellung (unsigned idnr, unsigned istv) {
  for (LCursor<KommBestellzettel> c (ObjArbeitsliste); c; c++) {
    if (c ()->hatArbeitsplan (idnr, istv)) {
      KommJob* zJ = c ()->stornieren ();
      if (zJ)                      delete ObjJobs.remove (*zJ);
      if (c ()->isAbgearbeitet ()) delete ObjArbeitsliste.remove (c);
      return true;
    }
  }
  return false;
}

/*virtual*/ bool typKommissionierer::loescheArtBestellung (unsigned idnr, unsigned istv) {
  typArbeitsplan* zA = MySFXModel->findWorkScheduleByArticle (idnr, this);
  for (LCursor<KommBestellzettel> c (ObjArbeitsliste); c; c++) {
    if (c ()->hatArbeitsplan (zA->Nummer (), istv)) {
      KommJob* zJ = c ()->stornieren ();
      if (zJ)                      delete ObjJobs.remove (*zJ);
      if (c ()->isAbgearbeitet ()) delete ObjArbeitsliste.remove (c);
      return true;
    }
  }
  return false;
}

/*virtual*/ unsigned typKommissionierer::AnzahlArbeitsplanVorhanden (unsigned arbNr, unsigned istv /*= 0*/) const {
  unsigned anzTE = 0;
  if (ObjTransportEinheit && (ObjTransportEinheit->Baustein () == (typBaustein*) this)
      && ObjTransportEinheit->hatArbeitsplan (arbNr, istv))   anzTE++;
  if (ObjNeuTemp && (ObjNeuTemp->Baustein () == (typBaustein*) this)
      && ObjNeuTemp->hatArbeitsplan (arbNr, istv))   anzTE++;
  return anzTE;
}

/*virtual*/ unsigned typKommissionierer::AnzahlArtikelVorhanden (unsigned artNr, unsigned istv /*= 0*/) const {
  unsigned anzTE = 0;
  if (ObjTransportEinheit && (ObjTransportEinheit->Baustein () == (typBaustein*) this)
      && ObjTransportEinheit->hatArtikel (artNr, istv))   anzTE++;
  if (ObjNeuTemp && (ObjNeuTemp->Baustein () == (typBaustein*) this)
      && ObjNeuTemp->hatArtikel (artNr, istv))   anzTE++;
  return anzTE;
}

/*virtual*/ double typKommissionierer::MengeArbeitsplanVorhanden (unsigned arbNr, unsigned istv /*= 0*/) const {
  double mengeTE = 0;
  if (ObjTransportEinheit && (ObjTransportEinheit->Baustein () == (typBaustein*) this)
      && ObjTransportEinheit->hatArbeitsplan (arbNr, istv))   mengeTE += ObjTransportEinheit->Menge ();
  if (ObjNeuTemp && (ObjNeuTemp->Baustein () == (typBaustein*) this)
      && ObjNeuTemp->hatArbeitsplan (arbNr, istv))   mengeTE += ObjNeuTemp->Menge ();
  return mengeTE;
}

/*virtual*/ double typKommissionierer::MengeArtikelVorhanden (unsigned artNr, unsigned istv /*= 0*/) const {
  double mengeTE = 0;
  if (ObjTransportEinheit && (ObjTransportEinheit->Baustein () == (typBaustein*) this)
      && ObjTransportEinheit->hatArtikel (artNr, istv))   mengeTE += ObjTransportEinheit->Menge ();
  if (ObjNeuTemp && (ObjNeuTemp->Baustein () == (typBaustein*) this)
      && ObjNeuTemp->hatArtikel (artNr, istv))   mengeTE += ObjNeuTemp->Menge ();
  return mengeTE;
}

/*virtual*/ void typKommissionierer::individuellAktivieren (typBaustein* vonWem) {
  if (!(((ObjZustand == RUHE)  && (!ObjMeldungen.isEmpty ()))
        || ((ObjZustand == WARTEDP) && (ObjZielBaustein == vonWem))
        || (vonWem == this)))
    return;
  if (ObjTransportEinheit)  {
    if (ObjTransportEinheitWartet && !ObjNeuTemp)
      planesofort (ETakommissionieren, ObjNummer, ObjTransportEinheit->AktPlatz ()->Nummer ());
    return;
  }
  typMeldung* zM = findeAusfuehrbareMeldung ();
  if (zM) {
    ObjTransportEinheit = zM->TransportEinheit ();
    ObjZielBaustein = zM->VonBaustein ();
    wartenaufnehmen (ObjTransportEinheit, ObjZielBaustein);
  }
}

typMeldung* typKommissionierer::findeAusfuehrbareMeldung () {
  LCursor<typMeldung> c (ObjMeldungen);
  typTransportEinheit *zTE;
  for ( ; c; c++) {
    zTE = c ()->TransportEinheit ();
    if (ObjStrategie == 0)  {
      findeBestellung (zTE);
      break;
    }
    else
      if ((ObjStrategie == 1) && findeBestellung (zTE)) break;
  }

  typTransportEinheit* zBuch = ObjPalPlaetze [0]->Buchung ();
  if (c && (!zBuch || (zTE == zBuch))) {
    if (!zBuch)  zTE->setZielPlatz (waehlePlatz (zTE));
    return &(ObjMeldungen.prepend (*(ObjMeldungen.remove (c))));
  }
  return NULL;
}

void typKommissionierer::erzeugeNeuTemp () {
  ObjNeuTemp = ObjAktJob->Bestellung->erzeugeTeil ();
  for (LCursor<typVorgang> c (ObjNeuTemp->Arbeitsplan ()->Vorgaenge ()); c; c++)
    c ()->ZielBaustein ()->Avis ()->incIndIstAnzahl ();
  ObjAvis->decIndIstAnzahl ();
  ObjAvis->incDirIstAnzahl ();
  ObjNeuTemp->schaltePlatz ();
  ObjNeuTemp->SystemEintritt (ObjPalPlaetze [1]->Position (), ObjWinkel);
//  ObjNeuTemp->fearbeGestalt (default);
}

bool typKommissionierer::findeBestellung (typTransportEinheit* zTE) {
  if (ObjAktJob) return (ObjAktArtikel = ObjAktJob->isRelevant (zTE));
  for (LCursor<KommJob> c (ObjJobs); c; c++)
    if ((ObjAktArtikel = c ()->isRelevant (zTE)) != NULL) {
      if (c ()->Bestellung->Wieoft ())
        ObjJobs.append (*(c ()->Bestellung->erzeugeJob ())); //26.04.00 rei
      ObjAktJob = ObjJobs.remove (c);
      return true;
    }
  return false;
}

unsigned typKommissionierer::findeSollVorgang (typTransportEinheit *zTE) {
  for (LCursor<KommBestellzettel> c (ObjArbeitsliste); c; c++)
    if (c ()->isMeinTeil (zTE))  {
      unsigned sollvorgang = c ()->SollVorgang ();
      c ()->vergissTeil ();
      if (c ()->isAbgearbeitet ())  delete ObjArbeitsliste.remove (c);
      return sollvorgang;
    }
  sim_error ("KOMM", "typKommissionierer::findeSollVorgang",
             "Palette ohne Bestellzettel auf Kommissionierer", ObjNummer, FATAL);
  return 0;
}
