diff --git a/lab-02/.gitignore b/lab-02/.gitignore new file mode 100644 index 0000000..0b0c0f5 --- /dev/null +++ b/lab-02/.gitignore @@ -0,0 +1,3 @@ +/.idea/ +/lab-02.iml +/out/ diff --git a/lab-02/lib/json-simple-1.1.jar b/lab-02/lib/json-simple-1.1.jar new file mode 100644 index 0000000..f395f41 Binary files /dev/null and b/lab-02/lib/json-simple-1.1.jar differ diff --git a/lab-02/src/by/parfen01/docks_and_hobos/CargoDecoder.java b/lab-02/src/by/parfen01/docks_and_hobos/CargoDecoder.java new file mode 100644 index 0000000..d2fdb38 --- /dev/null +++ b/lab-02/src/by/parfen01/docks_and_hobos/CargoDecoder.java @@ -0,0 +1,24 @@ +package by.parfen01.docks_and_hobos; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.stream.IntStream; + +public class CargoDecoder { + private final HashMap cargoRepresent; + private final ArrayList productNames; + + public CargoDecoder(ArrayList cargo) { + this.productNames = cargo; + this.cargoRepresent = new HashMap<>(); + IntStream.range(0, cargo.size()).forEach(i -> cargoRepresent.put(cargo.get(i), i)); + } + + public int cargoToProduct(String cargo) { + return cargoRepresent.get(cargo); + } + + public String getProductName(int product) { + return productNames.get(product); + } +} diff --git a/lab-02/src/by/parfen01/docks_and_hobos/Dock.java b/lab-02/src/by/parfen01/docks_and_hobos/Dock.java new file mode 100644 index 0000000..f619a79 --- /dev/null +++ b/lab-02/src/by/parfen01/docks_and_hobos/Dock.java @@ -0,0 +1,55 @@ +package by.parfen01.docks_and_hobos; + +import by.parfen01.docks_and_hobos.control.Controller; +import by.parfen01.docks_and_hobos.ships.Ship; + +import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.logging.Level; + +import static java.lang.Thread.sleep; + +public class Dock { + private final int id; + private final int unloadingSpeed; + private final int[] maxProductCapacity; + private final AtomicIntegerArray productsCount; + + public Dock(int id, int unloadingSpeed, int[] maxProductCapacity) { + this.id = id; + this.unloadingSpeed = unloadingSpeed; + this.maxProductCapacity = maxProductCapacity; + productsCount = new AtomicIntegerArray(maxProductCapacity.length); + } + + public void UnloadShip(Ship ship) throws InterruptedException { + Controller.getController().getConsoleLogger().log( + Level.INFO, "Ship number " + ship.getId() + " started to unload in dock number " + id); + int product = Controller.getController().getCargoDecoder().cargoToProduct(ship.getCargoType()); + int pred = productsCount.get(product); + productsCount.set(product, Math.min(maxProductCapacity[product], + productsCount.get(product) + ship.getShipCapacity())); + sleep((productsCount.get(product) - pred) * 1000L / unloadingSpeed); + Controller.getController().getConsoleLogger().log( + Level.INFO, "Ship number " + ship.getId() + " unloaded in dock number " + id); + } + + public void start() throws InterruptedException { + Controller.getController().getConsoleLogger().log( + Level.INFO, "Dock number " + id + " started to work"); + while (Controller.getController().isWorking()) { + UnloadShip(Controller.getController().getTunnel().getShip()); + } + } + + public boolean stealProduct(int product) { + if (productsCount.get(product) == 0) { + return false; + } + productsCount.decrementAndGet(product); + return true; + } + + public int getId() { + return id; + } +} diff --git a/lab-02/src/by/parfen01/docks_and_hobos/Main.java b/lab-02/src/by/parfen01/docks_and_hobos/Main.java new file mode 100644 index 0000000..616a438 --- /dev/null +++ b/lab-02/src/by/parfen01/docks_and_hobos/Main.java @@ -0,0 +1,26 @@ +package by.parfen01.docks_and_hobos; + +import by.parfen01.docks_and_hobos.control.Controller; +import by.parfen01.docks_and_hobos.control.ProjectInitializer; +import org.json.simple.parser.ParseException; + +import java.io.IOException; + +import static java.lang.Thread.sleep; + +public class Main { + public static void main(String[] args) throws InterruptedException, IOException, ParseException { + if (args.length != 1) { + throw new IllegalArgumentException("wrong number of arguments"); + } + String path = args[0]; + Controller controller = ProjectInitializer.initController(path); + controller.start(); + sleep(10 * 1000L); + controller.stop(); + sleep(10 * 1000L); + controller.start(); + sleep((500 * 1000L)); + controller.stop(); + } +} \ No newline at end of file diff --git a/lab-02/src/by/parfen01/docks_and_hobos/Tunnel.java b/lab-02/src/by/parfen01/docks_and_hobos/Tunnel.java new file mode 100644 index 0000000..2f1213e --- /dev/null +++ b/lab-02/src/by/parfen01/docks_and_hobos/Tunnel.java @@ -0,0 +1,40 @@ +package by.parfen01.docks_and_hobos; + +import by.parfen01.docks_and_hobos.control.Controller; +import by.parfen01.docks_and_hobos.ships.Ship; + +import java.util.ArrayDeque; +import java.util.logging.Level; + +public class Tunnel { + private final ArrayDeque shipQueue; + private final int maxShipCount; + + public Tunnel(int maxShipCount) { + this.maxShipCount = maxShipCount; + this.shipQueue = new ArrayDeque<>(); + } + + public synchronized void addShip(Ship ship) { + if (shipQueue.size() == maxShipCount) { + Controller.getController().getConsoleLogger().log( + Level.INFO, "The ship number " + ship.getId() + " sank"); + return; + } + shipQueue.add(ship); + notify(); + Controller.getController().getConsoleLogger().log( + Level.INFO, "The ship number " + ship.getId() + " entered tunnel"); + } + + public synchronized Ship getShip() throws InterruptedException { + while (shipQueue.isEmpty()) { + wait(); + } + Ship result = shipQueue.poll(); + assert(result != null); + Controller.getController().getConsoleLogger().log( + Level.INFO, "The ship number " + result.getId() + " leaved tunnel"); + return result; + } +} diff --git a/lab-02/src/by/parfen01/docks_and_hobos/config.json b/lab-02/src/by/parfen01/docks_and_hobos/config.json new file mode 100644 index 0000000..14e1668 --- /dev/null +++ b/lab-02/src/by/parfen01/docks_and_hobos/config.json @@ -0,0 +1,71 @@ +{ + "ShipGenerator" : { + "shipCapacityMin" : 1, + "shipCapacityMax" : 100, + "generatingTime" : 10, + "cargoTypes" : [ + "milk", + "meat", + "bread", + "carrot", + "cucumber" + ] + }, + "Tunnel" : 7, + "Docks" : [ + { + "id" : 1, + "unloadingSpeed" : 4, + "maxProductCapacity" : [ + 50, + 50, + 20, + 25, + 100 + ] + }, + { + "id" : 2, + "unloadingSpeed" : 7, + "maxProductCapacity" : [ + 80, + 100, + 35, + 40, + 55 + ] + } + ], + "Hobos" : [ + { + "stealingTime" : 7, + "name" : "Ivan" + }, + { + "stealingTime" : 2, + "name" : "Vlad" + }, + { + "stealingTime" : 1, + "name" : "Danil" + }, + { + "stealingTime" : 5, + "name" : "Nicolay" + }, + { + "stealingTime" : 3, + "name" : "Jon" + } + ], + "hobosVillage" : { + "eatingTime" : 10, + "ingredientsCount" : [ + 11, + 4, + 8, + 9, + 2 + ] + } +} \ No newline at end of file diff --git a/lab-02/src/by/parfen01/docks_and_hobos/control/Controller.java b/lab-02/src/by/parfen01/docks_and_hobos/control/Controller.java new file mode 100644 index 0000000..1871b23 --- /dev/null +++ b/lab-02/src/by/parfen01/docks_and_hobos/control/Controller.java @@ -0,0 +1,114 @@ +package by.parfen01.docks_and_hobos.control; + +import by.parfen01.docks_and_hobos.CargoDecoder; +import by.parfen01.docks_and_hobos.Dock; +import by.parfen01.docks_and_hobos.Tunnel; +import by.parfen01.docks_and_hobos.hobos.HobosVillage; +import by.parfen01.docks_and_hobos.ships.ShipGenerator; + +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class Controller { + private boolean isWorking; + private final ShipGenerator shipGenerator; + private final HobosVillage hobosVillage; + private final Tunnel tunnel; + private final CargoDecoder cargoDecoder; + private final ArrayList docks; + private ArrayList workingThreads; + private final Logger consoleLogger; + private static Controller controller; + + public Controller(ShipGenerator shipGenerator, + HobosVillage hobosVillage, Tunnel tunnel, + CargoDecoder cargoDecoder, ArrayList docks) { + this.isWorking = false; + this.shipGenerator = shipGenerator; + this.hobosVillage = hobosVillage; + this.tunnel = tunnel; + this.cargoDecoder = cargoDecoder; + this.docks = docks; + this.consoleLogger = Logger.getLogger(Controller.class.getName()); + controller = this; + } + + public static Controller getController() { + return controller; + } + + public HobosVillage getHobosVillage() { + return hobosVillage; + } + + public Tunnel getTunnel() { + return tunnel; + } + + public CargoDecoder getCargoDecoder() { + return cargoDecoder; + } + + public ArrayList getDocks() { + return docks; + } + + public Logger getConsoleLogger() { + return consoleLogger; + } + + public boolean isWorking() { + return isWorking; + } + + public void start() { + isWorking = true; + consoleLogger.log( + Level.INFO, "Start"); + workingThreads = new ArrayList<>(); + Thread shipGeneratorThread = new Thread(() -> { + try { + shipGenerator.start(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + workingThreads.add(shipGeneratorThread); + Thread hobosVillageThread = new Thread(() -> { + try { + hobosVillage.start(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + workingThreads.add(hobosVillageThread); + for (Dock i : docks) { + Thread dockThread = new Thread(() -> { + try { + i.start(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + workingThreads.add(dockThread); + } + for (Thread i : workingThreads) { + i.start(); + } + } + + public void stop() throws InterruptedException { + if (workingThreads == null) { + return; + } + isWorking = false; + for (Thread i : workingThreads) { + if (i.getState() != Thread.State.WAITING) { + i.join(); + } + } + consoleLogger.log( + Level.INFO, "Stop"); + } +} diff --git a/lab-02/src/by/parfen01/docks_and_hobos/control/ProjectInitializer.java b/lab-02/src/by/parfen01/docks_and_hobos/control/ProjectInitializer.java new file mode 100644 index 0000000..b1c2b87 --- /dev/null +++ b/lab-02/src/by/parfen01/docks_and_hobos/control/ProjectInitializer.java @@ -0,0 +1,69 @@ +package by.parfen01.docks_and_hobos.control; + +import by.parfen01.docks_and_hobos.CargoDecoder; +import by.parfen01.docks_and_hobos.Dock; +import by.parfen01.docks_and_hobos.Tunnel; +import by.parfen01.docks_and_hobos.hobos.Hobo; +import by.parfen01.docks_and_hobos.hobos.HobosVillage; +import by.parfen01.docks_and_hobos.ships.ShipGenerator; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; + +public class ProjectInitializer { + public static Controller initController(String path) throws IOException, ParseException { + ArrayList hobos = new ArrayList<>(); + ArrayList cargo = new ArrayList<>(); + ArrayList docks = new ArrayList<>(); + ShipGenerator shipGenerator; + HobosVillage hobosVillage; + CargoDecoder cargoDecoder; + Tunnel tunnel; + Object obj = new JSONParser().parse(new FileReader(path)); + JSONObject jsonObject = (JSONObject) obj; + tunnel = new Tunnel((int) (long) jsonObject.get("Tunnel")); + JSONObject jsonShip = (JSONObject) jsonObject.get("ShipGenerator"); + int shipCapacityMin = (int) (long) jsonShip.get("shipCapacityMin"); + int shipCapacityMax = (int) (long) jsonShip.get("shipCapacityMax"); + int generatingTime = (int) (long) jsonShip.get("generatingTime"); + JSONArray jsonCargo = (JSONArray) jsonShip.get("cargoTypes"); + for (Object i : jsonCargo) { + cargo.add((String) i); + } + shipGenerator = new ShipGenerator(shipCapacityMin, shipCapacityMax, generatingTime, cargo); + cargoDecoder = new CargoDecoder(cargo); + JSONArray jsonDocks = (JSONArray) jsonObject.get("Docks"); + for (Object i : jsonDocks) { + JSONObject jsonDock = (JSONObject) i; + int id = (int) (long) jsonDock.get("id"); + int unloadingSpeed = (int) (long) jsonDock.get("unloadingSpeed"); + int[] maxProductCapacity = new int[cargo.size()]; + JSONArray jsonCapacity = (JSONArray) jsonDock.get("maxProductCapacity"); + for (int j = 0; j < jsonCapacity.size(); ++j) { + maxProductCapacity[j] = (int) (long) jsonCapacity.get(j); + } + docks.add(new Dock(id, unloadingSpeed, maxProductCapacity)); + } + JSONArray jsonHobos = (JSONArray) jsonObject.get("Hobos"); + for (Object i : jsonHobos) { + JSONObject jsonHobo = (JSONObject) i; + int stealingTime = (int) (long) jsonHobo.get("stealingTime"); + String name = (String) jsonHobo.get("name"); + hobos.add(new Hobo(stealingTime, name)); + } + JSONObject jsonVillage = (JSONObject) jsonObject.get("hobosVillage"); + int eatingTime = (int) (long) jsonVillage.get("eatingTime"); + JSONArray jsonIngredients = (JSONArray) jsonVillage.get("ingredientsCount"); + int[] requiredIngredientsCount = new int[cargo.size()]; + for (int i = 0; i < jsonIngredients.size(); ++i) { + requiredIngredientsCount[i] = (int) (long) jsonIngredients.get(i); + } + hobosVillage = new HobosVillage(requiredIngredientsCount, eatingTime, hobos); + return new Controller(shipGenerator, hobosVillage, tunnel, cargoDecoder, docks); + } +} diff --git a/lab-02/src/by/parfen01/docks_and_hobos/hobos/Hobo.java b/lab-02/src/by/parfen01/docks_and_hobos/hobos/Hobo.java new file mode 100644 index 0000000..d51a40d --- /dev/null +++ b/lab-02/src/by/parfen01/docks_and_hobos/hobos/Hobo.java @@ -0,0 +1,58 @@ +package by.parfen01.docks_and_hobos.hobos; + +import by.parfen01.docks_and_hobos.control.Controller; +import by.parfen01.docks_and_hobos.Dock; + +import java.util.ArrayList; +import java.util.logging.Level; + +import static java.lang.Thread.sleep; + +public class Hobo { + private final int stealingTime; + private final String name; + + public Hobo(int stealingTime, String name) { + this.stealingTime = stealingTime; + this.name = name; + } + + public String getName() { + return name; + } + + public void start() throws InterruptedException { + while (Controller.getController().isWorking()) { + int product = Controller.getController().getHobosVillage().getAbsentProduct(); + if (product == -1) { + break; + } + steal(product); + } + } + + private void steal(int product) throws InterruptedException { + int dockId = -1; + label: + while (Controller.getController().isWorking()) { + ArrayList docks = Controller.getController().getDocks(); + for (Dock dock : docks) { + if (dock.stealProduct(product)) { + dockId = dock.getId(); + break label; + } + } + } + // нужно для stop(). Если ничего не нашёл, при остановке, то просто возвращается домой, иначе честно доносит до дома + if (dockId == -1 && !Controller.getController().isWorking()) { + return; + } + sleep(stealingTime * 1000L); + Controller.getController().getConsoleLogger().log( + Level.INFO, "Hobo " + name + " stole " + + Controller.getController().getCargoDecoder().getProductName(product) + + " from dock number " + dockId); + Controller.getController().getHobosVillage() + .getCurrentIngredientsCount().incrementAndGet(product); + } +} diff --git a/lab-02/src/by/parfen01/docks_and_hobos/hobos/HobosVillage.java b/lab-02/src/by/parfen01/docks_and_hobos/hobos/HobosVillage.java new file mode 100644 index 0000000..cabc6db --- /dev/null +++ b/lab-02/src/by/parfen01/docks_and_hobos/hobos/HobosVillage.java @@ -0,0 +1,91 @@ +package by.parfen01.docks_and_hobos.hobos; + +import by.parfen01.docks_and_hobos.control.Controller; + +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.logging.Level; + +import static java.lang.Thread.sleep; + +public class HobosVillage { + private final int[] requiredIngredientsCount; + private final AtomicIntegerArray currentIngredientsCount; + private final int eatingTime; + private final ArrayList hobos; + + public HobosVillage(int[] requiredIngredientsCount, int eatingTime, ArrayList hobos) { + this.requiredIngredientsCount = requiredIngredientsCount; + this.eatingTime = eatingTime; + this.hobos = hobos; + currentIngredientsCount = new AtomicIntegerArray(this.requiredIngredientsCount.length); + } + + public AtomicIntegerArray getCurrentIngredientsCount() { + return currentIngredientsCount; + } + + public int getAbsentProduct() { + for (int i = 0; i < requiredIngredientsCount.length; ++i) { + if (requiredIngredientsCount[i] > currentIngredientsCount.get(i)) { + return i; + } + } + return -1; + } + + public void cookAndEat() throws InterruptedException { + Controller.getController().getConsoleLogger().log( + Level.INFO, "Hobos started to eat"); + for (int i = 0; i < requiredIngredientsCount.length; ++i) { + currentIngredientsCount.set(i, currentIngredientsCount.get(i) - requiredIngredientsCount[i]); + } + sleep(eatingTime * 1000L); + Controller.getController().getConsoleLogger().log( + Level.INFO, "Hobos ended to eat"); + } + + public void start() throws InterruptedException { + Controller.getController().getConsoleLogger().log( + Level.INFO, "HobosVillage started to work"); + while (Controller.getController().isWorking()) { + int firstCook = (int) (Math.random() * hobos.size()); + int secondCook = (int) (Math.random() * hobos.size()); + if (secondCook == firstCook) { + secondCook = (secondCook + 1) % hobos.size(); + } + Controller.getController().getConsoleLogger().log( + Level.INFO, "Hobo " + hobos.get(firstCook).getName() + " is first cook"); + Controller.getController().getConsoleLogger().log( + Level.INFO, "Hobo " + hobos.get(secondCook).getName() + " is second cook"); + ArrayList threads = new ArrayList<>(); + Controller.getController().getConsoleLogger().log( + Level.INFO, "Hobos started to steal"); + for (int i = 0; i < hobos.size(); ++i) { + if (i != firstCook && i != secondCook) { + int finalI = i; + Thread thread = new Thread(() -> { + try { + hobos.get(finalI).start(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + thread.start(); + threads.add(thread); + } + } + for (Thread i : threads) { + i.join(); + } + Controller.getController().getConsoleLogger().log( + Level.INFO, "All hobos returned home"); + // нужно для stop(), чтобы не есть, когда бродяги закончили красть из-за остановки системы, + // а не потому что всё украли + if (!Controller.getController().isWorking()) { + break; + } + cookAndEat(); + } + } +} diff --git a/lab-02/src/by/parfen01/docks_and_hobos/ships/Ship.java b/lab-02/src/by/parfen01/docks_and_hobos/ships/Ship.java new file mode 100644 index 0000000..badc799 --- /dev/null +++ b/lab-02/src/by/parfen01/docks_and_hobos/ships/Ship.java @@ -0,0 +1,32 @@ +package by.parfen01.docks_and_hobos.ships; + +import by.parfen01.docks_and_hobos.control.Controller; + +import java.util.logging.Level; + +public class Ship { + private final int id; + private final int shipCapacity; + private final String cargoType; + static private int lastId = 0; + + public Ship(int shipCapacity, String cargoType) { + this.id = ++lastId; + this.shipCapacity = shipCapacity; + this.cargoType = cargoType; + Controller.getController().getConsoleLogger().log( + Level.INFO, "Created new ship"); + } + + public int getShipCapacity() { + return shipCapacity; + } + + public String getCargoType() { + return cargoType; + } + + public int getId() { + return id; + } +} diff --git a/lab-02/src/by/parfen01/docks_and_hobos/ships/ShipGenerator.java b/lab-02/src/by/parfen01/docks_and_hobos/ships/ShipGenerator.java new file mode 100644 index 0000000..62f248d --- /dev/null +++ b/lab-02/src/by/parfen01/docks_and_hobos/ships/ShipGenerator.java @@ -0,0 +1,38 @@ +package by.parfen01.docks_and_hobos.ships; + +import by.parfen01.docks_and_hobos.control.Controller; + +import java.util.ArrayList; +import java.util.logging.Level; + +import static java.lang.Thread.sleep; + +public class ShipGenerator { + private final int shipCapacityMin; + private final int shipCapacityMax; + private final int generatingTime; + private final ArrayList cargoTypes; + + public ShipGenerator(int shipCapacityMin, int shipCapacityMax, + int generatingTime, ArrayList cargoTypes) { + this.shipCapacityMin = shipCapacityMin; + this.shipCapacityMax = shipCapacityMax; + this.generatingTime = generatingTime; + this.cargoTypes = cargoTypes; + } + + public void start() throws InterruptedException { + Controller.getController().getConsoleLogger().log( + Level.INFO, "ShipGenerator started to work"); + while (Controller.getController().isWorking()) { + Controller.getController().getTunnel().addShip(this.generate()); + sleep(generatingTime * 1000L); + } + } + + public Ship generate() { + int capacity = (int) (Math.random() * (shipCapacityMax - shipCapacityMin)) + shipCapacityMin; + String cargoType = cargoTypes.get((int) (Math.random() * cargoTypes.size())); + return new Ship(capacity, cargoType); + } +}