Merge branch 'develop' into feature/text_report_impl

This commit is contained in:
Nikita Iryupin 2021-01-03 11:43:02 +01:00
commit 5e9113cb98
15 changed files with 230 additions and 80 deletions

Binary file not shown.

View File

@ -10,6 +10,7 @@
generate generate
consume consume
doaction <product-operation-type> doaction <product-operation-type>
doaction <product-operation-type> <product-name>
sell <product-type> <product-name> sell <product-type> <product-name>
buy <product-type> <produt-name> <product-price> buy <product-type> <produt-name> <product-price>
pass pass

View File

@ -46,8 +46,6 @@ public class FoodChain
return factoryManager; return factoryManager;
} }
// todo maybe some refactoring needed?
// add other entities
public static void parseChannels() public static void parseChannels()
{ {
try try

View File

@ -56,6 +56,9 @@ public abstract class ChannelBase implements Observable {
public boolean tick() { public boolean tick() {
requirements.forEach(Requirement::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)); requirements = requirements.stream().filter(r -> r.getTTL() > 0).collect(Collectors.toCollection(LinkedList::new));
notifyAllObservers(); notifyAllObservers();
return requirements.size() != 0; return requirements.size() != 0;

View File

@ -11,11 +11,11 @@ public class Customer extends BuyerNodeBase {
super(startMoney); super(startMoney);
allowedSeller = NodeType.Distributor; allowedSeller = NodeType.Distributor;
allowedPurchaser = NodeType.None; allowedPurchaser = NodeType.None;
this.nodeType = NodeType.Customer; nodeType = NodeType.Customer;
} }
public void consumeProduct() { public void consumeProduct() {
int i = new Random().nextInt(getStorage().size()); int i = new Random().nextInt(getStorage().getNumberOfStoredProducts());
getStorage().remove(i); getStorage().getProducts().remove(i);
} }
} }

View File

@ -13,11 +13,11 @@ public class Farmer extends SellerNodeBase {
super(startMoney); super(startMoney);
allowedSeller = NodeType.None; allowedSeller = NodeType.None;
allowedPurchaser = NodeType.Processor; allowedPurchaser = NodeType.Processor;
this.nodeType = NodeType.Farmer; nodeType = NodeType.Farmer;
} }
public void generateProduct(Product product) { public void generateProduct(Product product) {
storeProduct(product); this.storage.storeProduct(product);
try { try {
createRequirement(TransactionType.SALE, product.getProductType(), product.getName(), product.getPrice()); createRequirement(TransactionType.SALE, product.getProductType(), product.getName(), product.getPrice());
} catch (InsufficientResourcesException e) { } catch (InsufficientResourcesException e) {

View File

@ -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<Product> 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<Product> 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<Product> 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));
}
}

View File

