From 06b1f2df822bc1bfd59a0fa8525ba39b25c16ff5 Mon Sep 17 00:00:00 2001 From: LJ5O <75009579+LJ5O@users.noreply.github.com> Date: Mon, 20 Oct 2025 14:07:37 +0200 Subject: [PATCH] Patch correctif TP2 --- artis-factory.layout | 38 +-- src/FactoryGraphManager.hpp | 161 +++++++++---- src/FactoryGraphManagerParameters.hpp | 128 +++++++++++ src/Item.hpp | 14 +- src/ItemStock.cpp | 42 +++- src/ItemStock.hpp | 67 +++++- src/JsonReader.hpp | 124 +++++++--- src/Machine.hpp | 16 +- src/PoolRouter.cpp | 35 ++- src/PoolRouter.hpp | 19 +- src/Processor.cpp | 248 ++++++++++++++------ src/Processor.hpp | 134 +++++++++-- src/ProductionOrder.cpp | 21 +- src/ProductionOrder.hpp | 25 +- src/ProductionOrderGenerator.cpp | 61 +++-- src/ProductionOrderGenerator.hpp | 14 +- src/Sink.cpp | 66 +++--- src/Sink.hpp | 53 ++--- src/Stock.cpp | 103 ++++++++- src/Stock.hpp | 140 ++++++++++- test/test_json.cpp | 8 +- test/test_optim.cpp | 229 ++++++++++++++++++ test/test_simple.cpp | 319 ++++++++++++++++---------- 23 files changed, 1610 insertions(+), 455 deletions(-) create mode 100644 src/FactoryGraphManagerParameters.hpp create mode 100644 test/test_optim.cpp diff --git a/artis-factory.layout b/artis-factory.layout index 570d6d1..576ad0c 100644 --- a/artis-factory.layout +++ b/artis-factory.layout @@ -2,14 +2,29 @@ - + - + + + + + + + + + + + - + + + + + + @@ -24,7 +39,7 @@ - + @@ -32,19 +47,4 @@ - - - - - - - - - - - - - - - diff --git a/src/FactoryGraphManager.hpp b/src/FactoryGraphManager.hpp index 0551528..bfd915c 100644 --- a/src/FactoryGraphManager.hpp +++ b/src/FactoryGraphManager.hpp @@ -28,24 +28,22 @@ #define ARTIS_FACTORY_FACTORY_GRAPH_MANAGER_HPP #include "Base.hpp" +#include "FactoryGraphManagerParameters.hpp" +#include "ItemStock.hpp" #include "JsonReader.hpp" #include "PoolRouter.hpp" #include "Router.hpp" -#include "Sink.hpp" +#include "Stock.hpp" #include namespace artis::factory { -struct FactoryGraphManagerParameters { - Factory factory; -}; - class FactoryGraphManager : public artis::pdevs::GraphManager { public: enum sub_models { - GENERATOR, ROUTER, POOL_ROUTER, MACHINE = 10000 + GENERATOR, ROUTER, POOL_ROUTER, MACHINE = 10000, ITEM_STOCK = 20000, STOCK = 30000 }; FactoryGraphManager(Coordinator *coordinator, const artis::common::NoParameters ¶meters, @@ -53,27 +51,13 @@ public: artis::pdevs::GraphManager( coordinator, parameters, graph_parameters) { unsigned int machine_index = 0; - std::map programs; - for (const auto &product: graph_parameters.factory._products) { - unsigned int product_id = product.first; - Program program; - - for (const auto &pool_job: product.second._pool_jobs) { - std::vector machines; - - for (const auto &machine_job: pool_job._machine_jobs) { - machines.push_back(machine_job._machine_id); - } - program.emplace_back((uint8_t) pool_job._pool_id, machines); - } - programs[product_id] = program; - } - - ProductionOrderGeneratorParameters generator_parameters{programs, + ProductionOrderGeneratorParameters generator_parameters{graph_parameters.get_programs(), graph_parameters.factory._generator._random_seed, graph_parameters.factory._generator._min_send_speed_rate, - graph_parameters.factory._generator._max_send_speed_rate}; + graph_parameters.factory._generator._max_send_speed_rate, + graph_parameters.factory._generator._po_number, + graph_parameters.factory._generator._po_order}; _generator = new GeneratorSimulator("G", generator_parameters); _router = new RouterSimulator("R", {graph_parameters.factory._pools.size()}); @@ -81,45 +65,131 @@ public: this->add_child(ROUTER, _router); this->add_child(GENERATOR, _generator); + // item stocks + for (const auto &p: graph_parameters.factory._stocks) { + _item_stocks.push_back( + new ItemStockSimulator("I_S_" + std::to_string(p._id), + ItemStockParameters{graph_parameters.get_item_stock(p._id), + graph_parameters.get_machines_to_item_stock(p._id)})); + this->add_child(ITEM_STOCK + p._id, _item_stocks.back()); + } + + // intermediate stocks for (const auto &p: graph_parameters.factory._pools) { + unsigned int pool_id = std::get<0>(p); + for (const auto &s: std::get<3>(p)) { + _stocks[std::make_pair(pool_id, s._id)] = new StockSimulator( + "S_" + std::to_string(pool_id) + "_" + std::to_string(s._id), + StockParameters{graph_parameters.get_machines_to_in_stock(pool_id, s._id), + graph_parameters.get_machines_to_out_stock(pool_id, s._id), + s._capacity}); + this->add_child(STOCK + pool_id * 100 + s._id, _stocks[std::make_pair(pool_id, s._id)]); + } + } + + // pool + machines + for (const auto &p: graph_parameters.factory._pools) { + unsigned int pool_id = std::get<0>(p); + std::map capacities; + + for (const auto &m: std::get<2>(p)) { + capacities[m->machine_id] = m->capacity; + } _pool_routers.push_back( - new PoolRouterSimulator("P_R_" + std::to_string(std::get<0>(p)), - PoolRouterParameters{std::get<1>(p), std::get<2>(p).size()})); + new PoolRouterSimulator("P_R_" + std::to_string(pool_id), PoolRouterParameters{std::get<1>(p), capacities})); this->add_child(POOL_ROUTER + std::get<0>(p), _pool_routers.back()); - out({_router, artis::factory::Router::outputs::OUT_P + std::get<0>(p)}) + out({_router, artis::factory::Router::outputs::OUT_P + pool_id}) >> in({_pool_routers.back(), artis::factory::PoolRouter::inputs::IN}); out({_pool_routers.back(), artis::factory::PoolRouter::outputs::OUT}) - >> in({_router, artis::factory::Router::inputs::IN_P + std::get<0>(p)}); + >> in({_router, artis::factory::Router::inputs::IN_P + pool_id}); + + // machines for (const auto &m: std::get<2>(p)) { switch (m->machine_type) { case PROCESSOR: { + auto &processor_parameters = (ProcessorParameters &) (*m); auto new_processor = new ProcessorSimulator( - "M_" + std::to_string(m->pool_id) + "_" + std::to_string(m->machine_id), (ProcessorParameters &) (*m)); + "M_" + std::to_string(m->pool_id) + "_" + std::to_string(m->machine_id), processor_parameters); _processors.push_back(new_processor); this->add_child(MACHINE + machine_index, new_processor); + + // connection processor <-> pool router out({_pool_routers.back(), artis::factory::PoolRouter::outputs::OUT_M + m->machine_id}) >> in({new_processor, artis::factory::Processor::inputs::IN}); out({new_processor, artis::factory::Processor::outputs::OUT}) >> in({_pool_routers.back(), artis::factory::PoolRouter::inputs::IN_M + m->machine_id}); + + // connection processor <-> item stock + std::vector item_stocks; + + for (const auto &q: processor_parameters.in_out) { + for (const auto &r: q.second._ins) { + if (r._source._pool_id == -1) { + item_stocks.push_back(r._source._stock_id); + } + } + } + for (const auto &q: item_stocks) { + out({new_processor, artis::factory::Processor::outputs::OUT_ITEM_DEMAND + q}) + >> in({_item_stocks.back(), artis::factory::ItemStock::inputs::IN + m->machine_id}); + out({_item_stocks.back(), artis::factory::ItemStock::outputs::OUT + m->machine_id}) + >> in({new_processor, artis::factory::Processor::inputs::IN_ITEM}); + } + // connection processor <-> intermediate stock + std::vector> stocks; + + for (const auto &q: processor_parameters.in_out) { + for (const auto &r: q.second._outs) { + auto u = std::make_pair((unsigned int) r._destination._pool_id, r._destination._stock_id); + + if (r._destination._pool_id != -1 and std::find(stocks.cbegin(), stocks.cend(), u) == stocks.cend()) { + stocks.push_back(u); + } + } + } + for (const auto &q: stocks) { + out({new_processor, artis::factory::Processor::outputs::OUT_STOCK + (q.first * 100 + q.second)}) + >> in({_stocks[q], artis::factory::Stock::inputs::IN}); + out({new_processor, artis::factory::Processor::outputs::OUT_STOCK_AVAILABLE + (q.first * 100 + q.second)}) + >> in({_stocks[q], artis::factory::Stock::inputs::IN_STOCK_AVAILABLE + m->machine_id}); + out({_stocks[q], artis::factory::Stock::outputs::OUT_STOCK_AVAILABLE + m->machine_id}) + >> in( + {new_processor, artis::factory::Processor::inputs::IN_STOCK_AVAILABLE + (q.first * 100 + q.second)}); + } + stocks.clear(); + for (const auto &q: processor_parameters.in_out) { + for (const auto &r: q.second._ins) { + auto u = std::make_pair((unsigned int) r._source._pool_id, r._source._stock_id); + + if (r._source._pool_id != -1 and std::find(stocks.cbegin(), stocks.cend(), u) == stocks.cend()) { + stocks.push_back(u); + } + } + } + for (const auto &q: stocks) { + out({new_processor, artis::factory::Processor::outputs::OUT_STOCK_DEMAND + (q.first * 100 + q.second)}) + >> in({_stocks[q], artis::factory::Stock::inputs::IN_DEMAND + m->machine_id}); + out({_stocks[q], artis::factory::Stock::outputs::OUT + m->machine_id}) + >> in({new_processor, artis::factory::Processor::inputs::IN_STOCK}); + } ++machine_index; break; } case SINK: { - auto new_sink = new SinkSimulator( - "M_" + std::to_string(m->pool_id) + "_" + std::to_string(m->machine_id), - SinkParameters{} - ); + auto new_sink = new SinkSimulator( + "M_" + std::to_string(m->pool_id) + "_" + std::to_string(m->machine_id), (SinkParameters &) (*m)); - _sinks.push_back(new_sink); - this->add_child(MACHINE + machine_index, new_sink); + _sinks.push_back(new_sink); + this->add_child(MACHINE + machine_index, new_sink); - // PoolRouter -> Sink - out({_pool_routers.back(), artis::factory::PoolRouter::outputs::OUT_M + m->machine_id}) - >> in({_sinks.back(), artis::factory::Sink::inputs::IN}); - - ++machine_index; - break; + // connection sink <-> pool router + out({_pool_routers.back(), artis::factory::PoolRouter::outputs::OUT_M + m->machine_id}) + >> in({new_sink, artis::factory::Sink::inputs::IN}); + out({new_sink, artis::factory::Sink::outputs::OUT}) + >> in({_pool_routers.back(), artis::factory::PoolRouter::inputs::FINISH}); + ++machine_index; + break; } case SEPARATOR: { // TODO @@ -129,11 +199,12 @@ public: // TODO break; } - case CONVEYOR :{ + case CONVEYOR : { // TODO break; } - default: {} + default: { + } } } } @@ -147,12 +218,16 @@ private: typedef artis::pdevs::Simulator PoolRouterSimulator; typedef artis::pdevs::Simulator ProcessorSimulator; typedef artis::pdevs::Simulator SinkSimulator; + typedef artis::pdevs::Simulator ItemStockSimulator; + typedef artis::pdevs::Simulator StockSimulator; GeneratorSimulator *_generator; RouterSimulator *_router; std::vector _pool_routers; std::vector _processors; std::vector _sinks; + std::vector _item_stocks; + std::map, StockSimulator *> _stocks; }; } diff --git a/src/FactoryGraphManagerParameters.hpp b/src/FactoryGraphManagerParameters.hpp new file mode 100644 index 0000000..691cdec --- /dev/null +++ b/src/FactoryGraphManagerParameters.hpp @@ -0,0 +1,128 @@ +/** + * @file FactoryGraphManagerParameters.hpp + * @author The ARTIS Development Team + * See the AUTHORS or Authors.txt file + */ + +/* + * ARTIS - the multimodeling and simulation environment + * This file is a part of the ARTIS environment + * + * Copyright (C) 2013-2023 ULCO http://www.univ-littoral.fr + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ARTIS_FACTORY_FACTORY_GRAPH_MANAGER_PARAMETERS_HPP +#define ARTIS_FACTORY_FACTORY_GRAPH_MANAGER_PARAMETERS_HPP + +#include "JsonReader.hpp" + +namespace artis::factory { + +struct FactoryGraphManagerParameters { + Factory factory; + + std::map get_item_stock(unsigned int id) const { + std::map items; + + auto it = std::find_if(factory._stocks.cbegin(), factory._stocks.cend(), + [id](const auto &e) { return e._id == id; }); + if (it != factory._stocks.cend()) { + for (const auto &e: it->_items) { + items[e._id] = e._quantity; + } + } + return items; + } + + std::vector get_machines_to_item_stock(unsigned int id) const { + std::vector machines; + + for (const auto &p: factory._products) { + for (const auto &q: p.second._pool_jobs) { + for (const auto &r: q._machine_jobs) { + for (const auto &s: r._ins) { + if (s._source._stock_id == id and + std::find(machines.cbegin(), machines.cend(), r._machine_id) == machines.cend()) { + machines.push_back(r._machine_id); + } + } + } + } + } + return machines; + } + + std::vector get_machines_to_in_stock(unsigned int pool_id, unsigned int stock_id) const { + std::vector machines; + + for (const auto &p: factory._products) { + for (const auto &q: p.second._pool_jobs) { + for (const auto &r: q._machine_jobs) { + for (const auto &s: r._ins) { + if (s._source._pool_id == (int) pool_id and s._source._stock_id == stock_id and + std::find(machines.cbegin(), machines.cend(), r._machine_id) == machines.cend()) { + machines.push_back(r._machine_id); + } + } + } + } + } + return machines; + } + + std::vector get_machines_to_out_stock(unsigned int pool_id, unsigned int stock_id) const { + std::vector machines; + + for (const auto &p: factory._products) { + for (const auto &q: p.second._pool_jobs) { + for (const auto &r: q._machine_jobs) { + for (const auto &s: r._outs) { + if (s._destination._pool_id == (int) pool_id and s._destination._stock_id == stock_id and + std::find(machines.cbegin(), machines.cend(), r._machine_id) == machines.cend()) { + machines.push_back(r._machine_id); + } + } + } + } + } + return machines; + } + + std::map get_programs() const { + std::map programs; + + for (const auto &product: factory._products) { + unsigned int product_id = product.first; + Program program; + + for (const auto &pool_job: product.second._pool_jobs) { + std::vector machines; + + for (const auto &machine_job: pool_job._machine_jobs) { + machines.push_back(machine_job._machine_id); + } + program.emplace_back((uint8_t) pool_job._pool_id, machines); + } + programs[product_id] = program; + } + return programs; + } + +}; + +} + +#endif //ARTIS_FACTORY_FACTORY_GRAPH_MANAGER_PARAMETERS_HPP diff --git a/src/Item.hpp b/src/Item.hpp index bfaaad3..7973995 100644 --- a/src/Item.hpp +++ b/src/Item.hpp @@ -1,5 +1,5 @@ /** - * @file Item.hpp +* @file Item.hpp * @author The ARTIS Development Team * See the AUTHORS or Authors.txt file */ @@ -29,13 +29,13 @@ namespace artis::factory { -struct BaseItem { - unsigned int _id; -}; + struct BaseItem { + unsigned int _id; + }; -struct Item { - unsigned int _po_id; -}; + struct Item { + unsigned int _po_id; + }; } diff --git a/src/ItemStock.cpp b/src/ItemStock.cpp index 79cb840..555285c 100644 --- a/src/ItemStock.cpp +++ b/src/ItemStock.cpp @@ -29,28 +29,58 @@ namespace artis::factory { void ItemStock::dint(const Time & /* t */) { - // TODO + switch (_phase) { + case Phase::SEND: { + _demands.clear(); + _phase = Phase::WAIT; + break; + } + case Phase::WAIT: { + assert(false); + } + } } void ItemStock::dext(const Time & /* t */, const Time & /* e */, const Bag &bag) { - std::for_each(bag.begin(), bag.end(), [](const ExternalEvent & /* event */) { - // TODO + std::for_each(bag.begin(), bag.end(), [this](const ExternalEvent &event) { + ItemDemand demand; + + event.data()(demand); + if (_items[demand.item_id] == -1 or _items[demand.item_id] >= (int) demand.quantity) { + _demands.push_back(std::make_pair(event.port_index() - inputs::IN, demand)); + if (_items[demand.item_id] != -1) { + _items[demand.item_id] -= (int) demand.quantity; + } + _phase = Phase::SEND; + } else { + _unsatisfied_demands.push_back(std::make_pair(event.port_index() - inputs::IN, demand)); + } }); } void ItemStock::start(const Time & /* t */) { - // TODO + _phase = Phase::WAIT; + _items = _parameters._items; } Time ItemStock::ta(const Time & /* t */) const { - // TODO + switch (_phase) { + case Phase::WAIT: + return artis::common::DoubleTime::infinity; + case Phase::SEND: + return 0; + } return artis::common::DoubleTime::infinity; } Bag ItemStock::lambda(const Time & /* t */) const { Bag bag; - // TODO + if (_phase == Phase::SEND) { + for (const auto &e: _demands) { + bag.push_back(ExternalEvent(outputs::OUT + e.first, ItemResponse(e.second.item_id, e.second.po_id))); + } + } return bag; } diff --git a/src/ItemStock.hpp b/src/ItemStock.hpp index 58f280e..918e48b 100644 --- a/src/ItemStock.hpp +++ b/src/ItemStock.hpp @@ -32,7 +32,42 @@ namespace artis::factory { +struct ItemDemand { + int item_id; + int po_id; + unsigned int quantity; + + ItemDemand(int item_id = -1, int po_id = -1, unsigned int quantity = 0) : item_id(item_id), po_id(po_id), + quantity(quantity) {} + + bool operator==(const ItemDemand &other) const { + return item_id == other.item_id and po_id == other.po_id and quantity == other.quantity; + } + + std::string to_string() const { + return "Demand < " + std::to_string(item_id) + " | " + std::to_string(po_id) + " | " + std::to_string(quantity) + + " >"; + } +}; + +struct ItemResponse { + int item_id; + int po_id; + + ItemResponse(int item_id = -1, int po_id = -1) : item_id(item_id), po_id(po_id) {} + + bool operator==(const ItemResponse &other) const { + return item_id == other.item_id and po_id == other.po_id; + } + + std::string to_string() const { + return "Response < " + std::to_string(item_id) + " | " + std::to_string(po_id) + " >"; + } +}; + struct ItemStockParameters { + std::map _items; + std::vector _machines; }; class ItemStock : public Dynamics { @@ -55,9 +90,11 @@ public: }; ItemStock(const std::string &name, const Context &context) - : Dynamics(name, context) { - input_port({inputs::IN, "in"}); - output_port({outputs::OUT, "out"}); + : Dynamics(name, context), _parameters(context.parameters()) { + for (const auto &m: _parameters._machines) { + input_port({inputs::IN + m, "in_" + std::to_string(m)}); + output_port({outputs::OUT + m, "out_" + std::to_string(m)}); + } } ~ItemStock() override = default; @@ -75,7 +112,29 @@ public: artis::common::event::Value observe(const Time &t, unsigned int index) const override; private: - // TODO (state) + struct Phase { + enum values { + WAIT, + SEND + }; + + static std::string to_string(const values &value) { + switch (value) { + case WAIT: + return "WAIT"; + case SEND: + return "SEND"; + } + return ""; + } + }; + + ItemStockParameters _parameters; + + Phase::values _phase; + std::map _items; + std::vector> _demands; + std::vector> _unsatisfied_demands; }; } // namespace artis::factory diff --git a/src/JsonReader.hpp b/src/JsonReader.hpp index 0889edb..2d02537 100644 --- a/src/JsonReader.hpp +++ b/src/JsonReader.hpp @@ -39,27 +39,11 @@ namespace artis::factory { -struct Location { - int _pool_id; - unsigned int _stock_id; -}; - -struct In { - int _id; // -1 if global - Location _source; - unsigned int _quantity; -}; - -struct Out { - int _id; // -1 if global - Location _destination; - unsigned int _quantity; -}; - struct MachineJob { unsigned int _machine_id; std::vector _ins; std::vector _outs; + std::shared_ptr _durations; }; struct PoolJob { @@ -69,16 +53,20 @@ struct PoolJob { struct Product { unsigned int _id; + std::vector> _components; std::vector _pool_jobs; + bool final; }; struct Generator { uint _random_seed; uint _min_send_speed_rate; uint _max_send_speed_rate; + int _po_number; + std::vector _po_order; }; -struct Stock { +struct IntermediateStock { unsigned int _id; int _capacity; // if -1 then infinity }; @@ -88,16 +76,16 @@ struct Item { int _quantity; // if -1 then infinity }; -struct ItemStock { +struct GlobalStock { unsigned int _id; std::vector _items; }; struct Factory { Generator _generator; - std::vector>, std::vector >> _pools; + std::vector>, std::vector >> _pools; std::map _products; - std::vector _stocks; + std::vector _stocks; }; class JsonReader { @@ -114,6 +102,7 @@ public: parse_products(data["products"]); parse_generator(data["generator"]); parse_pools(data["pools"]); + parse_product_job_durations(data["products"]); if (data.contains("stocks")) { parse_item_stocks(data["stocks"]); } @@ -123,7 +112,8 @@ private: void parse_generator(const nlohmann::json &data) { _factory._generator = Generator{data["random_seed"].get(), data["min_send_speed_rate"].get(), - data["max_send_speed_rate"].get()}; + data["max_send_speed_rate"].get(), + -1, {}}; } std::vector parse_ins(const nlohmann::json &data) { @@ -144,8 +134,22 @@ private: std::vector> machines; for (const nlohmann::json &machine: data) { + unsigned int machine_id = machine["id"].get(); unsigned int machine_type = machine["type"].get(); + unsigned int capacity = machine.contains("capacity") ? machine["capacity"].get() : 1; + std::map in_out; + for (const auto &product: _factory._products) { + for (const auto &pool_job: product.second._pool_jobs) { + if (pool_job._pool_id == pool_id) { + for (const auto &machine_job: pool_job._machine_jobs) { + if (machine_job._machine_id == machine_id) { + in_out[product.first] = InOut{machine_job._ins, machine_job._outs}; + } + } + } + } + } switch (machine_type) { case PROCESSOR: { unsigned int load_time = machine.contains("load_time") ? machine["load_time"].get() : 0; @@ -154,32 +158,32 @@ private: unsigned int unload_time = machine.contains("unload_time") ? machine["unload_time"].get() : 0; machines.push_back( - std::make_shared(ProcessorParameters{{machine["id"].get(), machine_type, - pool_id}, load_time, processing_time, - unload_time})); + std::make_shared(ProcessorParameters{{machine_id, machine_type, pool_id, capacity}, + ProcessorDurations{{}, load_time, processing_time, + unload_time}, in_out, {}})); break; } case SINK: { - machines.push_back(std::make_shared( - SinkParameters{{machine["id"].get(), machine_type, pool_id}})); + machines.push_back( + std::make_shared(SinkParameters{{machine_id, machine_type, pool_id, capacity}})); break; } case SEPARATOR: { // TODO - machines.push_back(std::make_shared( - SeparatorParameters{{machine["id"].get(), machine_type, pool_id}})); + machines.push_back( + std::make_shared(SeparatorParameters{{machine_id, machine_type, pool_id, capacity}})); break; } case COMBINER : { // TODO - machines.push_back(std::make_shared( - CombinerParameters{{machine["id"].get(), machine_type, pool_id}})); + machines.push_back( + std::make_shared(CombinerParameters{{machine_id, machine_type, pool_id, capacity}})); break; } case CONVEYOR : { // TODO - machines.push_back(std::make_shared( - ConveyorParameters{{machine["id"].get(), machine_type, pool_id}})); + machines.push_back( + std::make_shared(ConveyorParameters{{machine_id, machine_type, pool_id, capacity}})); break; } default: { @@ -207,7 +211,7 @@ private: for (const nlohmann::json &pool: data) { unsigned int pool_id = pool["id"].get(); unsigned int pool_type = pool["type"].get(); - std::vector stocks; + std::vector stocks; if (pool.contains("stocks")) { for (const nlohmann::json &stock: pool["stocks"]) { @@ -224,8 +228,54 @@ private: void parse_products(const nlohmann::json &data) { for (const nlohmann::json &product: data) { unsigned int product_id = product["id"].get(); + bool final = product.contains("final") ? product["final"].get() : true; + std::vector> components; - _factory._products[product_id] = Product{product_id, parse_program(product["program"])}; + if (product.contains("components")) { + for (const nlohmann::json &component: product["components"]) { + components.emplace_back(component["id"].get(), component["number"].get()); + } + } + _factory._products[product_id] = Product{product_id, components, parse_program(product["program"]), final}; + } + } + + void parse_product_job_durations(const nlohmann::json &data) { + for (const nlohmann::json &product: data) { + unsigned int product_id = product["id"].get(); + unsigned int pool_index = 0; + + for (const nlohmann::json &pool_machine: product["program"]) { + unsigned int pool_id = pool_machine["poolID"].get(); + const auto &pool = *std::find_if(_factory._pools.cbegin(), _factory._pools.cend(), + [pool_id](const auto &e) { return std::get<0>(e) == pool_id; }); + const auto &machines = std::get<2>(pool); + unsigned int sequence_index = 0; + + for (const nlohmann::json &machine: pool_machine["sequence"]) { + if (machine.is_object()) { + unsigned int machine_id = machine["machineID"].get(); + const auto &m = *std::find_if(machines.cbegin(), machines.cend(), + [machine_id](const auto &e) { return e->machine_id == machine_id; }); + + if (m->machine_type == PROCESSOR) { + auto &M = (ProcessorParameters &) (*m); + auto d = std::make_shared( + ProcessorDurations{{}, + machine.contains("load_time") ? machine["load_time"].get() : 0, + machine.contains("processing_time") + ? machine["processing_time"].get() : 0, + machine.contains("unload_time") ? machine["unload_time"].get() : 0 + }); + + _factory._products[product_id]._pool_jobs[pool_index]._machine_jobs[sequence_index]._durations = d; + M.product_durations[product_id] = d; + } + } + ++sequence_index; + } + ++pool_index; + } } } @@ -254,9 +304,9 @@ private: if (machine.contains("outs")) { outs = parse_outs(machine["outs"]); } - sequence.push_back(MachineJob{machine["machineID"].get(), ins, outs}); + sequence.push_back(MachineJob{machine["machineID"].get(), ins, outs, {}}); } else { - sequence.push_back(MachineJob{machine.get(), {}, {}}); + sequence.push_back(MachineJob{machine.get(), {}, {}, {}}); } } return sequence; diff --git a/src/Machine.hpp b/src/Machine.hpp index 7dc9f99..3d3e6f3 100644 --- a/src/Machine.hpp +++ b/src/Machine.hpp @@ -1,5 +1,5 @@ /** - * @file Machine.hpp +* @file Machine.hpp * @author The ARTIS Development Team * See the AUTHORS or Authors.txt file */ @@ -32,11 +32,15 @@ namespace artis::factory { -struct MachineParameters { - uint machine_id; - uint machine_type; - uint pool_id; -}; + struct MachineParameters { + uint machine_id; + uint machine_type; + uint pool_id; + uint capacity; + }; + + struct Durations { + }; } // namespace artis::factory diff --git a/src/PoolRouter.cpp b/src/PoolRouter.cpp index 737585d..79fcb42 100644 --- a/src/PoolRouter.cpp +++ b/src/PoolRouter.cpp @@ -25,7 +25,6 @@ */ #include "PoolRouter.hpp" -#include "Sink.hpp" namespace artis::factory { @@ -43,25 +42,13 @@ void PoolRouter::dint(const Time & /* t */) { assert(it != _pending_po.cend()); - if ((*it)->is_finish()) { - _available_machines[(*it)->last_operation()->get_loc_index()] = true; - } else if ((*it)->current_operation().get_type() == CHANGE_POOL_TYPE) { - _available_machines[(*it)->previous_operation()->get_loc_index()] = true; - } else { - //_available_machines[(*it)->current_operation().get_loc_index()] = false; - auto idx = (*it)->current_operation().get_loc_index(); - - // Modifié pour gérer le Sink - if (idx == _available_machines.size()-1) {// Par simplicité, je considère que la dernière machine connectée est le Sink - _available_machines[idx] = true;// Et si idx est le dernier index, alors cette "machine" reste disponible - } else { - _available_machines[idx] = false; - } + if (not (*it)->is_finish() and (*it)->current_operation().get_type() != CHANGE_POOL_TYPE) { + _available_machines[(*it)->current_operation().get_loc_index()]--; } _pending_po.erase(it); if (std::find_if(_pending_po.cbegin(), _pending_po.cend(), [this](const auto &e) { return e->is_finish() or e->current_operation().get_type() == CHANGE_POOL_TYPE or - _available_machines[e->current_operation().get_loc_index()]; + _available_machines[e->current_operation().get_loc_index()] > 0; }) != _pending_po.cend()) { _phase = Phase::SEND; } else { @@ -80,19 +67,24 @@ void PoolRouter::dext(const Time &t, const Time & /* e */, const Bag &bag) { event.data()(data); _pending_po.push_back(std::make_unique(data, event.data().size())); _pending_po.back()->next(); + } else if (event.port_index() == inputs::FINISH) { + unsigned int index; + + event.data()(index); + _available_machines[index]++; } else if (event.port_index() >= inputs::IN_M) { uint8_t *data = nullptr; event.data()(data); _pending_po.push_back(std::make_unique(data, event.data().size())); - _available_machines[event.port_index() - inputs::IN_M] = true; + _available_machines[event.port_index() - inputs::IN_M]++; _pending_po.back()->next(); } } ); if (std::find_if(_pending_po.cbegin(), _pending_po.cend(), [this](const auto &e) { return e->is_finish() or e->current_operation().get_type() == CHANGE_POOL_TYPE or - _available_machines[e->current_operation().get_loc_index()]; + _available_machines[e->current_operation().get_loc_index()] > 0; }) != _pending_po.cend()) { _phase = Phase::SEND; } else { @@ -102,6 +94,9 @@ void PoolRouter::dext(const Time &t, const Time & /* e */, const Bag &bag) { void PoolRouter::start(const Time & /* t */) { _phase = Phase::INIT; + for (const auto &e: _parameters.capacities) { + _available_machines[e.first] = e.second; + } } Time PoolRouter::ta(const Time & /* t */) const { @@ -155,8 +150,8 @@ PoolRouter::ProductionOrders::const_iterator PoolRouter::next_po() const { return it; } else { return std::find_if(_pending_po.cbegin(), _pending_po.cend(), - [this](const auto &e) { return _available_machines[e->current_operation().get_loc_index()]; }); + [this](const auto &e) { return _available_machines.at(e->current_operation().get_loc_index()) > 0; }); } } -} // namespace artis::factory +} // namespace artis::factory \ No newline at end of file diff --git a/src/PoolRouter.hpp b/src/PoolRouter.hpp index 94fe489..a9563b3 100644 --- a/src/PoolRouter.hpp +++ b/src/PoolRouter.hpp @@ -36,14 +36,14 @@ namespace artis::factory { struct PoolRouterParameters { uint type; - std::size_t machine_number; + std::map capacities; }; class PoolRouter : public Dynamics { public: struct inputs { enum values { - IN, IN_M = 1000 + IN, FINISH, IN_M = 1000 }; }; @@ -60,14 +60,13 @@ public: }; PoolRouter(const std::string &name, const Context &context) - : Dynamics(name, context), - _parameters(context.parameters()), _available_machines(context.parameters().machine_number) { - input_port({inputs::IN, "in"}); + : Dynamics(name, context), _parameters(context.parameters()) { + input_ports({{inputs::IN, "in"}, + {inputs::FINISH, "finish"}}); output_port({outputs::OUT, "out"}); - for (unsigned int i = 0; i < context.parameters().machine_number; ++i) { - output_port({outputs::OUT_M + i, "out_m" + std::to_string(i)}); - input_port({inputs::IN_M + i, "in_m" + std::to_string(i)}); - _available_machines[i] = true; + for (const auto &e: context.parameters().capacities) { + output_port({outputs::OUT_M + e.first, "out_m_" + std::to_string(e.first)}); + input_port({inputs::IN_M + e.first, "in_m_" + std::to_string(e.first)}); } observables({{vars::WAITING_PO_NUMBER, "waiting_po_number"}}); } @@ -115,7 +114,7 @@ private: // state Phase::values _phase; ProductionOrders _pending_po; - std::vector _available_machines; + std::map _available_machines; }; } // namespace artis::factory diff --git a/src/Processor.cpp b/src/Processor.cpp index 4e4581a..cbd041c 100644 --- a/src/Processor.cpp +++ b/src/Processor.cpp @@ -24,63 +24,97 @@ * along with this program. If not, see . */ +#include "ItemStock.hpp" #include "Processor.hpp" +#include "Stock.hpp" namespace artis::factory { - void Processor::dint(const Time & t) { - switch (_phase) { - case Phase::INIT: { - _phase = Phase::WAIT; - break; - } - case Phase::READY: - _po.reset(nullptr); - _phase = Phase::WAIT; - break; - case Phase::WAIT: - assert(false); - break; - case Phase::LOADING: { - _phase = Phase::PROCESSING; - break; - } - case Phase::PROCESSING: { - _phase = Phase::UNLOADING; - break; - } - case Phase::UNLOADING: { - _phase = Phase::READY; - -#ifdef WITH_TRACE - Trace::trace() - << TraceElement(get_name(), t, - artis::common::FormalismType::PDEVS, - artis::common::FunctionType::DELTA_INT, - artis::common::LevelType::USER) - << "FINISH po = " << _po->to_string(); - Trace::trace().flush(); -#endif - - break; - } - default: - assert(false); +void Processor::dint(const Time &t) { + for (auto it = _jobs.cbegin(); it != _jobs.cend();) { + if (it->second._next_time == t and it->second._phase == Phase::READY) { + it = _jobs.erase(it); + } else { + ++it; } } + for (auto &j: _jobs) { + auto &job = j.second; + if (job._next_time == t) { + switch (job._phase) { + case Phase::DEMAND_SUPPLY: + job._phase = Phase::WAIT_SUPPLY; + job._next_time = artis::common::DoubleTime::infinity; + break; + case Phase::WAIT_SUPPLY: + case Phase::WAIT: + assert(false); + break; + case Phase::LOADING: { + job._phase = Phase::PROCESSING; + job._next_time = t + durations(job._po->get_productID()).processing_time; + break; + } + case Phase::PROCESSING: { + job._phase = Phase::UNLOADING; + job._next_time = t + durations(job._po->get_productID()).unload_time; + break; + } + case Phase::UNLOADING: { + auto it = _parameters.in_out.find(job._po->get_productID()); - void Processor::dext(const Time &t, const Time & /* e */, const Bag &bag) { + if (it != _parameters.in_out.cend() and not it->second._outs.empty()) { + job._phase = Phase::SEND_STOCK_AVAILABLE; + job._next_time = t; + } else { + job._phase = Phase::READY; + job._next_time = t; + } + +#ifdef WITH_TRACE + Trace::trace() + << TraceElement(get_name(), t, + artis::common::FormalismType::PDEVS, + artis::common::FunctionType::DELTA_INT, + artis::common::LevelType::USER) + << "FINISH po = " << job._po->to_string(); + Trace::trace().flush(); +#endif + + break; + } + case Phase::SEND_STOCK_AVAILABLE: { + job._phase = Phase::WAIT_STOCK_AVAILABLE; + job._next_time = artis::common::DoubleTime::infinity; + break; + } + default: + assert(false); + } + } + } +} + +void Processor::dext(const Time &t, const Time & /* e */, const Bag &bag) { std::for_each(bag.begin(), bag.end(), [this, t](const ExternalEvent &event) { - if (event.port_index() == inputs::IN) { + if (event.on_port(inputs::IN)) { uint8_t *data = nullptr; - // TODO - assert(_po == nullptr); + assert(_jobs.size() < _parameters.capacity); event.data()(data); - _po = std::make_unique(data, event.data().size()); - _phase = Phase::LOADING; + + auto po = std::make_unique(data, event.data().size()); + auto po_id = po->getID(); + auto product_id = po->get_productID(); + auto it = _parameters.in_out.find(po->get_productID()); + + if (it == _parameters.in_out.end() or it->second._ins.empty()) { + _jobs[po_id] = Job{Phase::LOADING, std::move(po), 0, t + durations(product_id).load_time}; + } else { + _jobs[po_id] = Job{Phase::DEMAND_SUPPLY, std::move(po), (unsigned int) it->second._ins.size(), t}; + } #ifdef WITH_TRACE Trace::trace() @@ -88,43 +122,125 @@ namespace artis::factory { artis::common::FormalismType::PDEVS, artis::common::FunctionType::DELTA_EXT, artis::common::LevelType::USER) - << "START po = " << _po->to_string(); + << "START po = " << _jobs[po_id]._po->to_string(); Trace::trace().flush(); #endif + } else if (event.on_port(inputs::IN_ITEM)) { + ItemResponse response; + + event.data()(response); + + assert(_jobs.find(response.po_id) != _jobs.end()); + + auto &job = _jobs[response.po_id]; + job._demand_number--; + if (job._demand_number == 0) { + job._phase = Phase::LOADING; + job._next_time = t + durations(job._po->get_productID()).load_time; + } + } else if (event.on_port(inputs::IN_STOCK)) { + ProductResponse response; + + event.data()(response); + + assert(_jobs.find(response.po_id) != _jobs.end()); + + auto &job = _jobs[response.po_id]; + + job._demand_number--; + if (job._demand_number == 0) { + job._phase = Phase::LOADING; + job._next_time = t + durations(job._po->get_productID()).load_time; + } + } else if (event.port_index() >= inputs::IN_STOCK_AVAILABLE) { + StockAvailableResponse response; + + event.data()(response); + + assert(_jobs.find(response.po_id) != _jobs.end()); + + auto &job = _jobs[response.po_id]; + + switch (response.response) { + case StockAvailableResponse::AVAILABLE: { + job._phase = Phase::READY; + job._next_time = t; + break; + } + case StockAvailableResponse::NOT_AVAILABLE: { + job._phase = Phase::WAIT_STOCK_AVAILABLE; + job._next_time = artis::common::DoubleTime::infinity; + break; + } + case StockAvailableResponse::NEW_AVAILABLE: { + job._phase = Phase::SEND_STOCK_AVAILABLE; + job._next_time = t; + break; + } + } } }); } void Processor::start(const Time & /* t */) { - // TODO - _phase = Phase::INIT; } -Time Processor::ta(const Time & /* t */) const { - // TODO - switch (_phase) { - case Phase::INIT: - case Phase::READY: - return 0; - case Phase::WAIT: - return artis::common::DoubleTime::infinity; - case Phase::LOADING: - return _parameters.load_time; - case Phase::PROCESSING: - return _parameters.processing_time; - case Phase::UNLOADING: - return _parameters.unload_time; +Time Processor::ta(const Time &t) const { + if (not _jobs.empty()) { + return std::min_element(_jobs.cbegin(), _jobs.cend(), [](const auto &lhs, const auto &rhs) { + return (lhs.second._next_time != artis::common::DoubleTime::infinity and + lhs.second._next_time < rhs.second._next_time) or + rhs.second._next_time == artis::common::DoubleTime::infinity; + })->second._next_time - t; } return artis::common::DoubleTime::infinity; } -Bag Processor::lambda(const Time & /* t */) const { +Bag Processor::lambda(const Time &t) const { Bag bag; - // TODO - if (_phase == Phase::READY) { - bag.push_back(ExternalEvent(outputs::OUT, common::event::Value(_po->_buffer, _po->_size))); + for (const auto &e: _jobs) { + const auto &job = e.second; + + if (job._next_time == t) { + if (job._phase == Phase::READY) { + bag.push_back(ExternalEvent(outputs::OUT, common::event::Value(job._po->_buffer, job._po->_size))); + + auto it = _parameters.in_out.find(job._po->get_productID()); + + if (it != _parameters.in_out.cend() and not it->second._outs.empty()) { + const auto &p = it->second._outs.front()._destination; + + bag.push_back(ExternalEvent(outputs::OUT_STOCK + int(p._pool_id * 100 + p._stock_id), + StockEntry(e.first, job._po->operation_index(), + job._po->get_productID(), 1))); + } + } else if (job._phase == Phase::DEMAND_SUPPLY) { + auto it = _parameters.in_out.find(job._po->get_productID()); + + for (const auto &item: it->second._ins) { + if (item._source._pool_id == -1) { + bag.push_back(ExternalEvent(outputs::OUT_ITEM_DEMAND + (int) item._source._stock_id, + ItemDemand(item._id, e.first, item._quantity))); + } else { + bag.push_back( + ExternalEvent(outputs::OUT_STOCK_DEMAND + (int) (item._source._pool_id * 100 + item._source._stock_id), + ProductDemand(e.first, job._po->operation_index() - (job._po->get_operation( + job._po->operation_index() - 1)->get_type() == CHANGE_POOL_TYPE ? 2 : 1), + job._po->get_productID(), 1))); + } + } + } else if (job._phase == Phase::SEND_STOCK_AVAILABLE) { + auto it = _parameters.in_out.find(job._po->get_productID()); + + if (it != _parameters.in_out.cend() and not it->second._outs.empty()) { + const auto &p = it->second._outs.front()._destination; + + bag.push_back(ExternalEvent(outputs::OUT_STOCK_AVAILABLE + int(p._pool_id * 100 + p._stock_id), e.first)); + } + } + } } return bag; } @@ -133,4 +249,4 @@ artis::common::event::Value Processor::observe(const Time & /* t */, unsigned in return {}; } -} // namespace artis::factory +} // namespace artis::factory \ No newline at end of file diff --git a/src/Processor.hpp b/src/Processor.hpp index ecfc3fc..4bb86cb 100644 --- a/src/Processor.hpp +++ b/src/Processor.hpp @@ -33,23 +33,51 @@ namespace artis::factory { -struct ProcessorParameters : MachineParameters { +struct Location { + int _pool_id; // -1 if global + unsigned int _stock_id; +}; + +struct In { + int _id; // -1 if current product + Location _source; + unsigned int _quantity; +}; + +struct Out { + int _id; // -1 if current product + Location _destination; + unsigned int _quantity; +}; + +struct InOut { + std::vector _ins; + std::vector _outs; +}; + +struct ProcessorDurations : Durations { uint load_time; uint processing_time; uint unload_time; }; +struct ProcessorParameters : MachineParameters { + ProcessorDurations durations; + std::map in_out; + std::map> product_durations; +}; + class Processor : public Dynamics { public: struct inputs { enum values { - IN + IN, IN_ITEM, IN_STOCK, IN_STOCK_AVAILABLE = 4000 }; }; struct outputs { enum values { - OUT + OUT, OUT_ITEM_DEMAND = 1000, OUT_STOCK = 2000, OUT_STOCK_DEMAND = 3000, OUT_STOCK_AVAILABLE = 4000 }; }; @@ -60,8 +88,66 @@ public: Processor(const std::string &name, const Context &context) : Dynamics(name, context), _parameters(context.parameters()) { - input_port({inputs::IN, "in"}); + input_ports({{inputs::IN, "in"}, + {inputs::IN_ITEM, "in_supply"}, + {inputs::IN_STOCK, "in_stock"}}); output_port({outputs::OUT, "out"}); + + // demand: processor -> item stocks + std::vector item_stocks; + + for (const auto &p: _parameters.in_out) { + for (const auto &q: p.second._ins) { + if (q._source._pool_id == -1 and + std::find(item_stocks.cbegin(), item_stocks.cend(), q._source._stock_id) == item_stocks.cend()) { + item_stocks.push_back(q._source._stock_id); + } + } + } + for (const auto &p: item_stocks) { + output_port({outputs::OUT_ITEM_DEMAND + p, "out_demand_supply_" + std::to_string(p)}); + } + + // processor -> intermediate stocks + std::vector> stocks; + + for (const auto &p: _parameters.in_out) { + for (const auto &q: p.second._outs) { + auto r = std::make_pair((unsigned int) q._destination._pool_id, q._destination._stock_id); + + if (q._destination._pool_id != -1 and std::find(stocks.cbegin(), stocks.cend(), r) == stocks.cend()) { + stocks.push_back(r); + } + } + } + for (const auto &p: stocks) { + output_port( + {outputs::OUT_STOCK + (p.first * 100 + p.second), + "out_stock_" + std::to_string(p.first) + "_" + std::to_string(p.second)}); + output_port( + {outputs::OUT_STOCK_AVAILABLE + (p.first * 100 + p.second), + "out_stock_available_" + std::to_string(p.first) + "_" + std::to_string(p.second)}); + input_port( + {inputs::IN_STOCK_AVAILABLE + (p.first * 100 + p.second), + "in_stock_available_" + std::to_string(p.first) + "_" + std::to_string(p.second)}); + } + + // demand: processor -> intermediate stocks + stocks.clear(); + for (const auto &p: _parameters.in_out) { + for (const auto &q: p.second._ins) { + auto r = std::make_pair((unsigned int) q._source._pool_id, q._source._stock_id); + + if (q._source._pool_id != -1 and std::find(stocks.cbegin(), stocks.cend(), r) == stocks.cend()) { + stocks.push_back(r); + } + } + } + for (const auto &p: stocks) { + output_port( + {outputs::OUT_STOCK_DEMAND + (p.first * 100 + p.second), + "out_stock_demand_" + std::to_string(p.first) + "_" + std::to_string(p.second)}); + } } ~Processor() override = default; @@ -79,21 +165,29 @@ public: artis::common::event::Value observe(const Time &t, unsigned int index) const override; private: + const ProcessorDurations &durations(unsigned int product_id) const { + if (_parameters.product_durations.find(product_id) == _parameters.product_durations.cend()) { + return _parameters.durations; + } else { + return *_parameters.product_durations.at(product_id); + } + } struct Phase { enum values { - INIT, READY, WAIT, LOADING, PROCESSING, - UNLOADING + UNLOADING, + DEMAND_SUPPLY, + WAIT_SUPPLY, + SEND_STOCK_AVAILABLE, + WAIT_STOCK_AVAILABLE }; static std::string to_string(const values &value) { switch (value) { - case INIT: - return "INIT"; case READY: return "READY"; case WAIT: @@ -104,19 +198,33 @@ private: return "PROCESSING"; case UNLOADING: return "UNLOADING"; + case DEMAND_SUPPLY: + return "DEMAND SUPPLY"; + case WAIT_SUPPLY: + return "WAIT SUPPLY"; + case SEND_STOCK_AVAILABLE: + return "SEND STOCK AVAILABLE"; + case WAIT_STOCK_AVAILABLE: + return "WAIT STOCK AVAILABLE"; } return ""; } }; - // parameters + struct Job { + Phase::values _phase; + std::unique_ptr _po; + unsigned int _demand_number; + Time _next_time; + }; + +// parameters ProcessorParameters _parameters; - // state - Phase::values _phase; - std::unique_ptr _po; +// state + std::map _jobs; // key = po_id }; } // namespace artis::factory -#endif +#endif \ No newline at end of file diff --git a/src/ProductionOrder.cpp b/src/ProductionOrder.cpp index 3ae3f02..535714e 100644 --- a/src/ProductionOrder.cpp +++ b/src/ProductionOrder.cpp @@ -84,7 +84,8 @@ ProductionOrder::ProductionOrder(uint8_t *buffer, std::size_t size) _is_finish = _buffer[INDEX_POSITION] + 1 >= _buffer[OPERATION_NUMBER_POSITION]; } -ProductionOrder::ProductionOrder(uint16_t ID, uint16_t product_ID, const Program &program) : _is_finish(false) { +ProductionOrder::ProductionOrder(uint16_t ID, uint16_t parent_ID, uint32_t due_date, uint16_t product_ID, + const Program &program) : _is_finish(false) { Reference reference{0, 0}; std::size_t operation_number = 0; @@ -108,6 +109,16 @@ ProductionOrder::ProductionOrder(uint16_t ID, uint16_t product_ID, const Program _buffer[reference._byte_index++] = (uint8_t) (ID >> 8); _buffer[reference._byte_index++] = (uint8_t) (ID & 0xFF); + // parent ID + _buffer[reference._byte_index++] = (uint8_t) (parent_ID >> 8); + _buffer[reference._byte_index++] = (uint8_t) (parent_ID & 0xFF); + + // due date + _buffer[reference._byte_index++] = (uint8_t) (due_date >> 24); + _buffer[reference._byte_index++] = (uint8_t) ((due_date >> 16) & 0xFF); + _buffer[reference._byte_index++] = (uint8_t) ((due_date >> 8) & 0xFF); + _buffer[reference._byte_index++] = (uint8_t) (due_date & 0xFF); + // product ID _buffer[reference._byte_index++] = (uint8_t) (product_ID >> 8); _buffer[reference._byte_index++] = (uint8_t) (product_ID & 0xFF); @@ -199,8 +210,14 @@ std::unique_ptr ProductionOrder::previous_operation() const { std::string ProductionOrder::to_string() const { std::string str = "[ "; uint16_t ID = (_buffer[ID_POSITION] << 8) + _buffer[ID_POSITION + 1]; + uint16_t parentID = (_buffer[PARENT_ID_POSITION] << 8) + _buffer[PARENT_ID_POSITION + 1]; - str += std::to_string(ID) + " ][ "; + str += std::to_string(ID); + if (ID != parentID) { + str += + "/" + std::to_string(parentID) +" ][ "; + + } + str += " ][ "; for (uint8_t i = 0; i < _buffer[OPERATION_NUMBER_POSITION]; ++i) { if (_buffer[INDEX_POSITION] == i) { str += "<"; diff --git a/src/ProductionOrder.hpp b/src/ProductionOrder.hpp index 1dfef61..dd46409 100644 --- a/src/ProductionOrder.hpp +++ b/src/ProductionOrder.hpp @@ -34,15 +34,17 @@ namespace artis::factory { -// HEADER = [bit number (16 bits)][ID (16 bits)][Product ID (16 bits)][index (8 bits)][operation number (8 bits)] -const std::size_t POOL_SIZE = 7; // bit number -const std::size_t MACHINE_SIZE = 4; // bit number -const std::size_t TYPE_SIZE = 2; // bit number -const std::size_t HEADER_SIZE = 8; // byte number +// HEADER = [bit number (16 bits)][ID (16 bits)][parent ID (16 bits)][Due date (32 bits)][Product ID (16 bits)][index (8 bits)][operation number (8 bits)] +const std::size_t POOL_SIZE = 7; // bit number +const std::size_t MACHINE_SIZE = 4; // bit number +const std::size_t TYPE_SIZE = 2; // bitModifier le générateur de PO afin de générer tous les PO number +const std::size_t HEADER_SIZE = 14; // byte number const std::size_t BIT_NUMBER_POSITION = 0; const std::size_t ID_POSITION = BIT_NUMBER_POSITION + 2; -const std::size_t PRODUCT_ID_POSITION = ID_POSITION + 2; +const std::size_t PARENT_ID_POSITION = ID_POSITION + 2; +const std::size_t DUE_DATE_POSITION = PARENT_ID_POSITION + 2; +const std::size_t PRODUCT_ID_POSITION = DUE_DATE_POSITION + 4; const std::size_t INDEX_POSITION = PRODUCT_ID_POSITION + 2; const std::size_t OPERATION_NUMBER_POSITION = INDEX_POSITION + 1; @@ -139,7 +141,7 @@ struct ProductionOrder { ~ProductionOrder() { delete[] _buffer; } - ProductionOrder(uint16_t ID, uint16_t product_ID, const Program &program); + ProductionOrder(uint16_t ID, uint16_t parent_ID, uint32_t due_date, uint16_t product_ID, const Program &program); const Operation ¤t_operation() const { return *_current_operation; } @@ -147,8 +149,17 @@ struct ProductionOrder { return (_buffer[ID_POSITION] << 8) + _buffer[ID_POSITION + 1]; } + uint32_t get_due_date() const { + return (_buffer[DUE_DATE_POSITION] << 24) + (_buffer[DUE_DATE_POSITION] << 16) + (_buffer[DUE_DATE_POSITION] << 8) + + _buffer[DUE_DATE_POSITION + 1]; + } + std::unique_ptr get_operation(uint8_t index) const; + uint16_t get_parentID() const { + return (_buffer[PARENT_ID_POSITION] << 8) + _buffer[PARENT_ID_POSITION + 1]; + } + uint16_t get_productID() const { return (_buffer[PRODUCT_ID_POSITION] << 8) + _buffer[PRODUCT_ID_POSITION + 1]; } diff --git a/src/ProductionOrderGenerator.cpp b/src/ProductionOrderGenerator.cpp index 3bc3294..18fa932 100644 --- a/src/ProductionOrderGenerator.cpp +++ b/src/ProductionOrderGenerator.cpp @@ -28,20 +28,30 @@ namespace artis::factory { -void ProductionOrderGenerator::dint(const Time & /* &t */) { +void ProductionOrderGenerator::dint(const Time &t) { switch (_phase) { case Phase::INIT: _phase = Phase::SEND; break; case Phase::SEND: { - _current_id++; - _po_counter++; - _productID = _productIDs[_distrib_product(_rng)]; - _po = std::make_unique(_current_id, _productID, _parameters.programs[_productID]); - _send_speed = _distrib_send_speed(_rng); - _phase = Phase::SEND; + if (_parameters.po_number == -1) { + generate_production_orders(t); + _sigma = _distrib_send_speed(_rng); + _phase = Phase::SEND; + } else { + if (_po_index < (unsigned int) _parameters.po_number - 1) { + _po_index++; + if (not _parameters.po_order.empty()) { + _sigma = 0; + } + } else { + _phase = Phase::FINISH; + } + } break; } + case Phase::FINISH: + assert(false); } } @@ -51,14 +61,18 @@ void ProductionOrderGenerator::dext(const Time & /* t */, const Time & /* e */, }); } -void ProductionOrderGenerator::start(const Time & /* t */) { +void ProductionOrderGenerator::start(const Time &t) { _phase = Phase::INIT; - _po = nullptr; _current_id = 0; - _productID = _productIDs[_distrib_product(_rng)]; - _po = std::make_unique(_current_id, _productID, _parameters.programs[_productID]); - _po_counter = 0; - _send_speed = 0; // _distrib_send_speed(_rng); + _po_index = 0; + if (_parameters.po_number == -1) { + generate_production_orders(t); + } else { + for (unsigned int i = 0; i < (unsigned int) _parameters.po_number; ++i) { + generate_production_orders(t); + } + } + _sigma = 0; } Time ProductionOrderGenerator::ta(const Time & /* t */) const { @@ -66,7 +80,9 @@ Time ProductionOrderGenerator::ta(const Time & /* t */) const { case Phase::INIT: return 0; case Phase::SEND: - return _send_speed; + return _sigma; + case Phase::FINISH: + return artis::common::DoubleTime::infinity; } return artis::common::DoubleTime::infinity; } @@ -75,8 +91,12 @@ Bag ProductionOrderGenerator::lambda(const Time & /* t */) const { Bag bag; if (_phase == Phase::SEND) { - if (_po) { - bag.push_back(ExternalEvent(outputs::OUT, common::event::Value(_po->_buffer, _po->_size))); + if (_parameters.po_order.empty()) { + bag.push_back(ExternalEvent(outputs::OUT, common::event::Value(_pos[_po_index]->_buffer, _pos[_po_index]->_size))); + } else { + const auto &po = _pos[_parameters.po_order[_po_index]]; + + bag.push_back(ExternalEvent(outputs::OUT, common::event::Value(po->_buffer, po->_size))); } } return bag; @@ -85,9 +105,16 @@ Bag ProductionOrderGenerator::lambda(const Time & /* t */) const { artis::common::event::Value ProductionOrderGenerator::observe(const Time & /* t */, unsigned int index) const { switch (index) { case vars::TOTAL_PO_NUMBER: - return (unsigned int) _po_counter; + return (unsigned int) _po_index; } return {}; } +void ProductionOrderGenerator::generate_production_orders(const Time &t) { + _productID = _productIDs[_distrib_product(_rng)]; + _pos.push_back(std::make_unique(_current_id, _current_id, t + 8 * 60 * 60, _productID, + _parameters.programs[_productID])); + _current_id++; +} + } // namespace artis::factory diff --git a/src/ProductionOrderGenerator.hpp b/src/ProductionOrderGenerator.hpp index eb17013..a0ff848 100644 --- a/src/ProductionOrderGenerator.hpp +++ b/src/ProductionOrderGenerator.hpp @@ -42,6 +42,8 @@ struct ProductionOrderGeneratorParameters { uint random_seed; uint min_send_speed_rate; uint max_send_speed_rate; + int po_number{-1}; + std::vector po_order{}; }; class ProductionOrderGenerator @@ -89,9 +91,11 @@ public: artis::common::event::Value observe(const Time &t, unsigned int index) const override; private: + void generate_production_orders(const Time & t); + struct Phase { enum values { - INIT, SEND + INIT, SEND, FINISH }; static std::string to_string(const values &value) { @@ -100,6 +104,8 @@ private: return "INIT"; case SEND: return "SEND"; + case FINISH: + return "FINISH"; } return ""; } @@ -119,11 +125,11 @@ private: std::vector _productIDs; unsigned int _productID; - std::unique_ptr _po; + std::vector> _pos; unsigned int _current_id; - std::size_t _po_counter; - unsigned int _send_speed; + std::size_t _po_index; + unsigned int _sigma; }; } // namespace artis::factory diff --git a/src/Sink.cpp b/src/Sink.cpp index aa44550..5c5eca8 100644 --- a/src/Sink.cpp +++ b/src/Sink.cpp @@ -28,52 +28,51 @@ namespace artis::factory { -void Sink::dint(const Time &/*t*/) { - // TODO - // Inutile si l'on consiède que cet élément ce fait que recevoir des POs. Il ne fait rien par lui-même, et ne varie que en fonction de son environnement. +void Sink::dint(const Time & /* t */) { + switch (_phase) { + case Phase::WAIT: { + assert(false); + } + case Phase::SEND: { + _phase = Phase::WAIT; + } + } } void Sink::dext(const Time &t, const Time & /* e */, const Bag &bag) { - std::for_each(bag.begin(), bag.end(), [this, t](const ExternalEvent &event) { - + std::for_each(bag.begin(), bag.end(), [t, this](const ExternalEvent &event) { if (event.port_index() == inputs::IN) { uint8_t *data = nullptr; event.data()(data); - std::unique_ptr po = std::make_unique(data, event.data().size()); - #ifdef WITH_TRACE - Trace::trace() - << TraceElement(get_name(), t, - artis::common::FormalismType::PDEVS, - artis::common::FunctionType::DELTA_EXT, - artis::common::LevelType::USER) - << "Sink received po = " << po->to_string(); - Trace::trace().flush(); - #endif - _pos.push_back(std::move(po));//Je ne peux pas dupliquer ce pointer unique dans la liste, je dois explicitement le déplacer. - _phase = Phase::LOADED; + _finished_po.push_back(std::make_unique(data, event.data().size())); + _phase = Phase::SEND; +#ifdef WITH_TRACE + Trace::trace() + << TraceElement(get_name(), t, + artis::common::FormalismType::PDEVS, + artis::common::FunctionType::DELTA_EXT, + artis::common::LevelType::USER) + << "FINISH po = " << _finished_po.back()->to_string(); + Trace::trace().flush(); +#endif } }); } void Sink::start(const Time & /* t */) { - _phase = Phase::INIT; - // Préparation du modèle, j'imagine que ce n'est pas utile donc passage immédiat en READY - _phase = Phase::READY; - // Le modèle est prêt, on attend des PO _phase = Phase::WAIT; } Time Sink::ta(const Time & /* t */) const { switch (_phase) { - case Phase::INIT:// Phase transitoire, je n'y reste pas, le modèle ne fait qu'y passer - case Phase::READY:// Idem - //case Phase::UNLOADING:// Déchargement des PO dans la sortie, ce n'est que transitoire - return 0; - case Phase::WAIT:// J'attends un premier PO, potentiellement infini - case Phase::LOADED:// J'ai un PO en stock, j'attends d'en faire quelque chose,ce qui est potentiellement infini + case Phase::WAIT: { return artis::common::DoubleTime::infinity; + } + case Phase::SEND: { + return 0; + } } return artis::common::DoubleTime::infinity; } @@ -81,13 +80,18 @@ Time Sink::ta(const Time & /* t */) const { Bag Sink::lambda(const Time & /* t */) const { Bag bag; - // TODO - // Pour le moment, Sink ne laisse pas sortir les POs arrivés dedans. Inutile de définir ceci. + if (_phase == Phase::SEND) { + bag.push_back(ExternalEvent(outputs::OUT, (unsigned int) _parameters.machine_id)); + } return bag; } -artis::common::event::Value Sink::observe(const Time & /* t */, unsigned int /* index */) const { +artis::common::event::Value Sink::observe(const Time & /* t */, unsigned int index) const { + switch (index) { + case vars::FINISHED_PO_NUMBER: + return (unsigned int) _finished_po.size(); + } return {}; } -} // namespace artis::factory +} // namespace artis::factory \ No newline at end of file diff --git a/src/Sink.hpp b/src/Sink.hpp index cd11f6c..91453e8 100644 --- a/src/Sink.hpp +++ b/src/Sink.hpp @@ -34,7 +34,6 @@ namespace artis::factory { struct SinkParameters : MachineParameters { - }; class Sink : public Dynamics { @@ -53,13 +52,15 @@ public: struct vars { enum values { + FINISHED_PO_NUMBER }; }; Sink(const std::string &name, const Context &context) - : Dynamics(name, context) { + : Dynamics(name, context), _parameters(context.parameters()) { input_port({inputs::IN, "in"}); output_port({outputs::OUT, "out"}); + observables({{vars::FINISHED_PO_NUMBER, "finished_po_number"}}); } ~Sink() override = default; @@ -77,38 +78,30 @@ public: artis::common::event::Value observe(const Time &t, unsigned int index) const override; private: - struct Phase { - enum values { - INIT, - READY, - WAIT, - LOADED, - //UNLOADING - }; - - static std::string to_string(const values &value) { - switch (value) { - case INIT: - return "INIT"; - case READY: - return "READY"; - case WAIT: - return "WAIT"; - case LOADED: - return "LOADED"; - /*case UNLOADING: - return "UNLOADING";*/ - } - return ""; - } + struct Phase { + enum values { + WAIT, SEND }; + static std::string to_string(const values &value) { + switch (value) { + case WAIT: + return "WAIT"; + case SEND: + return "SEND"; + } + return ""; + } + }; + typedef std::deque> ProductionOrders; - Phase::values _phase; - std::vector> _pos; -}; // class Sink + SinkParameters _parameters; + + Phase::values _phase; + ProductionOrders _finished_po; +}; } // namespace artis::factory -#endif // ARTIS_FACTORY_SINK_HPP +#endif \ No newline at end of file diff --git a/src/Stock.cpp b/src/Stock.cpp index 8469936..3d62cae 100644 --- a/src/Stock.cpp +++ b/src/Stock.cpp @@ -29,32 +29,119 @@ namespace artis::factory { void Stock::dint(const Time & /* t */) { - // TODO + switch (_phase) { + case Phase::SEND_AVAILABLE: { + _available_demands.clear(); + _phase = Phase::WAIT; + break; + } + case Phase::SEND: { + _demands.clear(); + _phase = Phase::WAIT; + break; + } + case Phase::WAIT: { + assert(false); + } + } } -void Stock::dext(const Time & /* t */, const Time & /* e */, const Bag &bag) { - std::for_each(bag.begin(), bag.end(), [](const ExternalEvent & /* event */) { - // TODO +void Stock::dext(const Time &t, const Time & /* e */, const Bag &bag) { + std::for_each(bag.begin(), bag.end(), [t, this](const ExternalEvent &event) { + if (event.on_port(inputs::IN)) { + StockEntry entry; + + event.data()(entry); + if (_parameters._capacity == -1 or (int) _entries.size() < _parameters._capacity) { + _entries.push_back({t, entry.po_id, entry.po_index, entry.product_id, entry.quantity}); + } else { + assert(false); + } + } else if (event.port_index() >= inputs::IN_DEMAND and event.port_index() < inputs::IN_STOCK_AVAILABLE) { + ProductDemand demand; + + event.data()(demand); + auto it = std::find_if(_entries.begin(), _entries.end(), [demand](const auto &e) { + return e._po_id == demand.po_id and e._po_index == demand.po_index and e._product_id == demand.product_id + and e._quantity >= demand.quantity; + }); + + if (it != _entries.end()) { + if (it->_quantity == demand.quantity) { + _entries.erase(it); + } else { + it->_quantity -= demand.quantity; + } + _demands.push_back(std::make_pair(event.port_index() - inputs::IN_DEMAND, demand)); + _phase = Phase::SEND; + } + } else if (event.port_index() >= inputs::IN_STOCK_AVAILABLE) { + unsigned int machine_id = event.port_index() - inputs::IN_STOCK_AVAILABLE; + unsigned int po_id; + + event.data()(po_id); + _available_demands.push_back(AvailableDemand{machine_id, po_id, false}); + if (_parameters._capacity == -1 or (int) _entries.size() < _parameters._capacity) { + auto it = std::find_if(_waiting_machine_ids.begin(), _waiting_machine_ids.end(), + [machine_id, po_id](const auto &e) { + return machine_id == e.first and po_id == e.second; + }); + + if (it != _waiting_machine_ids.end()) { + _waiting_machine_ids.erase(it); + } + _available_demands.back()._available = true; + _phase = Phase::SEND_AVAILABLE; + } else { + _waiting_machine_ids.push_back(std::make_pair(machine_id, po_id)); + _phase = Phase::SEND_AVAILABLE; + } + } }); } void Stock::start(const Time & /* t */) { - // TODO + _phase = Phase::WAIT; } Time Stock::ta(const Time & /* t */) const { - // TODO + switch (_phase) { + case Phase::WAIT: + return artis::common::DoubleTime::infinity; + case Phase::SEND: + case Phase::SEND_AVAILABLE: + return 0; + } return artis::common::DoubleTime::infinity; } Bag Stock::lambda(const Time & /* t */) const { Bag bag; - // TODO + if (_phase == Phase::SEND) { + for (const auto &e: _waiting_machine_ids) { + bag.push_back(ExternalEvent(outputs::OUT_STOCK_AVAILABLE + e.first, + StockAvailableResponse(e.second, StockAvailableResponse::NEW_AVAILABLE))); + } + for (const auto &e: _demands) { + bag.push_back(ExternalEvent(outputs::OUT + e.first, ProductResponse(e.second.product_id, e.second.po_id))); + } + } else if (_phase == Phase::SEND_AVAILABLE) { + for (const auto &demand: _available_demands) { + bag.push_back(ExternalEvent(outputs::OUT_STOCK_AVAILABLE + demand._machine_id, + StockAvailableResponse(demand._po_id, + demand._available ? StockAvailableResponse::AVAILABLE + : StockAvailableResponse::NOT_AVAILABLE))); + } + } return bag; } -artis::common::event::Value Stock::observe(const Time & /* t */, unsigned int /* index */) const { +artis::common::event::Value Stock::observe(const Time & /* t */, unsigned int index) const { + switch (index) { + case vars::ENTRY_NUMBER: + return (unsigned int) _entries.size(); + } return {}; } diff --git a/src/Stock.hpp b/src/Stock.hpp index 779dcde..274b85c 100644 --- a/src/Stock.hpp +++ b/src/Stock.hpp @@ -1,5 +1,5 @@ /** - * @file Stock.hpp + * @file IntermediateStock.hpp * @author The ARTIS Development Team * See the AUTHORS or Authors.txt file */ @@ -32,32 +32,120 @@ namespace artis::factory { +struct StockEntry { + unsigned int po_id; + unsigned int po_index; + unsigned int product_id; + unsigned int quantity; + + StockEntry(unsigned int po_id = 0, unsigned int po_index = 0, unsigned int product_id = 0, unsigned int quantity = 0) + : po_id(po_id), po_index(po_index), product_id(product_id), quantity(quantity) {} + + bool operator==(const StockEntry &other) const { + return po_id == other.po_id and po_index == other.po_index and product_id == other.product_id and + quantity == other.quantity; + } + + std::string to_string() const { + return "StockEntry < " + std::to_string(po_id) + " | " + std::to_string(po_index) + " | " + + std::to_string(product_id) + " | " + std::to_string(quantity) + " >"; + } +}; + +struct ProductDemand { + unsigned int po_id; + unsigned int po_index; + unsigned int product_id; + unsigned int quantity; + + ProductDemand(unsigned int po_id = 0, unsigned int po_index = 0, unsigned int product_id = 0, + unsigned int quantity = 0) + : po_id(po_id), po_index(po_index), product_id(product_id), quantity(quantity) {} + + bool operator==(const ProductDemand &other) const { + return po_id == other.po_id and po_index == other.po_index and product_id == other.product_id and + quantity == other.quantity; + } + + std::string to_string() const { + return "ProductDemand < " + std::to_string(po_id) + " | " + std::to_string(po_index) + " | " + + std::to_string(product_id) + " | " + std::to_string(quantity) + " >"; + } +}; + +struct StockAvailableResponse { + int po_id; + + enum values { + AVAILABLE, NOT_AVAILABLE, NEW_AVAILABLE + } response; + + StockAvailableResponse(unsigned int po_id = 0, values response = NOT_AVAILABLE) + : po_id(po_id), response(response) {} + + bool operator==(const StockAvailableResponse &other) const { + return po_id == other.po_id and response == other.response; + } + + std::string to_string() const { + return "StockAvailableResponse < " + std::to_string(po_id) + " | " + + (response == AVAILABLE ? "AVAILABLE" : (response == NOT_AVAILABLE ? "NOT AVAILABLE" : "NEW AVAILABLE")) + " >"; + } +}; + +struct ProductResponse { + int product_id; + int po_id; + + ProductResponse(int product_id = -1, int po_id = -1) : product_id(product_id), po_id(po_id) {} + + bool operator==(const ProductResponse &other) const { + return product_id == other.product_id and po_id == other.po_id; + } + + std::string to_string() const { + return "Response < " + std::to_string(product_id) + " | " + std::to_string(po_id) + " >"; + } +}; + struct StockParameters { + std::vector _in_machines; + std::vector _out_machines; + int _capacity; // if -1 then infinity }; class Stock : public Dynamics { public: struct inputs { enum values { - IN + IN, IN_DEMAND = 1000, IN_STOCK_AVAILABLE = 2000 }; }; struct outputs { enum values { - OUT + OUT = 1000, OUT_STOCK_AVAILABLE = 2000 }; }; struct vars { enum values { + ENTRY_NUMBER }; }; Stock(const std::string &name, const Context &context) - : Dynamics(name, context) { + : Dynamics(name, context), _parameters(context.parameters()) { input_port({inputs::IN, "in"}); - output_port({outputs::OUT, "out"}); + for (const auto &m: _parameters._in_machines) { + output_port({outputs::OUT + m, "out_" + std::to_string(m)}); + input_port({inputs::IN_DEMAND + m, "in_demand_" + std::to_string(m)}); + } + for (const auto &m: _parameters._out_machines) { + output_port({outputs::OUT_STOCK_AVAILABLE + m, "out_stock_available_" + std::to_string(m)}); + input_port({inputs::IN_STOCK_AVAILABLE + m, "in_stock_available_" + std::to_string(m)}); + } + observables({{vars::ENTRY_NUMBER, "entry number"}}); } ~Stock() override = default; @@ -75,7 +163,47 @@ public: artis::common::event::Value observe(const Time &t, unsigned int index) const override; private: - // TODO (state) + struct Phase { + enum values { + WAIT, + SEND, + SEND_AVAILABLE + }; + + static std::string to_string(const values &value) { + switch (value) { + case WAIT: + return "WAIT"; + case SEND: + return "SEND"; + case SEND_AVAILABLE: + return "SEND AVAILABLE"; + } + return ""; + } + }; + + struct Entry { + Time _t; + unsigned int _po_id; + unsigned int _po_index; + unsigned int _product_id; + unsigned int _quantity; + }; + + struct AvailableDemand { + unsigned int _machine_id; + unsigned int _po_id; + bool _available; + }; + + StockParameters _parameters; + + Phase::values _phase; + std::vector _entries; + std::vector> _demands; + std::vector _available_demands; + std::deque> _waiting_machine_ids; // pair(machine_id,po_id) }; } // namespace artis::factory diff --git a/test/test_json.cpp b/test/test_json.cpp index ffcc17a..745e5bf 100644 --- a/test/test_json.cpp +++ b/test/test_json.cpp @@ -34,7 +34,8 @@ public: BOOST_AUTO_TEST_CASE(TestCase_JSONPool) { - std::ifstream input("../data/factory.json"); // Fix : j'ai retiré un ../ pour que le test passe sous Codeblocks + //std::ifstream input("../../data/factory_with_sinks.json"); + std::ifstream input("../../data/factory_with_sinks.json"); if (input) { std::string str((std::istreambuf_iterator(input)), std::istreambuf_iterator()); @@ -66,12 +67,11 @@ BOOST_AUTO_TEST_CASE(TestCase_JSONPool) output(context.begin(), context.end(), {context.begin(), 1}); - std::cout << artis::factory::Trace::trace().elements().filter_level_type(artis::common::LevelType::USER).to_string() + std::cout << artis::factory::Trace::trace().elements().filter_level_type(artis::common::LevelType::USER).filter_model_name("M_3_0").to_string() << std::endl; BOOST_CHECK(true); } else { - std::cout << "ERREUR : FICHIER JSON NON TROUVé" << std::endl; BOOST_CHECK(false); } -} +} \ No newline at end of file diff --git a/test/test_optim.cpp b/test/test_optim.cpp new file mode 100644 index 0000000..a727f36 --- /dev/null +++ b/test/test_optim.cpp @@ -0,0 +1,229 @@ +#include +#include +#include +#include + +#include "FactoryGraphManager.hpp" +#include "Machine.hpp" + +using namespace artis::factory; + +constexpr unsigned int product_number = 10; +constexpr unsigned int machine_number = 5; +constexpr unsigned int po_number = 100; +constexpr unsigned int iteration_number = 1000; + +unsigned int processing_times[product_number][machine_number] = { + {2, 7, 12, 17, 22}, + {3, 8, 13, 18, 23}, + {1, 9, 12, 19, 6}, + {5, 10, 15, 20, 25}, + {6, 11, 16, 21, 26}, + {27, 28, 29, 30, 2}, + {3, 4, 15, 10, 7}, + {8, 9, 10, 11, 12}, + {13, 3, 15, 7, 17}, + {18, 19, 20, 21, 22} +}; + +struct Solution { + std::vector po_order; + std::vector capacities; +}; + +class MyView : public View { +public: + MyView() { + for (int i = 0; i < (int) machine_number - 1; ++i) { + selector("Stock_" + std::to_string(i) + ":entry_number", + {FactoryGraphManager::STOCK + i, Stock::vars::ENTRY_NUMBER}); + } + selector("Sink:finished_po_number", + {FactoryGraphManager::MACHINE + machine_number, Sink::vars::FINISHED_PO_NUMBER}); + } +}; + +std::vector make_po_order() { + std::random_device rd; + std::mt19937 gen(rd()); + std::vector order; + std::vector allNumbers; + + for (unsigned int i = 0; i < po_number; ++i) { + allNumbers.push_back(i); + } + while (order.size() < po_number) { + std::uniform_int_distribution dist(0, allNumbers.size() - 1); + unsigned int index = dist(gen); + + order.push_back(allNumbers[index]); + allNumbers.erase(allNumbers.begin() + index); + } + return order; +} + +std::vector make_capacities() { + std::vector capacities(machine_number - 1, -1); + + return capacities; +} + +class Evaluator { +public: + Evaluator() { + make_flow_shop(); + } + + std::vector evaluate(const Solution &solution) { + apply(solution); + + FactoryGraphManagerParameters graph_parameters{factory}; + artis::common::context::Context context(0, 7200); + artis::common::RootCoordinator< + artis::common::DoubleTime, artis::pdevs::Coordinator< + artis::common::DoubleTime, FactoryGraphManager, artis::common::NoParameters, FactoryGraphManagerParameters> + > rc(context, "root", artis::common::NoParameters(), graph_parameters); + + rc.attachView("V", new MyView); + rc.switch_to_timed_observer(1); + + rc.run(context); + + auto sink_values = rc.observer().view("V").get("Sink:finished_po_number"); + double t = -1; + + for (const auto &v: sink_values) { + unsigned int n; + + v.second.operator()(n); + if (n == po_number) { + t = v.first; + break; + } + } + + double sum = 0; + for (unsigned int i = 0; i < machine_number - 1; ++i) { + auto stock_values = rc.observer().view("V").get("Stock_" + std::to_string(i) + ":entry_number"); + + for (const auto &v: stock_values) { + unsigned int n; + + v.second.operator()(n); + if (v.first > t) { + break; + } + sum += n; + } + } + return {t, sum / (machine_number - 1) / t}; + } + +private: + void apply(const Solution &solution) { + factory._generator._po_order = solution.po_order; + for (unsigned int i = 0; i < machine_number - 1; ++i) { + std::get<3>(factory._pools[0])[i]._capacity = (int)solution.capacities[i]; + } + } + + void fix_durations() { + for (const auto &product: factory._products) { + unsigned int product_id = product.first; + unsigned int pool_index = 0; + + for (const auto &pool_machine: product.second._pool_jobs) { + unsigned int pool_id = pool_machine._pool_id; + const auto &pool = *std::find_if(factory._pools.cbegin(), factory._pools.cend(), + [pool_id](const auto &e) { return std::get<0>(e) == pool_id; }); + const auto &machines = std::get<2>(pool); + unsigned int sequence_index = 0; + + for (const auto &machine: pool_machine._machine_jobs) { + unsigned int machine_id = machine._machine_id; + const auto &m = *std::find_if(machines.cbegin(), machines.cend(), + [machine_id](const auto &e) { return e->machine_id == machine_id; }); + + if (m->machine_type == PROCESSOR) { + auto &M = (ProcessorParameters &) (*m); + + M.product_durations[product_id] = std::static_pointer_cast(machine._durations); + } + ++sequence_index; + } + ++pool_index; + } + } + } + + void make_flow_shop() { + // generator + factory._generator._min_send_speed_rate = 0; + factory._generator._max_send_speed_rate = 0; + factory._generator._random_seed = 62642; + factory._generator._po_number = po_number; + // products + for (unsigned int i = 0; i < product_number; ++i) { + Product product{i, {}, {PoolJob{0, {}}}, true}; + + for (unsigned int j = 0; j < machine_number; ++j) { + std::vector ins; + std::vector outs; + + if (j > 0) { + ins.push_back({-1, {0, j - 1}, 1}); + } + if (j < machine_number - 1) { + outs.push_back({-1, {0, j}, 1}); + } + product._pool_jobs[0]._machine_jobs.push_back( + MachineJob{j, ins, outs, + std::make_shared(ProcessorDurations{{}, 1, processing_times[i][j], 1})}); + } + + product._pool_jobs[0]._machine_jobs.push_back(MachineJob{machine_number, {}, {}, {}}); + factory._products[i] = product; + } + // pools + factory._pools.push_back({0, 0, {}, {}}); + for (unsigned int i = 0; i < machine_number; ++i) { + std::map in_out; + + for (const auto &product: factory._products) { + for (const auto &pool_job: product.second._pool_jobs) { + if (pool_job._pool_id == 0) { + for (const auto &machine_job: pool_job._machine_jobs) { + if (machine_job._machine_id == i) { + in_out[product.first] = InOut{machine_job._ins, machine_job._outs}; + } + } + } + } + } + std::get<2>(factory._pools[0]).push_back(std::make_shared(ProcessorParameters{{i, 0, 0, 1}, + {{}, 1, 3, 1}, + in_out, + {}})); + } + std::get<2>(factory._pools[0]).push_back( + std::make_shared(SinkParameters{{machine_number, 1, 0, 1}})); + // intermediate stocks + for (unsigned int i = 0; i < machine_number - 1; ++i) { + std::get<3>(factory._pools[0]).push_back({i, -1}); + } + + fix_durations(); + } + + Factory factory; +}; + +int main() { + Evaluator evaluator; + + for (unsigned int i = 0; i < iteration_number; ++i) { + Solution solution{make_po_order(), make_capacities()}; + + std::cout << evaluator.evaluate(solution)[0] << " " << evaluator.evaluate(solution)[1] << std::endl; + } +} \ No newline at end of file diff --git a/test/test_simple.cpp b/test/test_simple.cpp index 97dfb56..a727f36 100644 --- a/test/test_simple.cpp +++ b/test/test_simple.cpp @@ -1,140 +1,229 @@ #include #include #include - -#include #include -#define BOOST_TEST_MODULE Factory_Simple_Tests +#include "FactoryGraphManager.hpp" +#include "Machine.hpp" -#include -#include +using namespace artis::factory; -#include "Processor.hpp" -#include "PoolRouter.hpp" -#include "ProductionOrderGenerator.hpp" -#include "Router.hpp" -#include "Sink.hpp" +constexpr unsigned int product_number = 10; +constexpr unsigned int machine_number = 5; +constexpr unsigned int po_number = 100; +constexpr unsigned int iteration_number = 1000; -using namespace std::chrono; +unsigned int processing_times[product_number][machine_number] = { + {2, 7, 12, 17, 22}, + {3, 8, 13, 18, 23}, + {1, 9, 12, 19, 6}, + {5, 10, 15, 20, 25}, + {6, 11, 16, 21, 26}, + {27, 28, 29, 30, 2}, + {3, 4, 15, 10, 7}, + {8, 9, 10, 11, 12}, + {13, 3, 15, 7, 17}, + {18, 19, 20, 21, 22} +}; -class OnlyOnePoolGraphManager - : public artis::pdevs::GraphManager { +struct Solution { + std::vector po_order; + std::vector capacities; +}; + +class MyView : public View { public: - enum sub_models { - PROCESSOR_1, PROCESSOR_2, PROCESSOR_3, POOL_ROUTER, ROUTER, GENERATOR, SINK - }; + MyView() { + for (int i = 0; i < (int) machine_number - 1; ++i) { + selector("Stock_" + std::to_string(i) + ":entry_number", + {FactoryGraphManager::STOCK + i, Stock::vars::ENTRY_NUMBER}); + } + selector("Sink:finished_po_number", + {FactoryGraphManager::MACHINE + machine_number, Sink::vars::FINISHED_PO_NUMBER}); + } +}; - OnlyOnePoolGraphManager(artis::common::Coordinator *coordinator, - const artis::common::NoParameters ¶meters, - const artis::common::NoParameters &graph_parameters - ) : artis::pdevs::GraphManager( - coordinator, parameters, graph_parameters), - _generator("G", - { - { - {0, {artis::factory::PoolMachineSequence(0, {0, 1, 2, 3})}}, - {1, {artis::factory::PoolMachineSequence(0, {1, 2, 0, 3})}}, - {2, {artis::factory::PoolMachineSequence(0, {2, 0, 1, 3})}} - }, - 62642, 15, 30 - }), - _router("R", {1}), - _pool_router("P_R", {0, 4}), - _processor_1("M1", {{0, 0, 0}, 1, 10, 1}), - _processor_2("M2", {{1, 0, 0}, 1, 8, 1}), - _processor_3("M3", {{2, 0, 0}, 1, 15, 1}), - _sink("S", {3, 0}) { - this->add_child(SINK, &_sink); - this->add_child(PROCESSOR_1, &_processor_1); - this->add_child(PROCESSOR_2, &_processor_2); - this->add_child(PROCESSOR_3, &_processor_3); - this->add_child(ROUTER, &_router); - this->add_child(POOL_ROUTER, &_pool_router); - this->add_child(GENERATOR, &_generator); +std::vector make_po_order() { + std::random_device rd; + std::mt19937 gen(rd()); + std::vector order; + std::vector allNumbers; - out({&_generator, artis::factory::ProductionOrderGenerator::outputs::OUT}) - >> in({&_router, artis::factory::Router::inputs::IN}); + for (unsigned int i = 0; i < po_number; ++i) { + allNumbers.push_back(i); + } + while (order.size() < po_number) { + std::uniform_int_distribution dist(0, allNumbers.size() - 1); + unsigned int index = dist(gen); - out({&_router, artis::factory::Router::outputs::OUT_P + 0}) - >> in({&_pool_router, artis::factory::PoolRouter::inputs::IN}); + order.push_back(allNumbers[index]); + allNumbers.erase(allNumbers.begin() + index); + } + return order; +} - out({&_pool_router, artis::factory::PoolRouter::outputs::OUT}) - >> in({&_router, artis::factory::Router::inputs::IN_P + 0}); +std::vector make_capacities() { + std::vector capacities(machine_number - 1, -1); - out({&_pool_router, artis::factory::PoolRouter::outputs::OUT_M + 0}) - >> in({&_processor_1, artis::factory::Processor::inputs::IN}); + return capacities; +} - out({&_pool_router, artis::factory::PoolRouter::outputs::OUT_M + 1}) - >> in({&_processor_2, artis::factory::Processor::inputs::IN}); - - out({&_pool_router, artis::factory::PoolRouter::outputs::OUT_M + 2}) - >> in({&_processor_3, artis::factory::Processor::inputs::IN}); - - out({&_processor_1, artis::factory::Processor::outputs::OUT}) - >> in({&_pool_router, artis::factory::PoolRouter::inputs::IN_M + 0}); - - out({&_processor_2, artis::factory::Processor::outputs::OUT}) - >> in({&_pool_router, artis::factory::PoolRouter::inputs::IN_M + 1}); - - out({&_processor_3, artis::factory::Processor::outputs::OUT}) - >> in({&_pool_router, artis::factory::PoolRouter::inputs::IN_M + 2}); - - out({&_router, artis::factory::Router::outputs::OUT/*_M+3*/}) // Sink - >> in({&_sink, artis::factory::Sink::inputs::IN}); +class Evaluator { +public: + Evaluator() { + make_flow_shop(); } - ~OnlyOnePoolGraphManager() override = default; + std::vector evaluate(const Solution &solution) { + apply(solution); + + FactoryGraphManagerParameters graph_parameters{factory}; + artis::common::context::Context context(0, 7200); + artis::common::RootCoordinator< + artis::common::DoubleTime, artis::pdevs::Coordinator< + artis::common::DoubleTime, FactoryGraphManager, artis::common::NoParameters, FactoryGraphManagerParameters> + > rc(context, "root", artis::common::NoParameters(), graph_parameters); + + rc.attachView("V", new MyView); + rc.switch_to_timed_observer(1); + + rc.run(context); + + auto sink_values = rc.observer().view("V").get("Sink:finished_po_number"); + double t = -1; + + for (const auto &v: sink_values) { + unsigned int n; + + v.second.operator()(n); + if (n == po_number) { + t = v.first; + break; + } + } + + double sum = 0; + for (unsigned int i = 0; i < machine_number - 1; ++i) { + auto stock_values = rc.observer().view("V").get("Stock_" + std::to_string(i) + ":entry_number"); + + for (const auto &v: stock_values) { + unsigned int n; + + v.second.operator()(n); + if (v.first > t) { + break; + } + sum += n; + } + } + return {t, sum / (machine_number - 1) / t}; + } private: - artis::pdevs::Simulator _generator; - artis::pdevs::Simulator _router; - artis::pdevs::Simulator _pool_router; - artis::pdevs::Simulator _processor_1; - artis::pdevs::Simulator _processor_2; - artis::pdevs::Simulator _processor_3; - artis::pdevs::Simulator _sink; -}; - -class PoolRouterView : public artis::factory::View { -public: - PoolRouterView() { - selector("PoolRouter:waiting_po_number", - {OnlyOnePoolGraphManager::POOL_ROUTER, artis::factory::PoolRouter::vars::WAITING_PO_NUMBER}); - selector("Generator:total_po_number", - {OnlyOnePoolGraphManager::GENERATOR, artis::factory::ProductionOrderGenerator::vars::TOTAL_PO_NUMBER}); + void apply(const Solution &solution) { + factory._generator._po_order = solution.po_order; + for (unsigned int i = 0; i < machine_number - 1; ++i) { + std::get<3>(factory._pools[0])[i]._capacity = (int)solution.capacities[i]; + } } + + void fix_durations() { + for (const auto &product: factory._products) { + unsigned int product_id = product.first; + unsigned int pool_index = 0; + + for (const auto &pool_machine: product.second._pool_jobs) { + unsigned int pool_id = pool_machine._pool_id; + const auto &pool = *std::find_if(factory._pools.cbegin(), factory._pools.cend(), + [pool_id](const auto &e) { return std::get<0>(e) == pool_id; }); + const auto &machines = std::get<2>(pool); + unsigned int sequence_index = 0; + + for (const auto &machine: pool_machine._machine_jobs) { + unsigned int machine_id = machine._machine_id; + const auto &m = *std::find_if(machines.cbegin(), machines.cend(), + [machine_id](const auto &e) { return e->machine_id == machine_id; }); + + if (m->machine_type == PROCESSOR) { + auto &M = (ProcessorParameters &) (*m); + + M.product_durations[product_id] = std::static_pointer_cast(machine._durations); + } + ++sequence_index; + } + ++pool_index; + } + } + } + + void make_flow_shop() { + // generator + factory._generator._min_send_speed_rate = 0; + factory._generator._max_send_speed_rate = 0; + factory._generator._random_seed = 62642; + factory._generator._po_number = po_number; + // products + for (unsigned int i = 0; i < product_number; ++i) { + Product product{i, {}, {PoolJob{0, {}}}, true}; + + for (unsigned int j = 0; j < machine_number; ++j) { + std::vector ins; + std::vector outs; + + if (j > 0) { + ins.push_back({-1, {0, j - 1}, 1}); + } + if (j < machine_number - 1) { + outs.push_back({-1, {0, j}, 1}); + } + product._pool_jobs[0]._machine_jobs.push_back( + MachineJob{j, ins, outs, + std::make_shared(ProcessorDurations{{}, 1, processing_times[i][j], 1})}); + } + + product._pool_jobs[0]._machine_jobs.push_back(MachineJob{machine_number, {}, {}, {}}); + factory._products[i] = product; + } + // pools + factory._pools.push_back({0, 0, {}, {}}); + for (unsigned int i = 0; i < machine_number; ++i) { + std::map in_out; + + for (const auto &product: factory._products) { + for (const auto &pool_job: product.second._pool_jobs) { + if (pool_job._pool_id == 0) { + for (const auto &machine_job: pool_job._machine_jobs) { + if (machine_job._machine_id == i) { + in_out[product.first] = InOut{machine_job._ins, machine_job._outs}; + } + } + } + } + } + std::get<2>(factory._pools[0]).push_back(std::make_shared(ProcessorParameters{{i, 0, 0, 1}, + {{}, 1, 3, 1}, + in_out, + {}})); + } + std::get<2>(factory._pools[0]).push_back( + std::make_shared(SinkParameters{{machine_number, 1, 0, 1}})); + // intermediate stocks + for (unsigned int i = 0; i < machine_number - 1; ++i) { + std::get<3>(factory._pools[0]).push_back({i, -1}); + } + + fix_durations(); + } + + Factory factory; }; -BOOST_AUTO_TEST_CASE(TestCase_OnePool) -{ - artis::common::context::Context context(0, 8 * 3600); // 8h - artis::common::RootCoordinator< - artis::common::DoubleTime, artis::pdevs::Coordinator< - artis::common::DoubleTime, OnlyOnePoolGraphManager, artis::common::NoParameters> - > rc(context, "root", artis::common::NoParameters(), artis::common::NoParameters()); +int main() { + Evaluator evaluator; - rc.attachView("PoolRouter", new PoolRouterView()); - rc.switch_to_timed_observer(1); + for (unsigned int i = 0; i < iteration_number; ++i) { + Solution solution{make_po_order(), make_capacities()}; - steady_clock::time_point t1 = steady_clock::now(); - - std::cout << "OK" << std::endl; - rc.run(context); - std::cout << "OK" << std::endl; - - steady_clock::time_point t2 = steady_clock::now(); - - duration time_span = duration_cast >(t2 - t1); - - std::cout << "Duration: " << time_span.count() << std::endl; - - artis::factory::Output output(rc.observer()); - - output(context.begin(), context.end(), {context.begin(), 1}); - - std::cout << artis::factory::Trace::trace().elements().filter_level_type(artis::common::LevelType::USER).to_string() - << std::endl; - - BOOST_CHECK(true); -} + std::cout << evaluator.evaluate(solution)[0] << " " << evaluator.evaluate(solution)[1] << std::endl; + } +} \ No newline at end of file