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),
34 rcpsp_.set_deadline(-1);
35 rcpsp_.set_horizon(-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 "
63 << rcpsp_.tasks_size() <<
" tasks, and " << rcpsp_.resources_size()
66 return num_declared_tasks_ + 2 == rcpsp_.tasks_size() &&
67 load_status_ == PARSING_FINISHED;
70 void RcpspParser::ReportError(
const std::string& line) {
71 LOG(
ERROR) <<
"Error: status = " << load_status_ <<
", line = " << line;
72 load_status_ = ERROR_FOUND;
75 void RcpspParser::SetNumDeclaredTasks(
int t) {
76 num_declared_tasks_ = t;
77 recipe_sizes_.resize(t + 2, 0);
80 void 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") {
96 rcpsp_.set_basedata(words[3]);
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") {
116 rcpsp_.set_horizon(strtoint32(words[1]));
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) {
121 Resource*
const res = rcpsp_.add_resources();
122 res->set_max_capacity(-1);
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) {
128 Resource*
const res = rcpsp_.add_resources();
129 res->set_max_capacity(-1);
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]));
151 rcpsp_.set_release_date(strtoint32(words[2]));
152 rcpsp_.set_due_date(strtoint32(words[3]));
153 rcpsp_.set_tardiness_cost(strtoint32(words[4]));
154 rcpsp_.set_mpm_time(strtoint32(words[5]));
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;
167 CHECK_EQ(task_index, rcpsp_.tasks_size());
168 recipe_sizes_[task_index] = strtoint32(words[1]);
169 const int num_successors = strtoint32(words[2]);
170 if (words.size() != 3 + num_successors) {
174 Task*
const task = rcpsp_.add_tasks();
175 for (
int i = 0; i < num_successors; ++i) {
177 task->add_successors(strtoint32(words[3 + i]) - 1);
179 }
else if (words[0] ==
"REQUESTS/DURATIONS") {
180 load_status_ = REQUEST_SECTION;
186 case REQUEST_SECTION: {
187 if (words[0] ==
"jobnr.") {
189 }
else if (words.size() == 3 + rcpsp_.resources_size()) {
191 current_task_ = strtoint32(words[0]) - 1;
192 const int current_recipe = strtoint32(words[1]) - 1;
193 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
194 if (current_recipe != 0) {
198 Recipe*
const recipe =
199 rcpsp_.mutable_tasks(current_task_)->add_recipes();
200 recipe->set_duration(strtoint32(words[2]));
201 for (
int i = 0; i < rcpsp_.resources_size(); ++i) {
202 const int demand = strtoint32(words[3 + i]);
204 recipe->add_demands(
demand);
205 recipe->add_resources(i);
208 }
else if (words.size() == 2 + rcpsp_.resources_size()) {
210 const int current_recipe = strtoint32(words[0]) - 1;
211 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
212 Recipe*
const recipe =
213 rcpsp_.mutable_tasks(current_task_)->add_recipes();
214 recipe->set_duration(strtoint32(words[1]));
215 for (
int i = 0; i < rcpsp_.resources_size(); ++i) {
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: {
231 if (words.size() == 2 * rcpsp_.resources_size()) {
233 }
else if (words.size() == rcpsp_.resources_size()) {
234 for (
int i = 0; i < words.size(); ++i) {
235 rcpsp_.mutable_resources(i)->set_max_capacity(strtoint32(words[i]));
237 load_status_ = PARSING_FINISHED;
243 case RESOURCE_MIN_SECTION: {
247 case PARSING_FINISHED: {
256 void 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: {
266 rcpsp_.set_is_rcpsp_max(
true);
267 if (words.size() == 2) {
268 rcpsp_.set_is_consumer_producer(
true);
269 }
else if (words.size() < 4 || strtoint32(words[3]) != 0) {
274 if (words.size() == 5) {
275 rcpsp_.set_deadline(strtoint32(words[4]));
276 rcpsp_.set_is_resource_investment(
true);
279 SetNumDeclaredTasks(strtoint32(words[0]));
280 temp_delays_.resize(num_declared_tasks_ + 2);
283 if (rcpsp_.is_consumer_producer()) {
284 const int num_nonrenewable_resources = strtoint32(words[1]);
285 for (
int i = 0; i < num_nonrenewable_resources; ++i) {
286 Resource*
const res = rcpsp_.add_resources();
287 res->set_max_capacity(-1);
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) {
296 Resource*
const res = rcpsp_.add_resources();
297 res->set_max_capacity(-1);
298 res->set_renewable(
true);
299 res->set_unit_cost(0);
301 for (
int i = 0; i < num_nonrenewable_resources; ++i) {
302 Resource*
const res = rcpsp_.add_resources();
303 res->set_max_capacity(-1);
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]);
341 Task*
const task = rcpsp_.add_tasks();
344 for (
int i = 0; i < num_successors; ++i) {
345 task->add_successors(strtoint32(words[3 + 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];
358 const int num_successors = rcpsp_.tasks(t).successors_size();
360 for (
int s = 0; s < num_successors; ++s) {
361 PerSuccessorDelays*
const succ_delays =
362 rcpsp_.mutable_tasks(t)->add_successor_delays();
363 for (
int r1 = 0; r1 < num_recipes; ++r1) {
364 PerRecipeDelays*
const recipe_delays =
365 succ_delays->add_recipe_delays();
366 const int other = rcpsp_.tasks(t).successors(s);
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: {
383 if (words.size() == 3 + rcpsp_.resources_size()) {
385 current_task_ = strtoint32(words[0]);
388 const int current_recipe = strtoint32(words[1]) - 1;
389 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
390 if (current_recipe != 0) {
394 Recipe*
const recipe =
395 rcpsp_.mutable_tasks(current_task_)->add_recipes();
396 recipe->set_duration(strtoint32(words[2]));
397 for (
int i = 0; i < rcpsp_.resources_size(); ++i) {
398 const int demand = strtoint32(words[3 + i]);
400 recipe->add_demands(
demand);
401 recipe->add_resources(i);
404 }
else if (words.size() == 2 + rcpsp_.resources_size() &&
405 rcpsp_.is_consumer_producer()) {
407 current_task_ = strtoint32(words[0]);
410 const int current_recipe = strtoint32(words[1]) - 1;
411 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
412 if (current_recipe != 0) {
416 Recipe*
const recipe =
417 rcpsp_.mutable_tasks(current_task_)->add_recipes();
418 recipe->set_duration(0);
419 for (
int i = 0; i < rcpsp_.resources_size(); ++i) {
420 const int demand = strtoint32(words[2 + i]);
422 recipe->add_demands(
demand);
423 recipe->add_resources(i);
426 }
else if (words.size() == 2 + rcpsp_.resources_size()) {
428 const int current_recipe = strtoint32(words[0]) - 1;
429 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
430 Recipe*
const recipe =
431 rcpsp_.mutable_tasks(current_task_)->add_recipes();
432 recipe->set_duration(strtoint32(words[1]));
433 for (
int i = 0; i < rcpsp_.resources_size(); ++i) {
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) {
442 if (rcpsp_.is_consumer_producer()) {
443 load_status_ = RESOURCE_MIN_SECTION;
445 load_status_ = RESOURCE_SECTION;
450 case RESOURCE_SECTION: {
451 if (words.size() == rcpsp_.resources_size()) {
452 for (
int i = 0; i < words.size(); ++i) {
453 if (rcpsp_.is_resource_investment()) {
454 rcpsp_.mutable_resources(i)->set_unit_cost(strtoint32(words[i]));
456 rcpsp_.mutable_resources(i)->set_max_capacity(strtoint32(words[i]));
459 load_status_ = PARSING_FINISHED;
465 case RESOURCE_MIN_SECTION: {
466 if (words.size() == rcpsp_.resources_size()) {
467 for (
int i = 0; i < words.size(); ++i) {
468 rcpsp_.mutable_resources(i)->set_min_capacity(strtoint32(words[i]));
470 load_status_ = RESOURCE_SECTION;
476 case PARSING_FINISHED: {
485 void 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) {
506 Resource*
const res = rcpsp_.add_resources();
507 res->set_max_capacity(-1);
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) {
528 rcpsp_.mutable_tasks(current_task_)
529 ->add_successors(strtoint32(words[i]) - 1);
534 if (words.size() < 2 + rcpsp_.resources_size()) {
538 CHECK_EQ(current_task_, rcpsp_.tasks_size());
539 Task*
const task = rcpsp_.add_tasks();
540 Recipe*
const recipe = task->add_recipes();
541 recipe->set_duration(strtoint32(words[0]));
543 const int num_resources = rcpsp_.resources_size();
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: {
571 if (words.size() == rcpsp_.resources_size()) {
572 for (
int i = 0; i < words.size(); ++i) {
573 rcpsp_.mutable_resources(i)->set_max_capacity(strtoint32(words[i]));
575 load_status_ = PRECEDENCE_SECTION;
582 case RESOURCE_MIN_SECTION: {
586 case PARSING_FINISHED: {
595 int RcpspParser::strtoint32(
const std::string& word) {
597 CHECK(absl::SimpleAtoi(word, &result));
601 int64_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)
bool ParseFile(const std::string &file_name)
Collection of objects used to extend the Constraint Solver library.