#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 -100; } if (bar->flags.robot != 0) { return -2; } // TODO добавить проверку того, что барабан нельзя перетащить если второй робот мешает // дальше нужно проверить, что можно передвигать бочку 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) 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) { // нужна хотя бы одна свободная выгрузка if (!zone_is_busy(0) || !zone_is_busy(1)) { return 7; // TODO сделать нормальный просчет зон промывок } } break; case PROCESS_RETURN_2: if (schedulerOneRobotMode) { if (!schedulerUnloadButton) { break; } } // нужна свободная выгрузка if (!zone_is_busy(0)) { return 0; } if (!zone_is_busy(1)) { return 1; } break; } return -1; } short compute_cost(struct barrel* b, short current_pos, short next_zone) { short delta = (short)(b->zone - current_pos); if (next_zone < 0 && next_zone != -2) { return -3; } if (delta < 0) { return (short)((-delta) * 2); } else { return delta; } } 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; } void schedule_robot_1(struct robot_cmd* cmd) { static short transaction_state = 0; if (transaction_state == 0) { // ищем работу роботу short min_cost = -1, target_barrel = 0, next_zone; for (short i = 0; i < BARRELS_COUNT; i++) { if (barrels[i].flags.robot == 1) { barrels[i].flags.robot = 0; } short nz = can_move(barrels + i); if (nz >= 0 || nz == -2) { short cost = compute_cost(barrels + i, robot1.curr_zone, nz); printf("Barrel calc cost result: id=%d cost=%d\n", i, cost); if (cost >= 0) { if (cost < min_cost || min_cost == -1) { min_cost = cost; target_barrel = i; next_zone = nz; } } } } if (min_cost >= 0) { if (next_zone == -2) { // пассивация printf("Get work: passivate barrel %d\n", target_barrel); robot1_cmd.cmd = 3; // пассивация } else { printf("Get work: move barrel %d from %d to %d\n", target_barrel, barrels[target_barrel].zone, next_zone); robot1_cmd.cmd = 2; // везем барабан robot1_cmd.args[0] = barrels[target_barrel].zone; robot1_cmd.args[1] = next_zone; } // barrels[target_barrel].zone = target_zone; barrels[target_barrel].flags.robot = 1; // TODO сделать нормальное переключение зон с учетом тех, что можно отключить // TODO добавить ограничение - нельзя отключить сразу все зоны цинкования или травления if (next_zone == 3 || next_zone == 4) { etching_zone ^= 0x1; // переключаем следующую зону } if (next_zone >= 9 && next_zone <= 17) { galvanizing_zone = (galvanizing_zone + 1) % 8; } if (barrels[target_barrel].zone == 22) { // выгрузка, снимаем кнопку выгрузки schedulerUnloadButton = 0; } transaction_state = 1; } } 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 (cmd->cmd == 2) { b->zone = cmd->args[2]; } else if (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() { // вставка барабанов 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(&robot1_cmd); } // пока без второго робота // if (robot2_cmd.cmd == 0) { // robot2_cmd.cmd = 2; // пиздуем в зону // robot2_cmd.args[0] = rand() % 23; // } }