@ -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.ChannelBase;
import cz.cvut.fel.sit.omo.foodchain.channel.ChannelSubscriptionType; 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.node.enums.NodeType;
import cz.cvut.fel.sit.omo.foodchain.operation.*; import cz.cvut.fel.sit.omo.foodchain.operation.*;
import cz.cvut.fel.sit.omo.foodchain.product.Product; import cz.cvut.fel.sit.omo.foodchain.product.Product;
@ -15,35 +16,30 @@ import org.javamoney.moneta.Money;
import javax.money.MonetaryAmount; import javax.money.MonetaryAmount;
import javax.naming.InsufficientResourcesException; import javax.naming.InsufficientResourcesException;
import java.util.HashMap; import java.util.*;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class NodeBase implements ChannelObserver { public class NodeBase implements ChannelObserver {
private final Map<ChannelBase, ChannelSubscriptionType> subscribedChannels; private final Map<ChannelBase, ChannelSubscriptionType> subscribedChannels = new HashMap<>();
private final List<Product> products; private final List<Transaction> refusedTransactions = new LinkedList<>();
private final List<Transaction> refusedTransactions; private final List<RefusedRequirement> refusedRequirements = new LinkedList<>();
private MonetaryAmount money; private MonetaryAmount money;
protected final Storage storage;
protected NodeType nodeType; protected NodeType nodeType;
protected NodeType allowedSeller; protected NodeType allowedSeller;
protected NodeType allowedPurchaser; protected NodeType allowedPurchaser;
public NodeBase() { public NodeBase() {
this.nodeType = NodeType.None; this.nodeType = NodeType.None;
this.subscribedChannels = new HashMap<>(); this.storage = new Storage(this);
this.products = new LinkedList<>();
this.refusedTransactions = new LinkedList<>();
this.money = Money.of(0, "USD"); this.money = Money.of(0, "USD");
} }
public NodeBase(double m) { public NodeBase(double m) {
this.subscribedChannels = new HashMap<>(); this.nodeType = NodeType.None;
this.products = new LinkedList<>(); this.storage = new Storage(this);
this.refusedTransactions = new LinkedList<>();
this.money = Money.of(m, "USD"); this.money = Money.of(m, "USD");
} }
@ -55,7 +51,7 @@ public class NodeBase implements ChannelObserver {
.filter(ch -> ch.getProductType().equals(product.getProductType())) .filter(ch -> ch.getProductType().equals(product.getProductType()))
.findAny().ifPresent(channel -> channel.addTransaction(transaction)); .findAny().ifPresent(channel -> channel.addTransaction(transaction));
} catch (InsufficientResourcesException e) { } 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())) .filter(ch -> ch.getProductType().equals(prevTransaction.getProduct().getProductType()))
.findAny().ifPresent(channel -> channel.addTransaction(transaction)); .findAny().ifPresent(channel -> channel.addTransaction(transaction));
} catch (InsufficientResourcesException e) { } 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 { public void createRequirement(TransactionType tType, ProductType pType, String pName, MonetaryAmount price) throws InsufficientResourcesException {
if (price == null) { if (price == null) {
try { try {
price = getProductFromStorage(pName, pType).getPrice(); price = storage.getProduct(pName, pType).getPrice();
} }
catch (NullPointerException e) { 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"); throw new InsufficientResourcesException("product unavailable");
} }
} }
@ -139,9 +145,9 @@ public class NodeBase implements ChannelObserver {
createRequirement(TransactionType.PURCHASE, req.getProductType(), req.getProductName(), req.getPrice()); createRequirement(TransactionType.PURCHASE, req.getProductType(), req.getProductName(), req.getPrice());
} }
else { else {
if (req.getPrice().isGreaterThanOrEqualTo(getProductFromStorage(req.getProductName(), req.getProductType()).getPrice().multiply(0.9))) { if (req.getPrice().isGreaterThanOrEqualTo(storage.getProduct(req.getProductName(), req.getProductType()).getPrice().multiply(0.9))) {
getProductFromStorage(req.getProductName(), req.getProductType()).setPrice(req.getPrice()); storage.getProduct(req.getProductName(), req.getProductType()).setPrice(req.getPrice());
sendTransaction(getProductFromStorage(req.getProductName(), req.getProductType()), req.getCreator()); sendTransaction(storage.getProduct(req.getProductName(), req.getProductType()), req.getCreator());
} }
else createRequirement(TransactionType.SALE, req.getProductType(), req.getProductName(), req.getPrice()); 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 action type of product operation to do
* @param product product to proceed * @param product product to proceed
*/ */
public void doAction(ProductOperationType action, Product product) throws IllegalCallerException public void doAction(ProductOperationType action, Product product) throws IllegalCallerException
{ {
if(action.getNode().equals(this.nodeType)) this.storage.doAction(action,product);
new Action(this, product, action);
else
throw new IllegalCallerException("! IllegalCallerException in NodeBase. " + this.nodeType.toString() + " cannot do action available only for "+action.getNode());
} }
/** /**
* Calls specific ProductOperation on each product in stock * Calls specific ProductOperation on each product in stock (Propagated to its storage)
* @param action ProductOperationType * @param action ProductOperationType
*/ */
public void doAction(ProductOperationType action) public void doAction(ProductOperationType action)
{ {
this.products.forEach(t -> new Action(this, t, action)); this.storage.doAction(action);
} }
public Storage getStorage() {
public void storeProduct(Product product) { return storage;
this.products.add(product);
} }
public NodeType getNodeType() {
public void storeProducts(List<Product> products) { return nodeType;
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<Product> getStorage() {
return products;
} }
public MonetaryAmount getMoney() { public MonetaryAmount getMoney() {
@ -263,9 +254,10 @@ public class NodeBase implements ChannelObserver {
} }
boolean requirementCheck(Requirement req) { boolean requirementCheck(Requirement req) {
Product product = storage.getProduct(req.getProductName(), req.getProductType());
return (req.getTransactionType() == TransactionType.SALE && money.isGreaterThanOrEqualTo(req.getPrice())) || return (req.getTransactionType() == TransactionType.SALE && money.isGreaterThanOrEqualTo(req.getPrice())) ||
(req.getTransactionType() == TransactionType.PURCHASE && getProductFromStorage(req.getProductName(), req.getProductType()) != null && (req.getTransactionType() == TransactionType.PURCHASE && product != null &&
getProductFromStorage(req.getProductName(), req.getProductType()).getProductState() == ProductState.AVAILABLE); ((product.getProductState() == ProductState.AVAILABLE) || product.getProductState() == ProductState.IN_REQUIREMENT));
} }
@Override @Override
@ -285,6 +277,20 @@ public class NodeBase implements ChannelObserver {
//System.out.println(this.getClass().getSimpleName() + ": " + money.getNumber()); //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){ public String accept(ReportTextVisitor visitor){
return visitor.visit(this); return visitor.visit(this);
} }

View File

@ -1,36 +1,36 @@
package cz.cvut.fel.sit.omo.foodchain.operation; 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.node.enums.NodeType;
import cz.cvut.fel.sit.omo.foodchain.product.ProductType;
public enum ProductOperationType public enum ProductOperationType
{ {
STORE_MEAT(NodeType.All, 20, -8), STORE_MEAT(NodeType.All, ProductType.MEAT, 20, -8),
STORE_FRUIT(NodeType.All, 8, 2), STORE_FRUIT(NodeType.All, ProductType.FRUIT, 8, 2),
STORE_VEGETABLE(NodeType.All, 10, 2), STORE_VEGETABLE(NodeType.All, ProductType.VEGETABLE, 10, 2),
STORE_OTHER(NodeType.All, 30, 21), STORE_OTHER(NodeType.All, ProductType.OTHER, 30, 21),
WRAP_INTO_GIFT_PACKAGE(NodeType.Seller, 1, -8), WRAP_INTO_GIFT_PACKAGE(NodeType.Seller, ProductType.ALL, 1, -8),
UNWRAP(NodeType.All, 1, 21), UNWRAP(NodeType.All, ProductType.ALL, 1, 21),
CUT_TO_PIECES(NodeType.All, 1, 21), CUT_TO_PIECES(NodeType.All, ProductType.ALL, 1, 21),
BOIL_MEAT(NodeType.All, 1, 21), BOIL_MEAT(NodeType.All, ProductType.MEAT, 1, 21),
GRILL_MEAT(NodeType.All, 1, 21), GRILL_MEAT(NodeType.All, ProductType.MEAT, 1, 21),
SMOKE_MEAT(NodeType.All, 1, 21), SMOKE_MEAT(NodeType.All, ProductType.MEAT, 1, 21),
DRY_MEAT(NodeType.All, 1, 21), DRY_MEAT(NodeType.All, ProductType.MEAT, 1, 21),
DRY_FRUIT(NodeType.All, 1, 21), DRY_FRUIT(NodeType.All, ProductType.FRUIT, 1, 21),
FREEZE_DRY_FRUIT(NodeType.All, 1, 21); FREEZE_DRY_FRUIT(NodeType.All, ProductType.FRUIT, 1, 21);
//todo add product type allowed for this action
private final NodeType node; private final NodeType node;
private final ProductType product;
private final int duration; private final int duration;
private final int temperature; private final int temperature;
ProductOperationType(NodeType node, int duration, int temperature) ProductOperationType(NodeType node, ProductType product, int duration, int temperature)
{ {
this.node = node; this.node = node;
this.product = product;
this.duration = duration; this.duration = duration;
this.temperature = temperature; this.temperature = temperature;
} }
@ -49,4 +49,6 @@ public enum ProductOperationType
{ {
return node; return node;
} }
public ProductType getProduct() { return product; }
} }

View File

@ -18,17 +18,17 @@ public class ProductTransaction extends Transaction {
this.product = product; this.product = product;
type = null; type = null;
lastOperation = product.getCurrentOperation(); 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.setCurrentOperation(this);
product.setStateToReadyForDispatch(); product.setStateToReadyForDispatch();
creator.getStorage().remove(product); creator.getStorage().removeProduct(product);
System.out.println(toString()); System.out.println(toString());
} }
@Override @Override
public void endTransaction() { public void endTransaction() {
super.endTransaction(); super.endTransaction();
getNodeReceiver().storeProduct(product); getNodeReceiver().getStorage().storeProduct(product);
product.setStateToAvailable(); product.setStateToAvailable();
} }

View File

@ -71,22 +71,27 @@ public class Product
// "State machine methods" // "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() public void setStateToAvailable()
{ {
if (this.productState.equals(ProductState.IN_ACTION) ||
this.productState.equals(ProductState.READY_FOR_RECEPTION) ||
this.productState.equals(ProductState.IN_REQUIREMENT))
this.productState = ProductState.AVAILABLE; 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() public void setStateToReadyForDispatch()
{ {
if(this.productState.equals(ProductState.AVAILABLE)) if(this.productState.equals(ProductState.IN_REQUIREMENT))
this.productState = ProductState.READY_FOR_DISPATCH; this.productState = ProductState.READY_FOR_DISPATCH;
else 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!"); 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) public void setProductState(ProductState productState)
{ {

View File

@ -2,5 +2,5 @@ package cz.cvut.fel.sit.omo.foodchain.product;
public enum ProductState 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
} }

View File

@ -2,5 +2,5 @@ package cz.cvut.fel.sit.omo.foodchain.product;
public enum ProductType public enum ProductType
{ {
MEAT, FRUIT, VEGETABLE, OTHER; MEAT, FRUIT, VEGETABLE, OTHER, ALL // ALL is used only for actions
} }

View File

@ -22,7 +22,6 @@ public class DoActionStep extends ScenarioStep {
@Override @Override
void execute() { void execute() {
//todo maybe do a better error handling
try { try {
if (product != null) { if (product != null) {
nodeBase.doAction(productOperationType, product); nodeBase.doAction(productOperationType, product);

View File

@ -8,7 +8,6 @@ public class Scenario {
Iterator<ScenarioStep> stepIterator; Iterator<ScenarioStep> stepIterator;
public Scenario() { public Scenario() {
//steps = //TODO init
stepIterator = steps.listIterator(0); stepIterator = steps.listIterator(0);
} }