18#include "absl/strings/match.h"
19#include "absl/strings/numbers.h"
20#include "absl/strings/str_split.h"
30 load_status_(NOT_STARTED),
31 num_declared_tasks_(-1),
39 if (load_status_ != NOT_STARTED) {
43 const bool is_rcpsp_max =
44 absl::EndsWith(file_name,
".sch") || absl::EndsWith(file_name,
".SCH");
45 const bool is_patterson = absl::EndsWith(file_name,
".rcp");
46 load_status_ = HEADER_SECTION;
48 for (
const std::string& line :
FileLines(file_name)) {
50 ProcessRcpspMaxLine(line);
51 }
else if (is_patterson) {
52 ProcessPattersonLine(line);
54 ProcessRcpspLine(line);
56 if (load_status_ == ERROR_FOUND) {
57 LOG(
INFO) << rcpsp_.DebugString();
61 VLOG(1) <<
"Read file: " << file_name <<
", max = " << is_rcpsp_max
62 <<
", patterson = " << is_patterson <<
", with "
66 return num_declared_tasks_ + 2 == rcpsp_.
tasks_size() &&
67 load_status_ == PARSING_FINISHED;
70void RcpspParser::ReportError(
const std::string& line) {
71 LOG(
ERROR) <<
"Error: status = " << load_status_ <<
", line = " << line;
72 load_status_ = ERROR_FOUND;
75void RcpspParser::SetNumDeclaredTasks(
int t) {
76 num_declared_tasks_ = t;
77 recipe_sizes_.resize(t + 2, 0);
80void RcpspParser::ProcessRcpspLine(
const std::string& line) {
81 if (absl::StartsWith(line,
"***"))
return;
82 if (absl::StartsWith(line,
"---"))
return;
84 const std::vector<std::string> words =
85 absl::StrSplit(line, absl::ByAnyChar(
" :\t\r"), absl::SkipEmpty());
87 if (words.empty())
return;
89 switch (load_status_) {
94 case HEADER_SECTION: {
95 if (words[0] ==
"file") {
97 }
else if (words[0] ==
"initial") {
98 rcpsp_.
set_seed(strtoint64(words[4]));
99 load_status_ = PROJECT_SECTION;
100 }
else if (words[0] ==
"jobs") {
102 SetNumDeclaredTasks(strtoint32(words[4]) - 2);
103 load_status_ = PROJECT_SECTION;
109 case PROJECT_SECTION: {
110 if (words[0] ==
"projects") {
112 }
else if (words[0] ==
"jobs") {
114 SetNumDeclaredTasks(strtoint32(words[4]) - 2);
115 }
else if (words[0] ==
"horizon") {
117 }
else if (words[0] ==
"RESOURCES") {
119 }
else if (words.size() > 1 && words[1] ==
"renewable") {
120 for (
int i = 0; i < strtoint32(words[2]); ++i) {
123 res->set_renewable(
true);
124 res->set_unit_cost(0);
126 }
else if (words.size() > 1 && words[1] ==
"nonrenewable") {
127 for (
int i = 0; i < strtoint32(words[2]); ++i) {
130 res->set_min_capacity(-1);
131 res->set_renewable(
false);
132 res->set_unit_cost(0);
134 }
else if (words.size() > 1 && words[1] ==
"doubly") {
136 }
else if (words.size() == 2 && words[0] ==
"PROJECT") {
137 load_status_ = INFO_SECTION;
138 }
else if (words.size() == 2 && words[0] ==
"PRECEDENCE") {
140 load_status_ = PRECEDENCE_SECTION;
147 if (words[0] ==
"pronr.") {
149 }
else if (words.size() == 6) {
150 SetNumDeclaredTasks(strtoint32(words[1]));
155 }
else if (words.size() == 2 && words[0] ==
"PRECEDENCE") {
156 load_status_ = PRECEDENCE_SECTION;
162 case PRECEDENCE_SECTION: {
163 if (words[0] ==
"jobnr.") {
165 }
else if (words.size() >= 3) {
166 const int task_index = strtoint32(words[0]) - 1;
168 recipe_sizes_[task_index] = strtoint32(words[1]);
169 const int num_successors = strtoint32(words[2]);
170 if (words.size() != 3 + num_successors) {
175 for (
int i = 0; i < num_successors; ++i) {
179 }
else if (words[0] ==
"REQUESTS/DURATIONS") {
180 load_status_ = REQUEST_SECTION;
186 case REQUEST_SECTION: {
187 if (words[0] ==
"jobnr.") {
191 current_task_ = strtoint32(words[0]) - 1;
192 const int current_recipe = strtoint32(words[1]) - 1;
194 if (current_recipe != 0) {
198 Recipe*
const recipe =
202 const int demand = strtoint32(words[3 + i]);
204 recipe->add_demands(
demand);
205 recipe->add_resources(i);
210 const int current_recipe = strtoint32(words[0]) - 1;
212 Recipe*
const recipe =
216 const int demand = strtoint32(words[2 + i]);
218 recipe->add_demands(
demand);
219 recipe->add_resources(i);
222 }
else if (words[0] ==
"RESOURCEAVAILABILITIES" ||
223 (words[0] ==
"RESOURCE" && words[1] ==
"AVAILABILITIES")) {
224 load_status_ = RESOURCE_SECTION;
230 case RESOURCE_SECTION: {
234 for (
int i = 0; i < words.size(); ++i) {
237 load_status_ = PARSING_FINISHED;
243 case RESOURCE_MIN_SECTION: {
247 case PARSING_FINISHED: {
256void RcpspParser::ProcessRcpspMaxLine(
const std::string& line) {
257 const std::vector<std::string> words =
258 absl::StrSplit(line, absl::ByAnyChar(
" :\t[]\r"), absl::SkipEmpty());
260 switch (load_status_) {
265 case HEADER_SECTION: {
267 if (words.size() == 2) {
269 }
else if (words.size() < 4 || strtoint32(words[3]) != 0) {
274 if (words.size() == 5) {
279 SetNumDeclaredTasks(strtoint32(words[0]));
280 temp_delays_.resize(num_declared_tasks_ + 2);
284 const int num_nonrenewable_resources = strtoint32(words[1]);
285 for (
int i = 0; i < num_nonrenewable_resources; ++i) {
288 res->set_min_capacity(-1);
289 res->set_renewable(
false);
290 res->set_unit_cost(0);
293 const int num_renewable_resources = strtoint32(words[1]);
294 const int num_nonrenewable_resources = strtoint32(words[2]);
295 for (
int i = 0; i < num_renewable_resources; ++i) {
298 res->set_renewable(
true);
299 res->set_unit_cost(0);
301 for (
int i = 0; i < num_nonrenewable_resources; ++i) {
304 res->set_min_capacity(-1);
305 res->set_renewable(
false);
306 res->set_unit_cost(0);
311 load_status_ = PRECEDENCE_SECTION;
315 case PROJECT_SECTION: {
323 case PRECEDENCE_SECTION: {
324 if (words.size() < 3) {
329 const int task_id = strtoint32(words[0]);
330 if (task_id != current_task_) {
337 const int num_recipes = strtoint32(words[1]);
338 recipe_sizes_[task_id] = num_recipes;
339 const int num_successors = strtoint32(words[2]);
344 for (
int i = 0; i < num_successors; ++i) {
349 for (
int i = 3 + num_successors; i < words.size(); ++i) {
350 temp_delays_[task_id].push_back(strtoint32(words[i]));
353 if (task_id == num_declared_tasks_ + 1) {
356 for (
int t = 1; t <= num_declared_tasks_; ++t) {
357 const int num_recipes = recipe_sizes_[t];
360 for (
int s = 0; s < num_successors; ++s) {
361 PerSuccessorDelays*
const succ_delays =
363 for (
int r1 = 0; r1 < num_recipes; ++r1) {
364 PerRecipeDelays*
const recipe_delays =
367 const int num_other_recipes = recipe_sizes_[other];
368 for (
int r2 = 0; r2 < num_other_recipes; ++r2) {
369 recipe_delays->add_min_delays(temp_delays_[t][count++]);
373 CHECK_EQ(count, temp_delays_[t].size());
378 load_status_ = REQUEST_SECTION;
382 case REQUEST_SECTION: {
385 current_task_ = strtoint32(words[0]);
388 const int current_recipe = strtoint32(words[1]) - 1;
390 if (current_recipe != 0) {
394 Recipe*
const recipe =
398 const int demand = strtoint32(words[3 + i]);
400 recipe->add_demands(
demand);
401 recipe->add_resources(i);
407 current_task_ = strtoint32(words[0]);
410 const int current_recipe = strtoint32(words[1]) - 1;
412 if (current_recipe != 0) {
416 Recipe*
const recipe =
420 const int demand = strtoint32(words[2 + i]);
422 recipe->add_demands(
demand);
423 recipe->add_resources(i);
428 const int current_recipe = strtoint32(words[0]) - 1;
430 Recipe*
const recipe =
434 const int demand = strtoint32(words[2 + i]);
436 recipe->add_demands(
demand);
437 recipe->add_resources(i);
441 if (current_task_ == num_declared_tasks_ + 1) {
443 load_status_ = RESOURCE_MIN_SECTION;
445 load_status_ = RESOURCE_SECTION;
450 case RESOURCE_SECTION: {
452 for (
int i = 0; i < words.size(); ++i) {
459 load_status_ = PARSING_FINISHED;
465 case RESOURCE_MIN_SECTION: {
467 for (
int i = 0; i < words.size(); ++i) {
470 load_status_ = RESOURCE_SECTION;
476 case PARSING_FINISHED: {
485void RcpspParser::ProcessPattersonLine(
const std::string& line) {
486 const std::vector<std::string> words =
487 absl::StrSplit(line, absl::ByAnyChar(
" :\t[]\r"), absl::SkipEmpty());
489 if (words.empty())
return;
491 switch (load_status_) {
496 case HEADER_SECTION: {
497 if (words.size() != 2) {
501 SetNumDeclaredTasks(strtoint32(words[0]) - 2);
504 const int num_renewable_resources = strtoint32(words[1]);
505 for (
int i = 0; i < num_renewable_resources; ++i) {
508 res->set_min_capacity(-1);
509 res->set_renewable(
true);
510 res->set_unit_cost(0);
514 load_status_ = RESOURCE_SECTION;
517 case PROJECT_SECTION: {
525 case PRECEDENCE_SECTION: {
527 for (
int i = 0; i < words.size(); ++i) {
544 for (
int i = 1; i <= num_resources; ++i) {
545 const int demand = strtoint32(words[i]);
547 recipe->add_demands(
demand);
548 recipe->add_resources(i - 1);
552 unreads_ = strtoint32(words[1 + num_resources]);
553 for (
int i = 2 + num_resources; i < words.size(); ++i) {
555 task->add_successors(strtoint32(words[i]) - 1);
561 if (unreads_ == 0 && ++current_task_ == num_declared_tasks_ + 2) {
562 load_status_ = PARSING_FINISHED;
566 case REQUEST_SECTION: {
570 case RESOURCE_SECTION: {
572 for (
int i = 0; i < words.size(); ++i) {
575 load_status_ = PRECEDENCE_SECTION;
582 case RESOURCE_MIN_SECTION: {
586 case PARSING_FINISHED: {
595int RcpspParser::strtoint32(
const std::string& word) {
597 CHECK(absl::SimpleAtoi(word, &result));
601int64_t RcpspParser::strtoint64(
const std::string& word) {
603 CHECK(absl::SimpleAtoi(word, &result));
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define VLOG(verboselevel)
::operations_research::scheduling::rcpsp::PerRecipeDelays * add_recipe_delays()
bool ParseFile(const std::string &file_name)
::operations_research::scheduling::rcpsp::Task * mutable_tasks(int index)
void set_deadline(::PROTOBUF_NAMESPACE_ID::int32 value)
::operations_research::scheduling::rcpsp::Task * add_tasks()
void set_basedata(ArgT0 &&arg0, ArgT... args)
void set_is_consumer_producer(bool value)
void set_seed(::PROTOBUF_NAMESPACE_ID::int64 value)
void set_horizon(::PROTOBUF_NAMESPACE_ID::int32 value)
::operations_research::scheduling::rcpsp::Resource * mutable_resources(int index)
void set_tardiness_cost(::PROTOBUF_NAMESPACE_ID::int32 value)
void set_mpm_time(::PROTOBUF_NAMESPACE_ID::int32 value)
const ::operations_research::scheduling::rcpsp::Task & tasks(int index) const
void set_is_rcpsp_max(bool value)
int resources_size() const
::operations_research::scheduling::rcpsp::Resource * add_resources()
bool is_consumer_producer() const
bool is_resource_investment() const
void set_due_date(::PROTOBUF_NAMESPACE_ID::int32 value)
void set_release_date(::PROTOBUF_NAMESPACE_ID::int32 value)
void set_is_resource_investment(bool value)
void set_duration(::PROTOBUF_NAMESPACE_ID::int32 value)
void set_min_capacity(::PROTOBUF_NAMESPACE_ID::int32 value)
void set_unit_cost(::PROTOBUF_NAMESPACE_ID::int32 value)
void set_max_capacity(::PROTOBUF_NAMESPACE_ID::int32 value)
void add_successors(::PROTOBUF_NAMESPACE_ID::int32 value)
int successors_size() const
::operations_research::scheduling::rcpsp::Recipe * add_recipes()
::operations_research::scheduling::rcpsp::PerSuccessorDelays * add_successor_delays()
::PROTOBUF_NAMESPACE_ID::int32 successors(int index) const
Collection of objects used to extend the Constraint Solver library.