16 #include "absl/strings/match.h"
17 #include "absl/strings/numbers.h"
18 #include "absl/strings/str_split.h"
28 load_status_(NOT_STARTED),
29 num_declared_tasks_(-1),
32 rcpsp_.set_deadline(-1);
33 rcpsp_.set_horizon(-1);
37 if (load_status_ != NOT_STARTED) {
41 const bool is_rcpsp_max =
42 absl::EndsWith(file_name,
".sch") || absl::EndsWith(file_name,
".SCH");
43 const bool is_patterson = absl::EndsWith(file_name,
".rcp");
44 load_status_ = HEADER_SECTION;
46 for (
const std::string& line :
FileLines(file_name)) {
48 ProcessRcpspMaxLine(line);
49 }
else if (is_patterson) {
50 ProcessPattersonLine(line);
52 ProcessRcpspLine(line);
54 if (load_status_ == ERROR_FOUND) {
55 LOG(
INFO) << rcpsp_.DebugString();
59 VLOG(1) <<
"Read file: " << file_name <<
", max = " << is_rcpsp_max
60 <<
", patterson = " << is_patterson <<
", with "
61 << rcpsp_.tasks_size() <<
" tasks, and " << rcpsp_.resources_size()
64 return num_declared_tasks_ + 2 == rcpsp_.tasks_size() &&
65 load_status_ == PARSING_FINISHED;
68 void RcpspParser::ReportError(
const std::string& line) {
69 LOG(
ERROR) <<
"Error: status = " << load_status_ <<
", line = " << line;
70 load_status_ = ERROR_FOUND;
73 void RcpspParser::SetNumDeclaredTasks(
int t) {
74 num_declared_tasks_ = t;
75 recipe_sizes_.resize(t + 2, 0);
78 void RcpspParser::ProcessRcpspLine(
const std::string& line) {
79 if (absl::StartsWith(line,
"***"))
return;
80 if (absl::StartsWith(line,
"---"))
return;
82 const std::vector<std::string> words =
83 absl::StrSplit(line, absl::ByAnyChar(
" :\t\r"), absl::SkipEmpty());
85 if (words.empty())
return;
87 switch (load_status_) {
92 case HEADER_SECTION: {
93 if (words[0] ==
"file") {
94 rcpsp_.set_basedata(words[3]);
95 }
else if (words[0] ==
"initial") {
96 rcpsp_.set_seed(strtoint64(words[4]));
97 load_status_ = PROJECT_SECTION;
98 }
else if (words[0] ==
"jobs") {
100 SetNumDeclaredTasks(strtoint32(words[4]) - 2);
101 load_status_ = PROJECT_SECTION;
107 case PROJECT_SECTION: {
108 if (words[0] ==
"projects") {
110 }
else if (words[0] ==
"jobs") {
112 SetNumDeclaredTasks(strtoint32(words[4]) - 2);
113 }
else if (words[0] ==
"horizon") {
114 rcpsp_.set_horizon(strtoint32(words[1]));
115 }
else if (words[0] ==
"RESOURCES") {
117 }
else if (words.size() > 1 && words[1] ==
"renewable") {
118 for (
int i = 0; i < strtoint32(words[2]); ++i) {
119 Resource*
const res = rcpsp_.add_resources();
120 res->set_max_capacity(-1);
121 res->set_renewable(
true);
122 res->set_unit_cost(0);
124 }
else if (words.size() > 1 && words[1] ==
"nonrenewable") {
125 for (
int i = 0; i < strtoint32(words[2]); ++i) {
126 Resource*
const res = rcpsp_.add_resources();
127 res->set_max_capacity(-1);
128 res->set_min_capacity(-1);
129 res->set_renewable(
false);
130 res->set_unit_cost(0);
132 }
else if (words.size() > 1 && words[1] ==
"doubly") {
134 }
else if (words.size() == 2 && words[0] ==
"PROJECT") {
135 load_status_ = INFO_SECTION;
136 }
else if (words.size() == 2 && words[0] ==
"PRECEDENCE") {
138 load_status_ = PRECEDENCE_SECTION;
145 if (words[0] ==
"pronr.") {
147 }
else if (words.size() == 6) {
148 SetNumDeclaredTasks(strtoint32(words[1]));
149 rcpsp_.set_release_date(strtoint32(words[2]));
150 rcpsp_.set_due_date(strtoint32(words[3]));
151 rcpsp_.set_tardiness_cost(strtoint32(words[4]));
152 rcpsp_.set_mpm_time(strtoint32(words[5]));
153 }
else if (words.size() == 2 && words[0] ==
"PRECEDENCE") {
154 load_status_ = PRECEDENCE_SECTION;
160 case PRECEDENCE_SECTION: {
161 if (words[0] ==
"jobnr.") {
163 }
else if (words.size() >= 3) {
164 const int task_index = strtoint32(words[0]) - 1;
165 CHECK_EQ(task_index, rcpsp_.tasks_size());
166 recipe_sizes_[task_index] = strtoint32(words[1]);
167 const int num_successors = strtoint32(words[2]);
168 if (words.size() != 3 + num_successors) {
172 Task*
const task = rcpsp_.add_tasks();
173 for (
int i = 0; i < num_successors; ++i) {
175 task->add_successors(strtoint32(words[3 + i]) - 1);
177 }
else if (words[0] ==
"REQUESTS/DURATIONS") {
178 load_status_ = REQUEST_SECTION;
184 case REQUEST_SECTION: {
185 if (words[0] ==
"jobnr.") {
187 }
else if (words.size() == 3 + rcpsp_.resources_size()) {
189 current_task_ = strtoint32(words[0]) - 1;
190 const int current_recipe = strtoint32(words[1]) - 1;
191 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
192 if (current_recipe != 0) {
196 Recipe*
const recipe =
197 rcpsp_.mutable_tasks(current_task_)->add_recipes();
198 recipe->set_duration(strtoint32(words[2]));
199 for (
int i = 0; i < rcpsp_.resources_size(); ++i) {
200 const int demand = strtoint32(words[3 + i]);
202 recipe->add_demands(
demand);
203 recipe->add_resources(i);
206 }
else if (words.size() == 2 + rcpsp_.resources_size()) {
208 const int current_recipe = strtoint32(words[0]) - 1;
209 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
210 Recipe*
const recipe =
211 rcpsp_.mutable_tasks(current_task_)->add_recipes();
212 recipe->set_duration(strtoint32(words[1]));
213 for (
int i = 0; i < rcpsp_.resources_size(); ++i) {
214 const int demand = strtoint32(words[2 + i]);
216 recipe->add_demands(
demand);
217 recipe->add_resources(i);
220 }
else if (words[0] ==
"RESOURCEAVAILABILITIES" ||
221 (words[0] ==
"RESOURCE" && words[1] ==
"AVAILABILITIES")) {
222 load_status_ = RESOURCE_SECTION;
228 case RESOURCE_SECTION: {
229 if (words.size() == 2 * rcpsp_.resources_size()) {
231 }
else if (words.size() == rcpsp_.resources_size()) {
232 for (
int i = 0; i < words.size(); ++i) {
233 rcpsp_.mutable_resources(i)->set_max_capacity(strtoint32(words[i]));
235 load_status_ = PARSING_FINISHED;
241 case RESOURCE_MIN_SECTION: {
245 case PARSING_FINISHED: {
254 void RcpspParser::ProcessRcpspMaxLine(
const std::string& line) {
255 const std::vector<std::string> words =
256 absl::StrSplit(line, absl::ByAnyChar(
" :\t[]\r"), absl::SkipEmpty());
258 switch (load_status_) {
263 case HEADER_SECTION: {
264 rcpsp_.set_is_rcpsp_max(
true);
265 if (words.size() == 2) {
266 rcpsp_.set_is_consumer_producer(
true);
267 }
else if (words.size() < 4 || strtoint32(words[3]) != 0) {
272 if (words.size() == 5) {
273 rcpsp_.set_deadline(strtoint32(words[4]));
274 rcpsp_.set_is_resource_investment(
true);
277 SetNumDeclaredTasks(strtoint32(words[0]));
278 temp_delays_.resize(num_declared_tasks_ + 2);
281 if (rcpsp_.is_consumer_producer()) {
282 const int num_nonrenewable_resources = strtoint32(words[1]);
283 for (
int i = 0; i < num_nonrenewable_resources; ++i) {
284 Resource*
const res = rcpsp_.add_resources();
285 res->set_max_capacity(-1);
286 res->set_min_capacity(-1);
287 res->set_renewable(
false);
288 res->set_unit_cost(0);
291 const int num_renewable_resources = strtoint32(words[1]);
292 const int num_nonrenewable_resources = strtoint32(words[2]);
293 for (
int i = 0; i < num_renewable_resources; ++i) {
294 Resource*
const res = rcpsp_.add_resources();
295 res->set_max_capacity(-1);
296 res->set_renewable(
true);
297 res->set_unit_cost(0);
299 for (
int i = 0; i < num_nonrenewable_resources; ++i) {
300 Resource*
const res = rcpsp_.add_resources();
301 res->set_max_capacity(-1);
302 res->set_min_capacity(-1);
303 res->set_renewable(
false);
304 res->set_unit_cost(0);
309 load_status_ = PRECEDENCE_SECTION;
313 case PROJECT_SECTION: {
321 case PRECEDENCE_SECTION: {
322 if (words.size() < 3) {
327 const int task_id = strtoint32(words[0]);
328 if (task_id != current_task_) {
335 const int num_recipes = strtoint32(words[1]);
336 recipe_sizes_[task_id] = num_recipes;
337 const int num_successors = strtoint32(words[2]);
339 Task*
const task = rcpsp_.add_tasks();
342 for (
int i = 0; i < num_successors; ++i) {
343 task->add_successors(strtoint32(words[3 + i]));
347 for (
int i = 3 + num_successors; i < words.size(); ++i) {
348 temp_delays_[task_id].push_back(strtoint32(words[i]));
351 if (task_id == num_declared_tasks_ + 1) {
354 for (
int t = 1; t <= num_declared_tasks_; ++t) {
355 const int num_recipes = recipe_sizes_[t];
356 const int num_successors = rcpsp_.tasks(t).successors_size();
358 for (
int s = 0; s < num_successors; ++s) {
359 PerSuccessorDelays*
const succ_delays =
360 rcpsp_.mutable_tasks(t)->add_successor_delays();
361 for (
int r1 = 0; r1 < num_recipes; ++r1) {
362 PerRecipeDelays*
const recipe_delays =
363 succ_delays->add_recipe_delays();
364 const int other = rcpsp_.tasks(t).successors(s);
365 const int num_other_recipes = recipe_sizes_[other];
366 for (
int r2 = 0; r2 < num_other_recipes; ++r2) {
367 recipe_delays->add_min_delays(temp_delays_[t][count++]);
371 CHECK_EQ(count, temp_delays_[t].size());
376 load_status_ = REQUEST_SECTION;
380 case REQUEST_SECTION: {
381 if (words.size() == 3 + rcpsp_.resources_size()) {
383 current_task_ = strtoint32(words[0]);
386 const int current_recipe = strtoint32(words[1]) - 1;
387 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
388 if (current_recipe != 0) {
392 Recipe*
const recipe =
393 rcpsp_.mutable_tasks(current_task_)->add_recipes();
394 recipe->set_duration(strtoint32(words[2]));
395 for (
int i = 0; i < rcpsp_.resources_size(); ++i) {
396 const int demand = strtoint32(words[3 + i]);
398 recipe->add_demands(
demand);
399 recipe->add_resources(i);
402 }
else if (words.size() == 2 + rcpsp_.resources_size() &&
403 rcpsp_.is_consumer_producer()) {
405 current_task_ = strtoint32(words[0]);
408 const int current_recipe = strtoint32(words[1]) - 1;
409 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
410 if (current_recipe != 0) {
414 Recipe*
const recipe =
415 rcpsp_.mutable_tasks(current_task_)->add_recipes();
416 recipe->set_duration(0);
417 for (
int i = 0; i < rcpsp_.resources_size(); ++i) {
418 const int demand = strtoint32(words[2 + i]);
420 recipe->add_demands(
demand);
421 recipe->add_resources(i);
424 }
else if (words.size() == 2 + rcpsp_.resources_size()) {
426 const int current_recipe = strtoint32(words[0]) - 1;
427 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
428 Recipe*
const recipe =
429 rcpsp_.mutable_tasks(current_task_)->add_recipes();
430 recipe->set_duration(strtoint32(words[1]));
431 for (
int i = 0; i < rcpsp_.resources_size(); ++i) {
432 const int demand = strtoint32(words[2 + i]);
434 recipe->add_demands(
demand);
435 recipe->add_resources(i);
439 if (current_task_ == num_declared_tasks_ + 1) {
440 if (rcpsp_.is_consumer_producer()) {
441 load_status_ = RESOURCE_MIN_SECTION;
443 load_status_ = RESOURCE_SECTION;
448 case RESOURCE_SECTION: {
449 if (words.size() == rcpsp_.resources_size()) {
450 for (
int i = 0; i < words.size(); ++i) {
451 if (rcpsp_.is_resource_investment()) {
452 rcpsp_.mutable_resources(i)->set_unit_cost(strtoint32(words[i]));
454 rcpsp_.mutable_resources(i)->set_max_capacity(strtoint32(words[i]));
457 load_status_ = PARSING_FINISHED;
463 case RESOURCE_MIN_SECTION: {
464 if (words.size() == rcpsp_.resources_size()) {
465 for (
int i = 0; i < words.size(); ++i) {
466 rcpsp_.mutable_resources(i)->set_min_capacity(strtoint32(words[i]));
468 load_status_ = RESOURCE_SECTION;
474 case PARSING_FINISHED: {
483 void RcpspParser::ProcessPattersonLine(
const std::string& line) {
484 const std::vector<std::string> words =
485 absl::StrSplit(line, absl::ByAnyChar(
" :\t[]\r"), absl::SkipEmpty());
487 if (words.empty())
return;
489 switch (load_status_) {
494 case HEADER_SECTION: {
495 if (words.size() != 2) {
499 SetNumDeclaredTasks(strtoint32(words[0]) - 2);
502 const int num_renewable_resources = strtoint32(words[1]);
503 for (
int i = 0; i < num_renewable_resources; ++i) {
504 Resource*
const res = rcpsp_.add_resources();
505 res->set_max_capacity(-1);
506 res->set_min_capacity(-1);
507 res->set_renewable(
true);
508 res->set_unit_cost(0);
512 load_status_ = RESOURCE_SECTION;
515 case PROJECT_SECTION: {
523 case PRECEDENCE_SECTION: {
525 for (
int i = 0; i < words.size(); ++i) {
526 rcpsp_.mutable_tasks(current_task_)
527 ->add_successors(strtoint32(words[i]) - 1);
532 if (words.size() < 2 + rcpsp_.resources_size()) {
536 CHECK_EQ(current_task_, rcpsp_.tasks_size());
537 Task*
const task = rcpsp_.add_tasks();
538 Recipe*
const recipe = task->add_recipes();
539 recipe->set_duration(strtoint32(words[0]));
541 const int num_resources = rcpsp_.resources_size();
542 for (
int i = 1; i <= num_resources; ++i) {
543 const int demand = strtoint32(words[i]);
545 recipe->add_demands(
demand);
546 recipe->add_resources(i - 1);
550 unreads_ = strtoint32(words[1 + num_resources]);
551 for (
int i = 2 + num_resources; i < words.size(); ++i) {
553 task->add_successors(strtoint32(words[i]) - 1);
559 if (unreads_ == 0 && ++current_task_ == num_declared_tasks_ + 2) {
560 load_status_ = PARSING_FINISHED;
564 case REQUEST_SECTION: {
568 case RESOURCE_SECTION: {
569 if (words.size() == rcpsp_.resources_size()) {
570 for (
int i = 0; i < words.size(); ++i) {
571 rcpsp_.mutable_resources(i)->set_max_capacity(strtoint32(words[i]));
573 load_status_ = PRECEDENCE_SECTION;
580 case RESOURCE_MIN_SECTION: {
584 case PARSING_FINISHED: {
593 int RcpspParser::strtoint32(
const std::string& word) {
595 CHECK(absl::SimpleAtoi(word, &result));
599 int64 RcpspParser::strtoint64(
const std::string& word) {
601 CHECK(absl::SimpleAtoi(word, &result));