#include #include #include "emulator.h" short etching_zone = 0, galvanizing_zone = 0; char zone_is_busy(short zone) { for (short i = 0; i < 10; i++) { if (barrels[i].flags.is_exist && barrels[i].zone == zone) { return 1; } } return 0; } static short get_robot_barrel(char robot_id) { for (short i = 0; i < BARRELS_COUNT; i++) { if (barrels[i].flags.robot == robot_id) { return i; } } return -1; } // вернет можно ли ехать и главное куда ехать, если можно (нельзя если вернулось значение < 0) // -1 вренет что перемещать нельзя // -2 вернет если требуется атомарная операция пассивации short can_move(struct barrel* bar) { // сразу отсекаем варианты, при которых невозможно переместить барабан if (!bar->flags.is_exist) { return -1; } if (bar->software_timer > 0) { return -1; } if (bar->flags.robot != 0) { return -2; } if (!schedulerOneRobotMode) { printf("WARMING: нет проверки того, что для перемещения барабана не мешает второй робот\n"); } // дальше нужно проверить, что можно передвигать бочку switch (bar->curr_process) { case PROCESS_NONE: // загрузка, нужно обезжиривание (зона 2) if (!zone_is_busy(2)) { return 2; } break; case PROCESS_DEFATTING: // обезжиривание, нужна промывка 1А (зона 3) if (!zone_is_busy(3)) { return 3; } break; case PROCESS_WASHING_1A: // промывка 1А, нужна промывка 1Б (зона 4) if (!zone_is_busy(4)) { return 4; } break; case PROCESS_WASHING_1B: // промывка 1Б, нужно травление (зоны 5-6) if (!zone_is_busy(5 + etching_zone)) { return 5 + etching_zone; } break; case PROCESS_ETCHING: // травление, нужна промывка 2А (зона 7) if (!zone_is_busy(7)) { return 7; } break; case PROCESS_WASHING_2A: // промывка 2А, нужна промывка 2Б (зона 8) if (!zone_is_busy(8)) { return 8; } break; case PROCESS_WASHING_2B: // промывка 2Б, нужно цинкование (зоны 9-16) // TODO сделать приоритет на барабан, который больше всего ждет if (!zone_is_busy(9 + galvanizing_zone)) { return 9 + galvanizing_zone; } break; case PROCESS_GALVANIZING: // цинкование, требуется чтобы в зонах 17-22 было максимум 2 барабана (3 барабана для этой части линии - максимум) { short count = 0; for (short i = 17; i <= 22; i++) { if (zone_is_busy(i)) { count++; } } if (count < 3 && !zone_is_busy(17)) { return 17; } } break; case PROCESS_WASHING_3A: // промывка 3А, перекладываем в промывку 3Б (зона 18) if (!zone_is_busy(18)) { return 18; } break; case PROCESS_WASHING_3B: // это перед пассивацией, требует свободную промывку 4А (зона 20) и на всякий случай свободную пассивацию (зона 19) if (!zone_is_busy(19) && !zone_is_busy(20)) { return -2; } // это атомарная операция, по идее вносить барабан в пассивацию нельзя break; case PROCESS_PASSIVATION: // процесс пассивации, нужна промывка 4B (зона 21) (потому что сейчас я в 4A) if (!zone_is_busy(21)) { return 21; } break; case PROCESS_WASHING_4B: // процесс пассивации, нужна промывка 4B (зона 21) (потому что сейчас я в 4A) if (!zone_is_busy(22)) { return 22; } break; case PROCESS_RETURN_1: // последняя промывка, нужно разрешение на выгрузку if (schedulerUnloadButton) { // нужно промывку 3б (зона 10) и загрузку 0 if (!zone_is_busy(17) || !zone_is_busy(0)) { return 17; } } break; case PROCESS_RETURN_2: if (schedulerOneRobotMode) { if (!schedulerUnloadButton) { break; } } // нужна свободная выгрузка if (!zone_is_busy(0)) { return 0; } break; } return -1; } struct barrel makeBarrel(short flags, short zone, short timer, short process) { struct barrel b; b.flags.raw_word = flags; b.zone = zone; b.software_timer = timer; b.curr_process = process; b.time_defatting = 6; b.time_washing_1a = 2; b.time_washing_1b = 3; b.time_etching = 8; b.time_washing_2a = 3; b.time_washing_2b = 4; b.time_galvanizing = 15; b.time_washing_3a = 4; b.time_washing_3b = 5; b.time_passivation = 3; b.time_washing_4a = 6; b.time_washing_4b = 7; return b; } struct scheduler_task { short start_zone; // стартовая зона short dest_zone; // конечная зона }; void schedule_robot_1() { static short transaction_state = 0; // начало транзакции if (transaction_state == 0) { // формируем список задач struct scheduler_task tasks[BARRELS_COUNT]; for (short i = 0; i < BARRELS_COUNT; i++) { // для каждой задачи: tasks[i].start_zone = barrels[i].zone; // определяем можно ли ее выполнить и что вообще нужно выполнить tasks[i].dest_zone = can_move(barrels + i); } // найти подходящую задачу if (schedulerOneRobotMode) { char forward_is_exist = 0; short target_task = -1, first_task = -1; for (short i = 0; i < BARRELS_COUNT; i++) { short target = tasks[i].start_zone; // фактическая зона откуда тащить барабан if (tasks[i].dest_zone == -2) { target = 18; } else if (tasks[i].dest_zone < 0) { continue; } if (robot1.curr_zone <= target) { forward_is_exist = 1; if (target_task == -1) { target_task = i; } else { if (barrels[target_task].zone > target) { target_task = i; } } } // тут нахождение первой задачи, нужно если вдруг if (first_task == -1) { first_task = i; } else { if (barrels[first_task].zone > target) { first_task = i; } } } // итого есть результат: есть ли таски, которые надо тащить вперед (и если надо то какой ближний), и есть первый таск if (!forward_is_exist) { target_task = first_task; } if (target_task >= 0) { // создаем транзакцию if (tasks[target_task].dest_zone == -2) { // пассивация printf("Get work: passivate barrel %d\n", target_task); robot1_cmd.cmd = 3; // пассивация } else { printf("Get work: move barrel %d from %d to %d\n", target_task, tasks[target_task].start_zone, tasks[target_task].dest_zone); robot1_cmd.cmd = 2; // везем барабан robot1_cmd.args[0] = tasks[target_task].start_zone; robot1_cmd.args[1] = tasks[target_task].dest_zone; } // barrels[target_barrel].zone = target_zone; barrels[target_task].flags.robot = 1; // TODO сделать нормальное переключение зон с учетом тех, что можно отключить // TODO добавить ограничение - нельзя отключить сразу все зоны цинкования или травления if (tasks[target_task].dest_zone == 3 || tasks[target_task].dest_zone == 4) { etching_zone ^= 0x1; // переключаем следующую зону } if (tasks[target_task].dest_zone >= 9 && tasks[target_task].dest_zone <= 17) { galvanizing_zone = (galvanizing_zone + 1) % 8; } if (tasks[target_task].start_zone == 22) { // выгрузка, снимаем кнопку выгрузки schedulerUnloadButton = 0; } transaction_state = 1; } } else { printf("WARMING: support only one robot mode\n"); } } else if (transaction_state == 1) { if (robot1_cmd.cmd == 0) { transaction_state = 2; } } else { // post transaction short barrel = get_robot_barrel(1); struct barrel* b = &barrels[barrel]; if (barrel >= 0) { b->flags.is_up = 0; b->flags.robot = 0; if (robot1_cmd.cmd == 2) { b->zone = robot1_cmd.args[2]; } else if (robot1_cmd.cmd == 3) { b->zone = 20; } } switch (b->curr_process) { // case curr: bar->curr_process = next; bar->software_timer = bar->time; break // после загрузки: ставим обезжиривание case PROCESS_NONE: b->curr_process = PROCESS_DEFATTING; b->software_timer = b->time_defatting; break; // после обезжира: ставим промывку 1а case PROCESS_DEFATTING: b->curr_process = PROCESS_WASHING_1A; b->software_timer = b->time_washing_1a; break; // после промывки 1а: ставим промывку 1б case PROCESS_WASHING_1A: b->curr_process = PROCESS_WASHING_1B; b->software_timer = b->time_washing_1b; break; // после промывки 1а: ставим травление case PROCESS_WASHING_1B: b->curr_process = PROCESS_ETCHING; b->software_timer = b->time_etching; break; // после травления: ставим промывку 2а case PROCESS_ETCHING: b->curr_process = PROCESS_WASHING_2A; b->software_timer = b->time_washing_2a; break; // после промывки 2а: ставим промывку 2б case PROCESS_WASHING_2A: b->curr_process = PROCESS_WASHING_2B; b->software_timer = b->time_washing_2b; break; // после промывки 2б: ставим цинкование case PROCESS_WASHING_2B: b->curr_process = PROCESS_GALVANIZING; b->software_timer = b->time_galvanizing; break; // после цинкования: ставим промывку 3а case PROCESS_GALVANIZING: b->curr_process = PROCESS_WASHING_3A; b->software_timer = b->time_washing_3a; break; // после промывки 3а: ставим промывку 1б case PROCESS_WASHING_3A: b->curr_process = PROCESS_WASHING_3B; b->software_timer = b->time_washing_3b; break; // после промывки 3б: ставим пассивацию case PROCESS_WASHING_3B: b->curr_process = PROCESS_PASSIVATION; break; // после пассивации, барабан в промывке 4а: ставим промывку 4б case PROCESS_PASSIVATION: b->curr_process = PROCESS_WASHING_4B; b->software_timer = b->time_washing_4b; break; // после промывки 4б: дальше выгрузка case PROCESS_WASHING_4B: if (schedulerOneRobotMode) { b->curr_process = PROCESS_RETURN_2; } else { b->curr_process = PROCESS_RETURN_1; } b->software_timer = 0; break; case PROCESS_RETURN_1: b->curr_process = PROCESS_RETURN_2; b->software_timer = 0; break; case PROCESS_RETURN_2: default: b->flags.is_exist = 0; } transaction_state = 0; } } // вернет false если удалось вставить барабан, иначе true char create_barrel_in_load(short zone) { for (int i = 0; i < BARRELS_COUNT; i++) { if (!barrels[i].flags.is_exist) { barrels[i] = makeBarrel(1, zone, 0, PROCESS_NONE); return 0; } } return 1; } void scheduler_main() { // тут должно быть удаление барабана из зоны 1, если он там есть // if (schedulerLoadButton1) { // schedulerLoadButton1 = create_barrel_in_load(0); // } // тут возможна только вставка барабанов if (schedulerLoadButton2) { schedulerLoadButton2 = create_barrel_in_load(1); } if (schedulerSoftwareTimer) { schedulerSoftwareTimer = 0; for (int i = 0; i < 10; i++) { if (barrels[i].software_timer > -9999) { barrels[i].software_timer--; } } } if (robot1_cmd.cmd == 0) { schedule_robot_1(); } // пока без второго робота // if (robot2_cmd.cmd == 0) { // robot2_cmd.cmd = 2; // пиздуем в зону // robot2_cmd.args[0] = rand() % 23; // } }