diff --git a/EAR diagrams/FoodChain.eap b/EAR diagrams/FoodChain.eap index 88db85c..961ec32 100644 Binary files a/EAR diagrams/FoodChain.eap and b/EAR diagrams/FoodChain.eap differ diff --git a/conf/scenarios.xml b/conf/scenarios.xml index 6f78563..2c43e9d 100644 --- a/conf/scenarios.xml +++ b/conf/scenarios.xml @@ -10,6 +10,7 @@ generate consume doaction +doaction sell buy pass diff --git a/src/main/java/cz/cvut/fel/sit/omo/foodchain/FoodChain.java b/src/main/java/cz/cvut/fel/sit/omo/foodchain/FoodChain.java index e69f752..126eb10 100644 --- a/src/main/java/cz/cvut/fel/sit/omo/foodchain/FoodChain.java +++ b/src/main/java/cz/cvut/fel/sit/omo/foodchain/FoodChain.java @@ -46,8 +46,6 @@ public class FoodChain return factoryManager; } - // todo maybe some refactoring needed? - // add other entities public static void parseChannels() { try diff --git a/src/main/java/cz/cvut/fel/sit/omo/foodchain/channel/ChannelBase.java b/src/main/java/cz/cvut/fel/sit/omo/foodchain/channel/ChannelBase.java index 84b2312..731433e 100644 --- a/src/main/java/cz/cvut/fel/sit/omo/foodchain/channel/ChannelBase.java +++ b/src/main/java/cz/cvut/fel/sit/omo/foodchain/channel/ChannelBase.java @@ -56,6 +56,9 @@ public abstract class ChannelBase implements Observable { public boolean tick() { requirements.forEach(Requirement::tick); + try { + requirements.stream().filter(r -> r.getTTL() == 0).forEach(re -> re.getCreator().getStorage().getProduct(re.getProductName(), re.getProductType()).setStateToAvailable()); + } catch (Exception ignored) {} requirements = requirements.stream().filter(r -> r.getTTL() > 0).collect(Collectors.toCollection(LinkedList::new)); notifyAllObservers(); return requirements.size() != 0; diff --git a/src/main/java/cz/cvut/fel/sit/omo/foodchain/node/Customer.java b/src/main/java/cz/cvut/fel/sit/omo/foodchain/node/Customer.java index d94670a..64af596 100644 --- a/src/main/java/cz/cvut/fel/sit/omo/foodchain/node/Customer.java +++ b/src/main/java/cz/cvut/fel/sit/omo/foodchain/node/Customer.java @@ -11,11 +11,11 @@ public class Customer extends BuyerNodeBase { super(startMoney); allowedSeller = NodeType.Distributor; allowedPurchaser = NodeType.None; - this.nodeType = NodeType.Customer; + nodeType = NodeType.Customer; } public void consumeProduct() { - int i = new Random().nextInt(getStorage().size()); - getStorage().remove(i); + int i = new Random().nextInt(getStorage().getNumberOfStoredProducts()); + getStorage().getProducts().remove(i); } } diff --git a/src/main/java/cz/cvut/fel/sit/omo/foodchain/node/Farmer.java b/src/main/java/cz/cvut/fel/sit/omo/foodchain/node/Farmer.java index 76bf758..406a7d3 100644 --- a/src/main/java/cz/cvut/fel/sit/omo/foodchain/node/Farmer.java +++ b/src/main/java/cz/cvut/fel/sit/omo/foodchain/node/Farmer.java @@ -13,11 +13,11 @@ public class Farmer extends SellerNodeBase { super(startMoney); allowedSeller = NodeType.None; allowedPurchaser = NodeType.Processor; - this.nodeType = NodeType.Farmer; + nodeType = NodeType.Farmer; } public void generateProduct(Product product) { - storeProduct(product); + this.storage.storeProduct(product); try { createRequirement(TransactionType.SALE, product.getProductType(), product.getName(), product.getPrice()); } catch (InsufficientResourcesException e) { diff --git a/src/main/java/cz/cvut/fel/sit/omo/foodchain/node/Storage.java b/src/main/java/cz/cvut/fel/sit/omo/foodchain/node/Storage.java new file mode 100644 index 0000000..a754ce7 --- /dev/null +++ b/src/main/java/cz/cvut/fel/sit/omo/foodchain/node/Storage.java @@ -0,0 +1,126 @@ +package cz.cvut.fel.sit.omo.foodchain.node; + +import cz.cvut.fel.sit.omo.foodchain.node.base.NodeBase; +import cz.cvut.fel.sit.omo.foodchain.operation.Action; +import cz.cvut.fel.sit.omo.foodchain.operation.ProductOperationType; +import cz.cvut.fel.sit.omo.foodchain.product.Product; +import cz.cvut.fel.sit.omo.foodchain.product.ProductType; + +import java.util.LinkedList; +import java.util.List; + +public class Storage +{ + private final NodeBase storageOwner; + private final List products = new LinkedList<>(); + + public Storage(NodeBase owner) + { + this.storageOwner = owner; + } + + /** + * Adds provided product into the storage + * @param product + */ + public void storeProduct(Product product) + { + this.products.add(product); + } + + /** + * Adds all of the provided products into the storage + * @param products + */ + public void storeProducts(List products) + { + this.products.addAll(products); + } + + /** + * Finds a product (first if there is multiple of them) based on its name and type + * @param name name of product to find + * @param type type of product to find + * @return product if found, null if product is not in storage + */ + public Product getProduct(String name, ProductType type) + { + return products.stream().filter(ch -> ch.getProductType() == type && ch.getName().equals(name)).findFirst().orElse(null); + } + + /** + * Returns List of all products in storage + * @return + */ + public List getProducts() + { + return products; + } + + /** + * Returns number of products in storage + * @return + */ + public int getNumberOfStoredProducts() + { + return this.products.size(); + } + + /** + * Checks whether storage contains provided product + * @param product product to check + * @return true if contains, false if not + */ + public boolean containsProduct(Product product) + { + return products.contains(product); + } + + /** + * Checks whether storage contains provided product + * @param name name of product to check + * @param type type of product to check + * @return true if contains, false if not + */ + public boolean containsProduct(String name, ProductType type) + { + return this.getProduct(name,type) != null; + } + + /** + * Removes provided product from storage if storage contains this product. (Removes only first found) + * @param product product to remove + */ + public void removeProduct(Product product) + { + if (containsProduct(product)) + this.products.remove(product); + } + + /** + * Proceed specific type of action on provided product if its possible. + * @param action type of product operation to do + * @param product product to proceed + */ + public void doAction(ProductOperationType action, Product product) throws IllegalCallerException + { + if (action.getNode().equals(this.storageOwner.getNodeType())) + if (action.getProduct().equals(product.getProductType()) || action.getProduct().equals(ProductType.ALL)) + new Action(this.storageOwner, product, action); + else + throw new IllegalCallerException("! IllegalCallerException in NodeBase. " + product.getProductType().toString() + " cannot be processed by action " + action.name() + " only for " + action.getProduct()); + else + throw new IllegalCallerException("! IllegalCallerException in NodeBase. " + this.storageOwner.getNodeType().toString() + " cannot do action available only for " + action.getNode()); + } + + /** + * Calls specific ProductOperation on each product in stock + * @param action ProductOperationType + */ + public void doAction(ProductOperationType action) + { + this.products.forEach(t -> new Action(this.storageOwner, t, action)); + } + + +} diff --git a/src/main/java/cz/cvut/fel/sit/omo/foodchain/node/base/NodeBase.java b/src/main/java/cz/cvut/fel/sit/omo/foodchain/node/base/NodeBase.java index 59ceb27..84d3526 100644 --- a/src/main/java/cz/cvut/fel/sit/omo/foodchain/node/base/NodeBase.java +++ b/src/main/java/cz/cvut/fel/sit/omo/foodchain/node/base/NodeBase.java @@ -2,6 +2,7 @@ package cz.cvut.fel.sit.omo.foodchain.node.base; import cz.cvut.fel.sit.omo.foodchain.channel.ChannelBase; import cz.cvut.fel.sit.omo.foodchain.channel.ChannelSubscriptionType; +import cz.cvut.fel.sit.omo.foodchain.node.Storage; import cz.cvut.fel.sit.omo.foodchain.node.enums.NodeType; import cz.cvut.fel.sit.omo.foodchain.operation.*; import cz.cvut.fel.sit.omo.foodchain.product.Product; @@ -15,35 +16,30 @@ import org.javamoney.moneta.Money; import javax.money.MonetaryAmount; import javax.naming.InsufficientResourcesException; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import java.util.*; public class NodeBase implements ChannelObserver { - private final Map subscribedChannels; - private final List products; - private final List refusedTransactions; + private final Map subscribedChannels = new HashMap<>(); + private final List refusedTransactions = new LinkedList<>(); + private final List refusedRequirements = new LinkedList<>(); private MonetaryAmount money; + protected final Storage storage; protected NodeType nodeType; protected NodeType allowedSeller; protected NodeType allowedPurchaser; public NodeBase() { this.nodeType = NodeType.None; - this.subscribedChannels = new HashMap<>(); - this.products = new LinkedList<>(); - this.refusedTransactions = new LinkedList<>(); + this.storage = new Storage(this); this.money = Money.of(0, "USD"); } public NodeBase(double m) { - this.subscribedChannels = new HashMap<>(); - this.products = new LinkedList<>(); - this.refusedTransactions = new LinkedList<>(); + this.nodeType = NodeType.None; + this.storage = new Storage(this); this.money = Money.of(m, "USD"); } @@ -55,7 +51,7 @@ public class NodeBase implements ChannelObserver { .filter(ch -> ch.getProductType().equals(product.getProductType())) .findAny().ifPresent(channel -> channel.addTransaction(transaction)); } catch (InsufficientResourcesException e) { - e.printStackTrace(); + System.err.println(e.getMessage()); } } @@ -67,7 +63,7 @@ public class NodeBase implements ChannelObserver { .filter(ch -> ch.getProductType().equals(prevTransaction.getProduct().getProductType())) .findAny().ifPresent(channel -> channel.addTransaction(transaction)); } catch (InsufficientResourcesException e) { - e.printStackTrace(); + System.err.println(e.getMessage()); } } @@ -121,9 +117,19 @@ public class NodeBase implements ChannelObserver { public void createRequirement(TransactionType tType, ProductType pType, String pName, MonetaryAmount price) throws InsufficientResourcesException { if (price == null) { try { - price = getProductFromStorage(pName, pType).getPrice(); + price = storage.getProduct(pName, pType).getPrice(); } catch (NullPointerException e) { + refusedRequirements.add(new RefusedRequirement(tType, pType, pName, price)); + throw new InsufficientResourcesException("product unavailable"); + } + } + if (tType == TransactionType.SALE) { + try { + storage.getProduct(pName, pType).setStateToInRequirement(); + } + catch (NullPointerException | IllegalStateException e) { + refusedRequirements.add(new RefusedRequirement(tType, pType, pName, price)); throw new InsufficientResourcesException("product unavailable"); } } @@ -139,9 +145,9 @@ public class NodeBase implements ChannelObserver { createRequirement(TransactionType.PURCHASE, req.getProductType(), req.getProductName(), req.getPrice()); } else { - if (req.getPrice().isGreaterThanOrEqualTo(getProductFromStorage(req.getProductName(), req.getProductType()).getPrice().multiply(0.9))) { - getProductFromStorage(req.getProductName(), req.getProductType()).setPrice(req.getPrice()); - sendTransaction(getProductFromStorage(req.getProductName(), req.getProductType()), req.getCreator()); + if (req.getPrice().isGreaterThanOrEqualTo(storage.getProduct(req.getProductName(), req.getProductType()).getPrice().multiply(0.9))) { + storage.getProduct(req.getProductName(), req.getProductType()).setPrice(req.getPrice()); + sendTransaction(storage.getProduct(req.getProductName(), req.getProductType()), req.getCreator()); } else createRequirement(TransactionType.SALE, req.getProductType(), req.getProductName(), req.getPrice()); } @@ -160,45 +166,30 @@ public class NodeBase implements ChannelObserver { /** - * Proceed specific type of action on provided product if its possible. + * Proceed specific type of action on provided product if its possible. (Propagated to its storage) * @param action type of product operation to do * @param product product to proceed */ public void doAction(ProductOperationType action, Product product) throws IllegalCallerException { - if(action.getNode().equals(this.nodeType)) - new Action(this, product, action); - else - throw new IllegalCallerException("! IllegalCallerException in NodeBase. " + this.nodeType.toString() + " cannot do action available only for "+action.getNode()); + this.storage.doAction(action,product); } - /** - * Calls specific ProductOperation on each product in stock + * Calls specific ProductOperation on each product in stock (Propagated to its storage) * @param action ProductOperationType */ public void doAction(ProductOperationType action) { - this.products.forEach(t -> new Action(this, t, action)); + this.storage.doAction(action); } - - public void storeProduct(Product product) { - this.products.add(product); + public Storage getStorage() { + return storage; } - - public void storeProducts(List products) { - this.products.addAll(products); - } - - - public Product getProductFromStorage(String name, ProductType type) { - return products.stream().filter(ch -> ch.getProductType() == type && ch.getName().equals(name)).findFirst().orElse(null); - } - - public List getStorage() { - return products; + public NodeType getNodeType() { + return nodeType; } public MonetaryAmount getMoney() { @@ -263,9 +254,10 @@ public class NodeBase implements ChannelObserver { } boolean requirementCheck(Requirement req) { + Product product = storage.getProduct(req.getProductName(), req.getProductType()); return (req.getTransactionType() == TransactionType.SALE && money.isGreaterThanOrEqualTo(req.getPrice())) || - (req.getTransactionType() == TransactionType.PURCHASE && getProductFromStorage(req.getProductName(), req.getProductType()) != null && - getProductFromStorage(req.getProductName(), req.getProductType()).getProductState() == ProductState.AVAILABLE); + (req.getTransactionType() == TransactionType.PURCHASE && product != null && + ((product.getProductState() == ProductState.AVAILABLE) || product.getProductState() == ProductState.IN_REQUIREMENT)); } @Override @@ -285,6 +277,20 @@ public class NodeBase implements ChannelObserver { //System.out.println(this.getClass().getSimpleName() + ": " + money.getNumber()); } + private static class RefusedRequirement { + public final TransactionType tType; + public final ProductType pType; + public final String pName; + public final MonetaryAmount price; + + public RefusedRequirement(TransactionType tType, ProductType pType, String pName, MonetaryAmount price) { + this.tType = tType; + this.pType = pType; + this.pName = pName; + this.price = price; + } + } + public String accept(ReportTextVisitor visitor){ return visitor.visit(this); } diff --git a/src/main/java/cz/cvut/fel/sit/omo/foodchain/operation/ProductOperationType.java b/src/main/java/cz/cvut/fel/sit/omo/foodchain/operation/ProductOperationType.java index 594be25..4f8943f 100644 --- a/src/main/java/cz/cvut/fel/sit/omo/foodchain/operation/ProductOperationType.java +++ b/src/main/java/cz/cvut/fel/sit/omo/foodchain/operation/ProductOperationType.java @@ -1,36 +1,36 @@ package cz.cvut.fel.sit.omo.foodchain.operation; import cz.cvut.fel.sit.omo.foodchain.node.enums.NodeType; +import cz.cvut.fel.sit.omo.foodchain.product.ProductType; public enum ProductOperationType { - STORE_MEAT(NodeType.All, 20, -8), - STORE_FRUIT(NodeType.All, 8, 2), - STORE_VEGETABLE(NodeType.All, 10, 2), - STORE_OTHER(NodeType.All, 30, 21), + STORE_MEAT(NodeType.All, ProductType.MEAT, 20, -8), + STORE_FRUIT(NodeType.All, ProductType.FRUIT, 8, 2), + STORE_VEGETABLE(NodeType.All, ProductType.VEGETABLE, 10, 2), + STORE_OTHER(NodeType.All, ProductType.OTHER, 30, 21), - WRAP_INTO_GIFT_PACKAGE(NodeType.Seller, 1, -8), - UNWRAP(NodeType.All, 1, 21), - CUT_TO_PIECES(NodeType.All, 1, 21), + WRAP_INTO_GIFT_PACKAGE(NodeType.Seller, ProductType.ALL, 1, -8), + UNWRAP(NodeType.All, ProductType.ALL, 1, 21), + CUT_TO_PIECES(NodeType.All, ProductType.ALL, 1, 21), - BOIL_MEAT(NodeType.All, 1, 21), - GRILL_MEAT(NodeType.All, 1, 21), - SMOKE_MEAT(NodeType.All, 1, 21), - DRY_MEAT(NodeType.All, 1, 21), + BOIL_MEAT(NodeType.All, ProductType.MEAT, 1, 21), + GRILL_MEAT(NodeType.All, ProductType.MEAT, 1, 21), + SMOKE_MEAT(NodeType.All, ProductType.MEAT, 1, 21), + DRY_MEAT(NodeType.All, ProductType.MEAT, 1, 21), - DRY_FRUIT(NodeType.All, 1, 21), - FREEZE_DRY_FRUIT(NodeType.All, 1, 21); - - - //todo add product type allowed for this action + DRY_FRUIT(NodeType.All, ProductType.FRUIT, 1, 21), + FREEZE_DRY_FRUIT(NodeType.All, ProductType.FRUIT, 1, 21); private final NodeType node; + private final ProductType product; private final int duration; private final int temperature; - ProductOperationType(NodeType node, int duration, int temperature) + ProductOperationType(NodeType node, ProductType product, int duration, int temperature) { this.node = node; + this.product = product; this.duration = duration; this.temperature = temperature; } @@ -49,4 +49,6 @@ public enum ProductOperationType { return node; } + + public ProductType getProduct() { return product; } } diff --git a/src/main/java/cz/cvut/fel/sit/omo/foodchain/operation/ProductTransaction.java b/src/main/java/cz/cvut/fel/sit/omo/foodchain/operation/ProductTransaction.java index c081341..a4d9bba 100644 --- a/src/main/java/cz/cvut/fel/sit/omo/foodchain/operation/ProductTransaction.java +++ b/src/main/java/cz/cvut/fel/sit/omo/foodchain/operation/ProductTransaction.java @@ -18,17 +18,17 @@ public class ProductTransaction extends Transaction { this.product = product; type = null; lastOperation = product.getCurrentOperation(); - if (product == null || product.getProductState() != ProductState.AVAILABLE) throw new InsufficientResourcesException("product not available"); + if (product == null || product.getProductState() != ProductState.IN_REQUIREMENT) throw new InsufficientResourcesException("product unavailable"); product.setCurrentOperation(this); product.setStateToReadyForDispatch(); - creator.getStorage().remove(product); + creator.getStorage().removeProduct(product); System.out.println(toString()); } @Override public void endTransaction() { super.endTransaction(); - getNodeReceiver().storeProduct(product); + getNodeReceiver().getStorage().storeProduct(product); product.setStateToAvailable(); } diff --git a/src/main/java/cz/cvut/fel/sit/omo/foodchain/product/Product.java b/src/main/java/cz/cvut/fel/sit/omo/foodchain/product/Product.java index 089fc7e..a55d7df 100644 --- a/src/main/java/cz/cvut/fel/sit/omo/foodchain/product/Product.java +++ b/src/main/java/cz/cvut/fel/sit/omo/foodchain/product/Product.java @@ -71,22 +71,27 @@ public class Product // "State machine methods" /** - * Set current product state to AVAILABLE + * Set current product state to AVAILABLE. Throws IllegalStateException if the product is not IN_ACTION, READY_FOR_RECEPTION or IN_REQUIREMENT. */ public void setStateToAvailable() { - this.productState = ProductState.AVAILABLE; + if (this.productState.equals(ProductState.IN_ACTION) || + this.productState.equals(ProductState.READY_FOR_RECEPTION) || + this.productState.equals(ProductState.IN_REQUIREMENT)) + this.productState = ProductState.AVAILABLE; + else + throw new IllegalStateException("Cannot set state AVAILABLE. Product is not IN_ACTION, READY_FOR_RECEPTION or IN_REQUIREMENT"); } /** - * Set current product state to READY_FOR_DISPATCH. Throws IllegalStateException if the product is not AVAILABLE. + * Set current product state to READY_FOR_DISPATCH. Throws IllegalStateException if the product is not IN_REQUIREMENT. */ public void setStateToReadyForDispatch() { - if(this.productState.equals(ProductState.AVAILABLE)) + if(this.productState.equals(ProductState.IN_REQUIREMENT)) this.productState = ProductState.READY_FOR_DISPATCH; else - throw new IllegalStateException("Cannot set state READY_FOR_DISPATCH. Product is not AVAILABLE!"); + throw new IllegalStateException("Cannot set state READY_FOR_DISPATCH. Product is not IN_REQUIREMENT!"); } /** @@ -122,6 +127,17 @@ public class Product throw new IllegalStateException("Cannot set state IN_ACTION. Product is not AVAILABLE!"); } + /** + * Set current product state to IN_REQUIREMENT. Throws IllegalStateException if the product is not AVAILABLE. + */ + public void setStateToInRequirement() + { + if(this.productState.equals(ProductState.AVAILABLE)) + this.productState = ProductState.IN_REQUIREMENT; + else + throw new IllegalStateException("Cannot set state IN_REQUIREMENT. Product is not AVAILABLE!"); + } + /* public void setProductState(ProductState productState) { diff --git a/src/main/java/cz/cvut/fel/sit/omo/foodchain/product/ProductState.java b/src/main/java/cz/cvut/fel/sit/omo/foodchain/product/ProductState.java index 906282e..7e4be38 100644 --- a/src/main/java/cz/cvut/fel/sit/omo/foodchain/product/ProductState.java +++ b/src/main/java/cz/cvut/fel/sit/omo/foodchain/product/ProductState.java @@ -2,5 +2,5 @@ package cz.cvut.fel.sit.omo.foodchain.product; public enum ProductState { - AVAILABLE, READY_FOR_DISPATCH, IN_TRANSIT, READY_FOR_RECEPTION, IN_ACTION + AVAILABLE, READY_FOR_DISPATCH, IN_TRANSIT, READY_FOR_RECEPTION, IN_ACTION, IN_REQUIREMENT } diff --git a/src/main/java/cz/cvut/fel/sit/omo/foodchain/product/ProductType.java b/src/main/java/cz/cvut/fel/sit/omo/foodchain/product/ProductType.java index 533c3c3..683936c 100644 --- a/src/main/java/cz/cvut/fel/sit/omo/foodchain/product/ProductType.java +++ b/src/main/java/cz/cvut/fel/sit/omo/foodchain/product/ProductType.java @@ -2,5 +2,5 @@ package cz.cvut.fel.sit.omo.foodchain.product; public enum ProductType { - MEAT, FRUIT, VEGETABLE, OTHER; + MEAT, FRUIT, VEGETABLE, OTHER, ALL // ALL is used only for actions } diff --git a/src/main/java/cz/cvut/fel/sit/omo/foodchain/scenario/DoActionStep.java b/src/main/java/cz/cvut/fel/sit/omo/foodchain/scenario/DoActionStep.java index f934978..97fef11 100644 --- a/src/main/java/cz/cvut/fel/sit/omo/foodchain/scenario/DoActionStep.java +++ b/src/main/java/cz/cvut/fel/sit/omo/foodchain/scenario/DoActionStep.java @@ -22,7 +22,6 @@ public class DoActionStep extends ScenarioStep { @Override void execute() { - //todo maybe do a better error handling try { if (product != null) { nodeBase.doAction(productOperationType, product); diff --git a/src/main/java/cz/cvut/fel/sit/omo/foodchain/scenario/Scenario.java b/src/main/java/cz/cvut/fel/sit/omo/foodchain/scenario/Scenario.java index c7e3063..96bc592 100644 --- a/src/main/java/cz/cvut/fel/sit/omo/foodchain/scenario/Scenario.java +++ b/src/main/java/cz/cvut/fel/sit/omo/foodchain/scenario/Scenario.java @@ -8,7 +8,6 @@ public class Scenario { Iterator stepIterator; public Scenario() { - //steps = //TODO init stepIterator = steps.listIterator(0); }