package storagesim; import java.time.LocalDate; import java.time.temporal.IsoFields; import java.util.ArrayList; import java.util.HashMap; import java.util.Random; import java.util.stream.Collectors; import lib.OpenSimplexNoise; import lib.SQLConnection; /** * Runs a loop with a strict interval to simulate time passing * * @author Manuel */ public class Simulation { boolean running; public LocalDate currentTime; OpenSimplexNoise noise; final float STEPS_PER_SECOND = 1 / 30f; final int MAX_STEPS = 1; final int TOLERANCE = 200; public Simulation() { this.running = false; this.currentTime = LocalDate.now(); //this.currentTime = LocalDate.parse("2020-02-06"); this.noise = new OpenSimplexNoise(); } /** * Starts and contains the loop * * @author Manuel */ public void run(LocalDate time) { // initialise timing values double interval = 1000f / (STEPS_PER_SECOND); double previousMillis = System.currentTimeMillis() - interval; this.currentTime = time; do { // update current time double currentMillis = System.currentTimeMillis(); double deltaMillis = currentMillis - previousMillis; // run code if enough time has passed int steps = 0; while (deltaMillis >= interval && steps < MAX_STEPS) { // -------------------- // this is the code that is executed in the specified interval SQLConnection db = new SQLConnection(); System.out.println("Date: " + this.currentTime.toString()); simulateSales(db); sellItems(db); disposeItems(db); receiveOrders(db); sortStorage(db); checkStorage(db); sortStatistics(db); db.close(); this.currentTime.plusDays(1); // this.running = false; // -------------------- steps++; deltaMillis -= interval; } if (steps > 0) { // only change time if any code was executed previousMillis = currentMillis; } } while (this.running); } /** * Removes all Items sold in the last time frame * * @author Johann */ public void sellItems(SQLConnection db) { // Liste der verkauften waren wird erstellt ArrayList> Verkauft = db.queryToMap( "SELECT * FROM t_statistik WHERE `Woche` = " + this.currentTime.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR)); int MengePalette; // Zwischenspeicher für die Menge der Items auf einer Palette for (HashMap Artikel : Verkauft) { int VerkaufteID = Integer.parseInt(Artikel.get("f_Artikel_ID")); // ID des momentan Bearbeiteten Produktes // Alle Paletten werden zwischengespeichert ArrayList> Paletten = db.queryToMap("SELECT * FROM t_mindesthaltbarkeit WHERE `f_ProduktID` = " + VerkaufteID + " ORDER BY Menge"); int Zähler = 0; // in einer späteren for() schleife wird ein weiterer Zähler benötigt for (int VerkaufteMenge = Integer.parseInt(Artikel.get("Menge")); VerkaufteMenge > 0; Zähler++) { if (Paletten.size() > Zähler) { // Der Rest auf einer Palette an Items MengePalette = Integer.parseInt(Paletten.get(Zähler).get("Menge")); if (VerkaufteMenge <= MengePalette) // Wenn mehr auf der einen Palette ist als verkauft wurde { MengePalette -= VerkaufteMenge; VerkaufteMenge = 0; // Alles wurde entfernt und die Palette wurde neu beschrieben } else // Wenn mehr verkauft wurde als auf der einen Palette war { VerkaufteMenge -= MengePalette; // Die Palette wird gelert, und die Variable neu beschrieben, es wird die // nächste Palette zu rate gezogen MengePalette = 0; } db.execute("UPDATE t_mindesthaltbarkeit SET `Menge` = " + MengePalette + " WHERE t_mindesthaltbarkeit.`ID` = " + Paletten.get(Zähler).get("ID")); db.execute("UPDATE t_lager SET `Menge` = " + MengePalette + " WHERE t_lager.`Paletten_ID` = " + Paletten.get(Zähler).get("ID")); } else { break; } } } } /** * Disposes of all items that have expired * * @author Johann */ public void disposeItems(SQLConnection db) { ArrayList> Waren = db.queryToMap("SELECT t_mindesthaltbarkeit.ID, t_mindesthaltbarkeit.Haltbarkeit, t_mindesthaltbarkeit.Menge, t_artikel.Name FROM t_mindesthaltbarkeit INNER JOIN t_artikel ON t_mindesthaltbarkeit.f_ProduktID = t_artikel.ID"); // Waren liste wird erstellt // Nach Produkten die verworfen werden müssen suchen for (HashMap Ware : Waren) { LocalDate Datum2 = LocalDate.parse(Ware.get("Haltbarkeit")); // Das DAtum des wahrscheinlich nichtmehr // Haltbaren abfragen // Compare if (this.currentTime.isAfter(Datum2)) // Abfagen ob das Item abgelaufen ist { int ID = Integer.parseInt(Ware.get("ID")); // Speichern welches Produkt abgelaufen ist System.out.printf("Eine Pallette mit %s %s ist abgelaufen.\n", Ware.get("Menge"), Ware.get("Name")); db.execute("UPDATE t_mindesthaltbarkeit SET `Menge` = '0' WHERE t_mindesthaltbarkeit.`ID` = " + ID); // produkt verwerfen db.execute("UPDATE t_lager SET `Menge` = '0' WHERE t_lager.`Paletten_ID` = " + ID); } } // Leere Paletten wegwerfen Waren = db.queryToMap("SELECT * FROM t_mindesthaltbarkeit"); // Liste mit allen Palette wird aufgerufen for (HashMap Ware : Waren) { if (Integer.parseInt(Ware.get("Menge")) == 0) // Wenn eine Palette leer ist { db.execute("DELETE FROM t_mindesthaltbarkeit WHERE t_mindesthaltbarkeit.`ID` = " + Ware.get("ID")); // Palette // wir // geleert } } // Leere Paletten in der Haupttabelle werden verworfen Waren = db.queryToMap("SELECT * FROM t_lager"); // Liste der Waren in der Haupttabelle wird erstellt for (HashMap Ware : Waren) { if (Integer.parseInt(Ware.get("Menge")) == 0) // Leere Paletten werden gesucht { // leere Palette wird gelöscht db.execute("DELETE FROM t_lager WHERE t_lager.`Lagerplatz` = " + Ware.get("Lagerplatz")); } } } /** * Receive orders and puts them into storage * * @author Johann, Manuel */ public void receiveOrders(SQLConnection db) { ArrayList> Angekommen = db.queryToMap( "SELECT t_bestellungen.ID, f_Artikel_ID, Einkaufspreis, Ankunftsdatum, Menge, Marke, Name FROM t_bestellungen " + "INNER JOIN t_artikel ON t_bestellungen.f_Artikel_ID = t_artikel.ID " + "WHERE Ankunftsdatum <= ('" + this.currentTime.toString() + "')"); ArrayList> Artikel = db .queryToMap("SELECT ID, `MHK in Tagen`, Anzahl_pro_Palette FROM t_artikel"); ArrayList> Haltbarkeit; LocalDate HaltbarBis; int MHKinTagen; for (HashMap row : Angekommen) { int Artikel_ID = Integer.parseInt(row.get("f_Artikel_ID")); MHKinTagen = Integer.parseInt(Artikel.get(Artikel_ID - 1).get("MHK in Tagen")); if (MHKinTagen >= 0) { HaltbarBis = this.currentTime.plusDays(MHKinTagen); } else { HaltbarBis = LocalDate.parse("9999-12-31"); } int PalettenMenge = Integer.parseInt(Artikel.get(Artikel_ID - 1).get("Anzahl_pro_Palette")); int BestellMenge = Integer.parseInt(row.get("Menge")); while (BestellMenge > 0) { int Menge = BestellMenge > PalettenMenge ? PalettenMenge : BestellMenge; BestellMenge -= Menge; db.execute("INSERT INTO t_mindesthaltbarkeit SET f_ProduktID = " + row.get("f_Artikel_ID") + ", Haltbarkeit = ('" + HaltbarBis.toString() + "')" + ", Menge = " + Menge); Haltbarkeit = db.queryToMap("SELECT ID, f_ProduktID, Haltbarkeit, Menge FROM t_mindesthaltbarkeit"); String palettenID = Haltbarkeit.get(Haltbarkeit.size()-1).get("ID") ; int lagerplatz = findStorageSpace(db); db.execute("INSERT INTO t_lager SET Lagerplatz = " + lagerplatz + ", Paletten_ID = " + palettenID + ", f_Artikel_ID = " + row.get("f_Artikel_ID") + ", Ankunft = ('" + row.get("Ankunftsdatum") + "')" + ", Menge = " + Menge); } db.execute("DELETE FROM t_bestellungen WHERE ID = " + row.get("ID")); } while (Angekommen.size() > 0) { BWLHelper.Bestellungen(db, this.currentTime, Angekommen, "Pascal"); } } /** * Goes through all available storage locations and finds the first empty in * line * * @author Manuel */ public int findStorageSpace(SQLConnection db) { ArrayList> Lagerplätze = db.queryToMap("SELECT Lagerplatz FROM t_lager"); ArrayList BelegteLagerplätze = new ArrayList<>( Lagerplätze.stream().filter((map) -> map.containsKey("Lagerplatz")) .map(map -> Integer.parseInt(map.get("Lagerplatz"))).collect(Collectors.toList())); BelegteLagerplätze.sort((a, b) -> Integer.compare(a, b)); int val = 1; for (int i = 0; i < BelegteLagerplätze.size(); i++) { if (val < BelegteLagerplätze.get(i)) { break; } else { val = BelegteLagerplätze.get(i) + 1; } } return val; } /** * Sorts storage to group products by type and removes gaps * * @author Johann */ public void sortStorage(SQLConnection db) { ArrayList> products = db.queryToMap("SELECT ID FROM t_artikel"); int Lagerplatz = 1; ArrayList> Waren = db.queryToMap("SELECT * FROM t_lager"); ArrayList> Waren_unverändert = db.queryToMap("SELECT * FROM t_lager"); String[][] Waren_sortiert = new String[Waren.size()][5]; for (HashMap product : products) { int productID = Integer.parseInt(product.get("ID")); Waren = db.queryToMap("SELECT * FROM t_lager WHERE f_Artikel_ID = " + productID); if (Waren.size() != 0) { for (HashMap Ware : Waren) { Waren_sortiert[Lagerplatz - 1][0] = Integer.toString(Lagerplatz); Waren_sortiert[Lagerplatz - 1][1] = Ware.get("Paletten_ID"); Waren_sortiert[Lagerplatz - 1][2] = Ware.get("f_Artikel_ID"); Waren_sortiert[Lagerplatz - 1][3] = Ware.get("Ankunft"); Waren_sortiert[Lagerplatz - 1][4] = Ware.get("Menge"); Lagerplatz++; } } } for (int u = 0; u <= Waren_sortiert.length - 1; u++) { db.execute("UPDATE `t_lager` SET `Lagerplatz` = " + Waren_sortiert[u][0] + ", Paletten_ID = " + Waren_sortiert[u][1] + ", `f_Artikel_ID` = " + Waren_sortiert[u][2] + ", `Ankunft` = ('" + Waren_sortiert[u][3] + "')" + ", `Menge` = " + Waren_sortiert[u][4] + " WHERE `t_lager`.`Lagerplatz` = " + Waren_unverändert.get(u).get("Lagerplatz")); } System.out.println("Lager wurde sortiert."); } /** * Check if items have to be restocked and order them accordingly * * @author Pascal */ public void checkStorage(SQLConnection db) { ArrayList> produkte = db.queryToMap("SELECT * FROM t_artikel"); for (HashMap produkt : produkte) { int produktID = Integer.parseInt(produkt.get("ID")); int sollbestand = Integer.parseInt(produkt.get("Sollbestand")); int ZinsenProzent = 8; if (sollbestand > 0) { boolean sicherheitsbestand = produkt.get("Sicherheitsbestand") == "1"; BWLHelper.sys2(db, this.currentTime, produktID, 7, sollbestand, ZinsenProzent, sicherheitsbestand); } else { BWLHelper.sys1(db, this.currentTime, produktID, ZinsenProzent); } } BWLHelper.lagerStatistik(db, this.currentTime.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR)); } /** * Summarises sales statistics in extra table * * @author Johann */ public void sortStatistics(SQLConnection db) { ArrayList> products = db.queryToMap("SELECT ID FROM t_artikel"); ArrayList> Verkaufszahlen; int KWoche = currentTime.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); int Verkauft; db.execute("TRUNCATE t_verkaufszahlen"); for(int n = 1; n < KWoche; n++) { Verkaufszahlen = db.queryToMap("SELECT * FROM `t_statistik` WHERE `Woche` = " + n); if(Verkaufszahlen.size() > 0) { for (HashMap product : products) { int productID = Integer.parseInt(product.get("ID")); Verkauft = 0; Verkaufszahlen = db.queryToMap("SELECT * FROM `t_statistik` WHERE `f_Artikel_ID` = " + productID + " AND `Woche` = " + n); if(Verkaufszahlen.size() > 0) { for(int u = 0; u < Verkaufszahlen.size(); u++) { Verkauft += Integer.parseInt(Verkaufszahlen.get(u).get("Menge")); } db.execute("INSERT INTO `t_verkaufszahlen` (`ID`, `f_Artikel_ID`, `Woche`, `Menge`, `Verkaufspreis`) VALUES (NULL, '" + productID + "', '" + n + "', '" + Verkauft + "', '" + Verkaufszahlen.get(0).get("Verkaufspreis") + "')"); } } } } } /** * Simulate sales through random noise * * @author Manuel */ public void simulateSales(SQLConnection db) { ArrayList> products = db.queryToMap("SELECT ID, Verkaufspreis, Anzahl_pro_Palette FROM t_artikel"); Random r = new Random(); for (HashMap product : products) { int productID = Integer.parseInt(product.get("ID")); int variance = Integer.parseInt(product.get("Anzahl_pro_Palette")); double stepSize = 52f * (r.nextDouble() + 0.00001) * 4; double val = (1 + this.noise.eval(productID * 100, this.currentTime.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR) / stepSize)) / 20.0; int sales = (int)(val * variance) + 1; //TODO somewhere we should check if we have enough of a product in storage db.execute("INSERT INTO t_statistik SET f_Artikel_ID = " + productID + ", Woche = " + this.currentTime.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR) + ", Menge = " + sales + ", Verkaufspreis = " + product.get("Verkaufspreis")); } } }