diff --git a/CMakeLists.txt b/CMakeLists.txt index ef2fad6..afc4fbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(sdp_sheduler C CXX) set(CMAKE_C_STANDARD 17) set(CMAKE_CXX_STANDARD 17) -add_executable(sdp_sheduler scheduler.c emulator.cpp emulator.h robot.cpp robot.h utils.h) +add_executable(sdp_sheduler scheduler.c emulator.cpp emulator.h robot.cpp robot.h utils.h utils.c) add_executable(remote_listener remote_listener.cpp) add_definitions(-DEMULATOR=1) diff --git a/emulator.cpp b/emulator.cpp index 54c1d7b..f5a1e5f 100644 --- a/emulator.cpp +++ b/emulator.cpp @@ -48,6 +48,9 @@ char hla_robot2_en = 0; char _scheduler_en = 1; +short etching_zone = 0, galvanizing_zone = 0; + + static const int ROWS = 10; static const int COLS = 23 * 5 + 1; static char buffer[ROWS][COLS]; @@ -196,10 +199,10 @@ static void open_socket() { } serv_addr.sin_family = AF_INET; - serv_addr.sin_port = htons(40000); + serv_addr.sin_port = htons(40090); // Convert IPv4 and IPv6 addresses from text to binary form - if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { + if (inet_pton(AF_INET, "192.168.0.160", &serv_addr.sin_addr) <= 0) { printf("\nInvalid address/ Address not supported \n"); exit(-1); } @@ -210,23 +213,29 @@ static void open_socket() { } } -extern "C" struct barrel makeBarrel(short flags, short zone, short timer); +static struct barrel make_barrel(short flags, short zone, short timer) { + barrel b; + b.flags.raw_word = flags; + b.zone = zone; + b.software_timer = timer; + b.time_degreasing = hla_time_degreasing; + b.time_washing_1a = hla_time_washing_1a; + b.time_washing_1b = hla_time_washing_1b; + b.time_etching = hla_time_etching; + b.time_washing_2a = hla_time_washing_2a; + b.time_washing_2b = hla_time_washing_2b; + b.time_galvanizing = hla_time_galvanizing; + b.time_washing_3a = hla_time_washing_3a; + b.time_washing_3b = hla_time_washing_3b; + b.time_passivation = hla_time_passivation; + b.time_washing_4a = hla_time_washing_4a; + b.time_washing_4b = hla_time_washing_4b; + return b; +} int main() { open_socket(); -// for (auto & b : barrels) { -// b.barrel_flags.is_exist = 1; -// b.software_timer = (short)(random() % 50); -// b.zone = (short) abs(random() % 20); -// } - -// barrels[5] = makeBarrel(1, 21, 3); -// barrels[6] = makeBarrel(1, 5, -6); -// barrels[7] = makeBarrel(1, 6, -7); -// -// barrels[8] = makeBarrel(1, 10, -8); - current_tic = 0; const char* message = nullptr; while (true) { @@ -239,7 +248,6 @@ int main() { std::cout << message << std::endl; send_str(message); send_str("\n"); - message = nullptr; } send_str("cmd >> "); diff --git a/emulator.h b/emulator.h index ad72f8e..a6a0897 100644 --- a/emulator.h +++ b/emulator.h @@ -1,5 +1,5 @@ -#ifndef SDP_SHEDULER_EMULATOR_H -#define SDP_SHEDULER_EMULATOR_H +#ifndef SDP_SCHEDULER_EMULATOR_H +#define SDP_SCHEDULER_EMULATOR_H #include "utils.h" @@ -58,4 +58,4 @@ extern char _scheduler_en; } #endif -#endif //SDP_SHEDULER_EMULATOR_H +#endif //SDP_SCHEDULER_EMULATOR_H diff --git a/remote_listener.cpp b/remote_listener.cpp index a0642ee..3897b2a 100644 --- a/remote_listener.cpp +++ b/remote_listener.cpp @@ -11,8 +11,6 @@ static int server_fd; static struct sockaddr_in sock_address; static void createServer() { - int opt = 1; - // Creating socket file descriptor if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket failed"); diff --git a/scheduler.c b/scheduler.c index bb46c97..cc3f4d4 100644 --- a/scheduler.c +++ b/scheduler.c @@ -1,262 +1,14 @@ #ifdef EMULATOR -#include #include "emulator.h" -short etching_zone = 0, galvanizing_zone = 0; #endif -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; -} - -// TODO обновить метод для работы с двумя роботами -// вернет можно ли ехать и главное куда ехать, если можно (нельзя если вернулось значение < 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; - } - -#ifdef EMULATOR - if (!_scheduler_one_robot_mode) { - printf("WARMING: нет проверки того, что для перемещения барабана не мешает второй робот\n"); - } -#endif - - // дальше нужно проверить, что можно передвигать бочку - - switch (bar->zone) { - case ZONE_LOAD_2: - // загрузка 2, только в нее можно грузить новые барабаны, нужно обезжиривание - if (!zone_is_busy(ZONE_DEGREASING)) { - return ZONE_DEGREASING; - } - break; - - case ZONE_DEGREASING: - // обезжиривание, нужна промывка 1А - if (!zone_is_busy(ZONE_WASHING_1A)) { - return ZONE_WASHING_1A; - } - break; - - case ZONE_WASHING_1A: - // промывка 1А, нужна промывка 1Б - if (!zone_is_busy(ZONE_WASHING_1B)) { - return ZONE_WASHING_1B; - } - break; - - case ZONE_WASHING_1B: - // промывка 1Б, нужно травление (зоны 5-6) - if (!zone_is_busy((short)(ZONE_ETCHING_1 + etching_zone))) { - return (short)(ZONE_ETCHING_1 + etching_zone); - } - break; - - case ZONE_ETCHING_1: - case ZONE_ETCHING_2: - // травление, нужна промывка 2А - if (!zone_is_busy(ZONE_WASHING_2A)) { - return ZONE_WASHING_2A; - } - break; - - case ZONE_WASHING_2A: - // промывка 2А, нужна промывка 2Б - if (!zone_is_busy(ZONE_WASHING_2B)) { - return ZONE_WASHING_2B; - } - break; - - case ZONE_WASHING_2B: - // промывка 2Б, нужно цинкование (зоны 9-16) - if (!zone_is_busy((short)(ZONE_GALVANIZING_1 + galvanizing_zone))) { - return (short)(ZONE_GALVANIZING_1 + galvanizing_zone); - } - break; - - case ZONE_GALVANIZING_1: - case ZONE_GALVANIZING_2: - case ZONE_GALVANIZING_3: - case ZONE_GALVANIZING_4: - case ZONE_GALVANIZING_5: - case ZONE_GALVANIZING_6: - case ZONE_GALVANIZING_7: - case ZONE_GALVANIZING_8: - // цинкование, требуется чтобы в зонах 17-22 было максимум 2 барабана (3 барабана для этой части линии - максимум) - if (!zone_is_busy(ZONE_WASHING_3A)) { - short count = 0; - // если зона 17 свободна, то диапазон начнется с 3Б - for (short i = ZONE_WASHING_3B; i <= (short)ZONE_UNLOAD; i++) { - if (zone_is_busy(i)) { - count++; - } - } - if (count < 3) { - return ZONE_WASHING_3A; - } - } - break; - - case ZONE_WASHING_3A: - // промывка 3А, перекладываем в промывку 3Б - if (!zone_is_busy(ZONE_WASHING_3B)) { - return ZONE_WASHING_3B; - } - break; - - case ZONE_WASHING_3B: - // это перед пассивацией, требует свободную промывку 4А и на всякий случай свободную пассивацию - if (!zone_is_busy(ZONE_PASSIVATION) && !zone_is_busy(ZONE_WASHING_4A)) { - return ZONE_PASSIVATION; - } - // это атомарная операция, по идее вносить барабан в пассивацию нельзя - break; - - case ZONE_PASSIVATION: - // процесс пассивации, нужна промывка 4A - // чисто теоретически сюда никогда не попадем, но если вдруг выстрелит пусть будет - if (!zone_is_busy(ZONE_WASHING_4A)) { - return ZONE_WASHING_4A; - } - break; - - case ZONE_WASHING_4A: - // промывка 4А, перекладываем в промывку 4Б - if (!zone_is_busy(ZONE_WASHING_4B)) { - return ZONE_WASHING_4B; - } - break; - - case ZONE_WASHING_4B: - // процесс пассивации, нужна промывка 4B (зона 21) (потому что сейчас я в 4A) - if (!zone_is_busy(ZONE_UNLOAD)) { - return ZONE_UNLOAD; - } - break; - - case ZONE_UNLOAD: - // последняя промывка, нужно разрешение на выгрузку - if (_scheduler_one_robot_mode && button_unload) { - // нужно промывку загрузку 0 - if (!zone_is_busy(ZONE_LOAD_1)) { - return ZONE_LOAD_1; - } - } - break; - } - return -1; -} - - struct scheduler_task { short start_zone; // стартовая зона short dest_zone; // конечная зона short priority; // приоритет, чем больше тем выше, по умолчанию 0 }; -// выставляет приоритет операции (зависит только от зоны, в которой робот находится) -short get_operation_priority(short barrel_id) { - // сделать приоритет на барабан, который больше всего ждет - if (barrels[barrel_id].zone >= ZONE_GALVANIZING_1 && barrels[barrel_id].zone <= ZONE_GALVANIZING_8) { - // теперь надо выяснить, есть ли барабаны с большим временем ожидания - // тут возможны несколько случаев: - // 1) когда барабан один такой (больше в цинковании нет барабанов чтобы их изъять), - // 2) когда барабанов несколько, соответственно если есть барабан с наибольшим временем, то надо ему дать приоритет 1, а остальным 0, - - char is_not_one = 0; - char is_with_max_time = 1; - - for (short i = 0; i < BARRELS_COUNT; i++) { - if (i == barrel_id) { - continue; - } - if (barrels[i].flags.is_exist && barrels[i].zone >= ZONE_GALVANIZING_1 && barrels[i].zone <= ZONE_GALVANIZING_8) { - if (can_move(barrels + i) >= 0) { - is_not_one = 1; - // чем больше у барабана время ожидания тем меньше у него число - if (barrels[i].software_timer < barrels[barrel_id].software_timer) { - is_with_max_time = 0; - break; - } - } - } - } - - if (is_not_one == 0) { - return 1; - } - return is_with_max_time; - } - - - // теперь то же самое, только для травления (только тут задача проще потому что травления всего 2 зоны, и надо только один барабан если он есть) - if (barrels[barrel_id].zone >= ZONE_ETCHING_1 && barrels[barrel_id].zone <= ZONE_ETCHING_2) { - - // если в травлении барабан лежит больше 40 минут, ему присвоить высокий приоритет - if (barrels[barrel_id].software_timer < (-1 * 60 * 40)) { - return 2; - } - - for (short i = 0; i < BARRELS_COUNT; i++) { - if (i == barrel_id) { - continue; - } - - if (barrels[i].flags.is_exist && barrels[i].zone >= ZONE_ETCHING_1 && barrels[i].zone <= ZONE_ETCHING_2) { - if (can_move(barrels + i) >= 0) { - if (barrels[i].software_timer < barrels[barrel_id].software_timer) { - return 0; // у этого барабана больше время ожидания (число меньше), значит приоритет 0 - } else { - return 1; // поскольку у нашего барабана максимальное время ожидания - } - } - } - } - // барабан не найден, приоритет ему 1 - return 1; - } - - return 1; // 1 - нормальный приоритет -} - - -struct barrel makeBarrel(short flags, short zone, short timer) { - struct barrel b; - b.flags.raw_word = flags; - b.zone = zone; - b.software_timer = timer; - b.time_degreasing = hla_time_degreasing; - b.time_washing_1a = hla_time_washing_1a; - b.time_washing_1b = hla_time_washing_1b; - b.time_etching = hla_time_etching; - b.time_washing_2a = hla_time_washing_2a; - b.time_washing_2b = hla_time_washing_2b; - b.time_galvanizing = hla_time_galvanizing; - b.time_washing_3a = hla_time_washing_3a; - b.time_washing_3b = hla_time_washing_3b; - b.time_passivation = hla_time_passivation; - b.time_washing_4a = hla_time_washing_4a; - b.time_washing_4b = hla_time_washing_4b; - return b; -} - short scheduler_find_task(const struct scheduler_task* tasks, const short curr_pos) { // TODO добавить поддержку ночного режима и режима двух роботов @@ -271,7 +23,7 @@ short scheduler_find_task(const struct scheduler_task* tasks, const short curr_p } if (max_priority < 0) { - return -1; // тасков нет) + return -1; // тасков нет } // ищем первый барабан слева, и ближайший справа @@ -323,7 +75,7 @@ short scheduler_find_task(const struct scheduler_task* tasks, const short curr_p short rd = right - curr_pos; // правая дельта // дальше сравниваем дельты - // по идее если они равны то с бОльшим приоритетом робот поедет в левую часть + // по идее если они равны то с большим приоритетом робот поедет в левую часть, // а левую дельту вообще уменьшу на 1, чтобы цель слева казалась ближе if (rd > ld - 1) { return left; @@ -332,301 +84,6 @@ short scheduler_find_task(const struct scheduler_task* tasks, const short curr_p } } -#ifdef EMULATOR -void debug_print_robot_code(const struct robot_code* code, const short robot_id) { -// printf("INFO: code length is %d\n", cmd_index); - printf("Code for R%d, B%d:\n", robot_id, code->barrel_id); - for (int i = 0; i < 16; i++) { - const short cmd_arg = (short)(code->code[i] & (short)(~ROBOT_CMD_MASK)); - - printf("%5d 0x%04X", i, code->code[i] & 0xFFFF); - - if ((code->code[i] & ROBOT_CMD_MASK) == ROBOT_CMD_END_code) { - printf(" END\n"); - break; - } - - switch ((short)(code->code[i] & (short)ROBOT_CMD_MASK)) { - case ROBOT_CMD_MOVE_TO_ZONE_code: - printf(" move to zone %d (with barrel: %d)\n", cmd_arg & (~ROBOT_WITH_BARREL), (cmd_arg & ROBOT_WITH_BARREL) != 0); - break; - - case ROBOT_CMD_MOVE_OFF_code: - printf(" move to offset pos\n"); - break; - - case ROBOT_CMD_MOVE_ACCURATE_code: - printf(" move to accurate pos\n"); - break; - - case ROBOT_CMD_UP_code: - printf(" up (with barrel: %d)\n", (cmd_arg & ROBOT_WITH_BARREL) != 0); - break; - - // в эмуляторе не важно где я, поэтому тут обе команды вниз обрабатываются одинаково - case ROBOT_CMD_DOWN_code: - printf(" down (with barrel: %d)\n", (cmd_arg & ROBOT_WITH_BARREL) != 0); - break; - - case ROBOT_CMD_WAIT_code: - printf(" wait %d secs\n", cmd_arg); - break; - - case ROBOT_CMD_TMR_SET_code: - printf(" set barrel timer %d secs\n", cmd_arg); - break; - - case ROBOT_CMD_CORRECT_AXIS_code: - if (cmd_arg == ROBOT_AXIS_X) { - printf(" correct axis: X\n"); - } else if (cmd_arg == ROBOT_AXIS_Z) { - printf(" correct axis: Z\n"); - } else { - printf(" correct axis: INVALID (%d)\n", cmd_arg); - } - break; - - case ROBOT_CMD_INC_ZONE_code: - if (cmd_arg == ROBOT_ZONE_GAL) { - printf(" increment zone: galvanic\n"); - } else if (cmd_arg == ROBOT_ZONE_ETCH) { - printf(" increment zone: etching\n"); - } else { - printf(" increment zone: INVALID (0x%4X)\n", cmd_arg); - } - break; - - default: - printf(" UNKNOWN: 0x%04X\n", code->code[i] & 0xFFFF); - } - } -} -#endif - - -/* -=== ЦИКЛОГРАММА ПЕРЕТАСКИВАНИЯ БАРАБАНА === -// NOTE первой командой на любую транзакцию должна стоять команда опустить траверсу (в 22 зоне мы никогда не закончим, за нее не беспокоится) -* опустить траверсу -если зона изъятия != промывка 3б - если текущая зона != зона изъятия - если зона изъятия == 22 - * встать на смещенную - * поднять траверсу - * съебать в 21 зону - * встать на смещенную - * опустить траверсу не до конца - * съебать в 22 зону - иначе - * съебать в зону изъятия - * поднять траверсу с барабаном - если зона изъятия != 22 и зона изъятия != 1: - * ждать скапывания (зависит от зоны) - * ехать в зону назначения - если зона назначения == 22 - * опустить траверсу не до конца с барабаном - * съебать в 21 зону - если ЭНКОДЕРЫ СТАРЫЕ (по умолчанию) - * поднять траверсу - * опустить траверсу - иначе - * опустить траверсу с барабаном - если зона назначения != 0 - * установить время ожидания барабана (зависит от зоны) -иначе - если текущая зона != промывка 3б - * съебать в промывку 3б - * поднять траверсу с барабаном - * съебать в пассивацию - * опустить траверсу с барабаном - * поставить время ожидания барабана в <время пассивации> - * поднять траверсу с барабаном - * съебать в зону промывка 4а - * опустить траверсу с барабаном - * установить время ожидания барабана (для промывки 4а) - */ -void create_operation(struct robot_code *code, const short barrel_id, const short start_zone, const short dest_zone, - const short current_zone, const short robot_id) { - // создаем код транзакции, пока обычный - code->barrel_id = barrel_id; - short cmd_index = 0; - - // первым делом добавляем команду опустить траверсу - code->code[cmd_index++] = ROBOT_CMD_DOWN(); - - if (dest_zone == ZONE_PASSIVATION) { - // пассивация, тут все просто - if (current_zone != start_zone) { - code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE(ZONE_WASHING_3B); - } - - code->code[cmd_index++] = ROBOT_CMD_UP_WITH_BARREL(); - code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE_WITH_BARREL(ZONE_PASSIVATION); - code->code[cmd_index++] = ROBOT_CMD_DOWN_WITH_BARREL(); - - code->code[cmd_index++] = ROBOT_CMD_TMR_SET(barrels[barrel_id].time_passivation); - - code->code[cmd_index++] = ROBOT_CMD_UP_WITH_BARREL(); - code->code[cmd_index++] = ROBOT_CMD_WAIT(hla_time_digging); - code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE_WITH_BARREL(ZONE_WASHING_4A); - code->code[cmd_index++] = ROBOT_CMD_DOWN_WITH_BARREL(); - - code->code[cmd_index++] = ROBOT_CMD_TMR_SET(barrels[barrel_id].time_washing_4a); - } else { - // любой другой случай - if (current_zone != start_zone) { - if (start_zone != 22) { - code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE(start_zone); - } else { - code->code[cmd_index++] = ROBOT_CMD_MOVE_OFF(); - code->code[cmd_index++] = ROBOT_CMD_UP(); - code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE(22); - code->code[cmd_index++] = ROBOT_CMD_MOVE_OFF(); - code->code[cmd_index++] = ROBOT_CMD_DOWN(); - code->code[cmd_index++] = ROBOT_CMD_MOVE_ACCURATE(); - } - } - - code->code[cmd_index++] = ROBOT_CMD_UP_WITH_BARREL(); - - // теперь надо определиться с тем, сколько ждать скапывания - switch (start_zone) { - case ZONE_DEGREASING: - case ZONE_ETCHING_1: - case ZONE_ETCHING_2: - case ZONE_GALVANIZING_1: - case ZONE_GALVANIZING_2: - case ZONE_GALVANIZING_3: - case ZONE_GALVANIZING_4: - case ZONE_GALVANIZING_5: - case ZONE_GALVANIZING_6: - case ZONE_GALVANIZING_7: - case ZONE_GALVANIZING_8: - // время скапывания реактивов - code->code[cmd_index++] = ROBOT_CMD_WAIT(hla_time_reagent); - break; - - case ZONE_WASHING_1A: - case ZONE_WASHING_2A: - case ZONE_WASHING_3A: - case ZONE_WASHING_4A: - // время скапывания 1-го каскада промывок - code->code[cmd_index++] = ROBOT_CMD_WAIT(hla_time_washing_1); - break; - - case ZONE_WASHING_1B: - case ZONE_WASHING_2B: - case ZONE_WASHING_3B: - case ZONE_WASHING_4B: - // время скапывания 2-го каскада промывок - code->code[cmd_index++] = ROBOT_CMD_WAIT(hla_time_washing_2); - break; - } - - code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE_WITH_BARREL(dest_zone); - - // инкремент зоны (если травление или цинкование) - if (dest_zone == ZONE_ETCHING_1 || dest_zone == ZONE_ETCHING_2) { - code->code[cmd_index++] = ROBOT_CMD_INC_ZONE(ROBOT_ZONE_ETCH); - } else if (dest_zone >= ZONE_GALVANIZING_1 && dest_zone <= ZONE_GALVANIZING_8) { - code->code[cmd_index++] = ROBOT_CMD_INC_ZONE(ROBOT_ZONE_GAL); - } - - code->code[cmd_index++] = ROBOT_CMD_DOWN_WITH_BARREL(); - - if (dest_zone == 22) { - code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE(21); - // NOTE старая механика не позволяет просто опустить траверсу до конца, для новой изменить поведение - code->code[cmd_index++] = ROBOT_CMD_UP(); - code->code[cmd_index++] = ROBOT_CMD_DOWN(); - } else { - - if (dest_zone != 0) { - - // установка времени ожидания барабана - short tmp = -1; - switch (dest_zone) { - case ZONE_DEGREASING: - tmp = barrels[barrel_id].time_degreasing; - break; - - case ZONE_ETCHING_1: - case ZONE_ETCHING_2: - tmp = barrels[barrel_id].time_etching; - break; - - case ZONE_GALVANIZING_1: - case ZONE_GALVANIZING_2: - case ZONE_GALVANIZING_3: - case ZONE_GALVANIZING_4: - case ZONE_GALVANIZING_5: - case ZONE_GALVANIZING_6: - case ZONE_GALVANIZING_7: - case ZONE_GALVANIZING_8: - tmp = barrels[barrel_id].time_galvanizing; - break; - - case ZONE_WASHING_1A: - tmp = barrels[barrel_id].time_washing_1a; - break; - case ZONE_WASHING_2A: - tmp = barrels[barrel_id].time_washing_2a; - break; - case ZONE_WASHING_3A: - tmp = barrels[barrel_id].time_washing_3a; - break; - case ZONE_WASHING_4A: - tmp = barrels[barrel_id].time_washing_4a; - break; - - case ZONE_WASHING_1B: - tmp = barrels[barrel_id].time_washing_1b; - break; - case ZONE_WASHING_2B: - tmp = barrels[barrel_id].time_washing_2b; - break; - case ZONE_WASHING_3B: - tmp = barrels[barrel_id].time_washing_3b; - break; - case ZONE_WASHING_4B: - tmp = barrels[barrel_id].time_washing_4b; - break; - } - - if (tmp > 0) { - if (tmp > 8000) { - tmp = 8000; - } - code->code[cmd_index++] = ROBOT_CMD_TMR_SET(tmp); - } - } - } - } - - if (!_scheduler_one_robot_mode) { - if (robot_id == 1) { - if (dest_zone >= ZONE_GALVANIZING_1) { - // из промывки 2б он перекладывал, пусть едет в промывку 2а - code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE(ZONE_WASHING_2A); - } else if (dest_zone < ZONE_DEGREASING) { - code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE(ZONE_DEGREASING); - } - } else if (robot_id == 2) { - if (dest_zone <= ZONE_WASHING_3A) { - // чтобы из этой зоны можно было переложить барабан первому роботу - code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE(ZONE_WASHING_4A); - } - } - } - - code->code[cmd_index++] = ROBOT_CMD_END(); - code->PC = 0; - -#ifdef EMULATOR - printf("INFO: code length is %d\n", cmd_index); -#endif -} - void schedule_one_robot(const struct scheduler_task* tasks, const struct robot_regs* r, struct robot_code* code, const short robot_id) { // формируем список задач @@ -642,16 +99,6 @@ void schedule_one_robot(const struct scheduler_task* tasks, const struct robot_r } } -char scheduler_remove_barrel_from_zone(short zone) { - for (short i = 0; i < 10; i++) { - if (barrels[i].flags.is_exist && barrels[i].flags.robot == 0 && barrels[i].zone == zone) { - barrels[i].flags.is_exist = 0; - return 1; - } - } - return 0; -} - #ifdef EMULATOR void scheduler_main() @@ -665,7 +112,21 @@ void scheduler_main() if (!zone_is_busy(1)) { for (int i = 0; i < BARRELS_COUNT; i++) { if (!barrels[i].flags.is_exist) { - barrels[i] = makeBarrel(1, 1, 0); + barrels[i].flags.raw_word = 1; // только is_exist + barrels[i].zone = 1; + barrels[i].software_timer = -1; + barrels[i].time_degreasing = hla_time_degreasing; + barrels[i].time_washing_1a = hla_time_washing_1a; + barrels[i].time_washing_1b = hla_time_washing_1b; + barrels[i].time_etching = hla_time_etching; + barrels[i].time_washing_2a = hla_time_washing_2a; + barrels[i].time_washing_2b = hla_time_washing_2b; + barrels[i].time_galvanizing = hla_time_galvanizing; + barrels[i].time_washing_3a = hla_time_washing_3a; + barrels[i].time_washing_3b = hla_time_washing_3b; + barrels[i].time_passivation = hla_time_passivation; + barrels[i].time_washing_4a = hla_time_washing_4a; + barrels[i].time_washing_4b = hla_time_washing_4b; button_load = 0; break; } @@ -675,12 +136,12 @@ void scheduler_main() // кнопка выгрузки if (button_unload_end) { - button_unload_end = (char)(scheduler_remove_barrel_from_zone(0) != 0); + button_unload_end = (char)(remove_barrel_from_zone(0) != 0); } // кнопка удаления барабана из 22 зоны if (button_unload_remove) { - button_unload_remove = (char)(scheduler_remove_barrel_from_zone(22) != 0); + button_unload_remove = (char)(remove_barrel_from_zone(22) != 0); } // таймер, применяется ко всем существующим барабанам @@ -693,8 +154,7 @@ void scheduler_main() } } - // выбор режима исполнения - if (robot1_code.PC < 0 || robot2_code.PC < 0) { + if ((hla_robot1_en && robot1_code.PC < 0) || (hla_robot2_en && robot2_code.PC < 0)) { struct scheduler_task tasks[BARRELS_COUNT]; for (short i = 0; i < BARRELS_COUNT; i++) { // для каждой задачи: @@ -710,7 +170,7 @@ void scheduler_main() // режим одного робота if (hla_robot1_en && robot1_code.PC < 0) { schedule_one_robot(tasks, &robot1, &robot1_code, 1); - } else if (robot2_code.PC < 0) { + } else if (robot2_code.PC < 0 && hla_robot2_en) { schedule_one_robot(tasks, &robot2, &robot2_code, 2); } } else { diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..11f19d0 --- /dev/null +++ b/utils.c @@ -0,0 +1,548 @@ +// +// Created by Владислав Остапов on 13.12.2022. +// + +#ifdef EMULATOR +#include +#include "emulator.h" +#endif + +char zone_is_busy(short zone) { + for (short i = 0; i < BARRELS_COUNT; i++) { + if (barrels[i].flags.is_exist && barrels[i].zone == zone) { + return 1; + } + } + return 0; +} + + +// TODO обновить метод для работы с двумя роботами +// вернет можно ли ехать и главное куда ехать, если можно (нельзя если вернулось значение < 0) +// -1 вернет что перемещать нельзя +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; + } + +#ifdef EMULATOR + if (!_scheduler_one_robot_mode) { + printf("WARMING: нет проверки того, что для перемещения барабана не мешает второй робот\n"); + } +#endif + + // дальше нужно проверить, что можно передвигать бочку + + switch (bar->zone) { + case ZONE_LOAD_2: + // загрузка 2, только в нее можно грузить новые барабаны, нужно обезжиривание + if (!zone_is_busy(ZONE_DEGREASING)) { + return ZONE_DEGREASING; + } + break; + + case ZONE_DEGREASING: + // обезжиривание, нужна промывка 1А + if (!zone_is_busy(ZONE_WASHING_1A)) { + return ZONE_WASHING_1A; + } + break; + + case ZONE_WASHING_1A: + // промывка 1А, нужна промывка 1Б + if (!zone_is_busy(ZONE_WASHING_1B)) { + return ZONE_WASHING_1B; + } + break; + + case ZONE_WASHING_1B: + // промывка 1Б, нужно травление (зоны 5-6) + if (etching_zone < 0) { + break; + } + if (!zone_is_busy((short)(ZONE_ETCHING_1 + etching_zone))) { + return (short)(ZONE_ETCHING_1 + etching_zone); + } + break; + + case ZONE_ETCHING_1: + case ZONE_ETCHING_2: + // травление, нужна промывка 2А + if (!zone_is_busy(ZONE_WASHING_2A)) { + return ZONE_WASHING_2A; + } + break; + + case ZONE_WASHING_2A: + // промывка 2А, нужна промывка 2Б + if (!zone_is_busy(ZONE_WASHING_2B)) { + return ZONE_WASHING_2B; + } + break; + + case ZONE_WASHING_2B: + // промывка 2Б, нужно цинкование (зоны 9-16) + if (galvanizing_zone < 0) { + break; + } + if (!zone_is_busy((short)(ZONE_GALVANIZING_1 + galvanizing_zone))) { + return (short)(ZONE_GALVANIZING_1 + galvanizing_zone); + } + break; + + case ZONE_GALVANIZING_1: + case ZONE_GALVANIZING_2: + case ZONE_GALVANIZING_3: + case ZONE_GALVANIZING_4: + case ZONE_GALVANIZING_5: + case ZONE_GALVANIZING_6: + case ZONE_GALVANIZING_7: + case ZONE_GALVANIZING_8: + // цинкование, требуется чтобы в зонах 17-22 было максимум 2 барабана (3 барабана для этой части линии - максимум) + if (!zone_is_busy(ZONE_WASHING_3A)) { + short count = 0; + // если зона 17 свободна, то диапазон начнется с 3Б + for (short i = ZONE_WASHING_3B; i <= (short)ZONE_UNLOAD; i++) { + if (zone_is_busy(i)) { + count++; + } + } + if (count < 3) { + return ZONE_WASHING_3A; + } + } + break; + + case ZONE_WASHING_3A: + // промывка 3А, перекладываем в промывку 3Б + if (!zone_is_busy(ZONE_WASHING_3B)) { + return ZONE_WASHING_3B; + } + break; + + case ZONE_WASHING_3B: + // это перед пассивацией, требует свободную промывку 4А и на всякий случай свободную пассивацию + if (!zone_is_busy(ZONE_PASSIVATION) && !zone_is_busy(ZONE_WASHING_4A)) { + return ZONE_PASSIVATION; + } + // это атомарная операция, по идее вносить барабан в пассивацию нельзя + break; + + case ZONE_PASSIVATION: + // процесс пассивации, нужна промывка 4A + // чисто теоретически сюда никогда не попадем, но если вдруг выстрелит пусть будет + if (!zone_is_busy(ZONE_WASHING_4A)) { + return ZONE_WASHING_4A; + } + break; + + case ZONE_WASHING_4A: + // промывка 4А, перекладываем в промывку 4Б + if (!zone_is_busy(ZONE_WASHING_4B)) { + return ZONE_WASHING_4B; + } + break; + + case ZONE_WASHING_4B: + // процесс пассивации, нужна промывка 4B (зона 21) (потому что сейчас я в 4A) + if (!zone_is_busy(ZONE_UNLOAD)) { + return ZONE_UNLOAD; + } + break; + + case ZONE_UNLOAD: + // последняя промывка, нужно разрешение на выгрузку + if (_scheduler_one_robot_mode && button_unload) { + // нужно промывку загрузку 0 + if (!zone_is_busy(ZONE_LOAD_1)) { + return ZONE_LOAD_1; + } + } + break; + } + return -1; +} + + +// выставляет приоритет операции (зависит только от зоны, в которой робот находится) +short get_operation_priority(short barrel_id) { + // сделать приоритет на барабан, который больше всего ждет + if (barrels[barrel_id].zone >= ZONE_GALVANIZING_1 && barrels[barrel_id].zone <= ZONE_GALVANIZING_8) { + // теперь надо выяснить, есть ли барабаны с большим временем ожидания + // тут возможны несколько случаев: + // 1) когда барабан один такой (больше в цинковании нет барабанов чтобы их изъять), + // 2) когда барабанов несколько, соответственно если есть барабан с наибольшим временем, то надо ему дать приоритет 1, а остальным 0, + + char is_not_one = 0; + char is_with_max_time = 1; + + for (short i = 0; i < BARRELS_COUNT; i++) { + if (i == barrel_id) { + continue; + } + if (barrels[i].flags.is_exist && barrels[i].zone >= ZONE_GALVANIZING_1 && barrels[i].zone <= ZONE_GALVANIZING_8) { + if (can_move(barrels + i) >= 0) { + is_not_one = 1; + // чем больше у барабана время ожидания тем меньше у него число + if (barrels[i].software_timer < barrels[barrel_id].software_timer) { + is_with_max_time = 0; + break; + } + } + } + } + + if (is_not_one == 0) { + return 1; + } + return is_with_max_time; + } + + + // теперь то же самое, только для травления (только тут задача проще потому что травления всего 2 зоны, и надо только один барабан если он есть) + if (barrels[barrel_id].zone >= ZONE_ETCHING_1 && barrels[barrel_id].zone <= ZONE_ETCHING_2) { + + // если в травлении барабан лежит больше 40 минут, ему присвоить высокий приоритет + if (barrels[barrel_id].software_timer < (-1 * 60 * 40)) { + return 2; + } + + for (short i = 0; i < BARRELS_COUNT; i++) { + if (i == barrel_id) { + continue; + } + + if (barrels[i].flags.is_exist && barrels[i].zone >= ZONE_ETCHING_1 && barrels[i].zone <= ZONE_ETCHING_2) { + if (can_move(barrels + i) >= 0) { + if (barrels[i].software_timer < barrels[barrel_id].software_timer) { + return 0; // у этого барабана больше время ожидания (число меньше), значит приоритет 0 + } else { + return 1; // поскольку у нашего барабана максимальное время ожидания + } + } + } + } + // барабан не найден, приоритет ему 1 + return 1; + } + + return 1; // 1 - нормальный приоритет +} + + +char remove_barrel_from_zone(short zone) { + for (short i = 0; i < 10; i++) { + if (barrels[i].flags.is_exist && barrels[i].flags.robot == 0 && barrels[i].zone == zone) { + barrels[i].flags.is_exist = 0; + return 1; + } + } + return 0; +} + + +#ifdef EMULATOR +void debug_print_robot_code(const struct robot_code* code, const short robot_id) { +// printf("INFO: code length is %d\n", cmd_index); + printf("Code for R%d, B%d:\n", robot_id, code->barrel_id); + for (int i = 0; i < 16; i++) { + const short cmd_arg = (short)(code->code[i] & (short)(~ROBOT_CMD_MASK)); + + printf("%5d 0x%04X", i, code->code[i] & 0xFFFF); + + if ((code->code[i] & ROBOT_CMD_MASK) == ROBOT_CMD_END_code) { + printf(" END\n"); + break; + } + + switch ((short)(code->code[i] & (short)ROBOT_CMD_MASK)) { + case ROBOT_CMD_MOVE_TO_ZONE_code: + printf(" move to zone %d (with barrel: %d)\n", cmd_arg & (~ROBOT_WITH_BARREL), (cmd_arg & ROBOT_WITH_BARREL) != 0); + break; + + case ROBOT_CMD_MOVE_OFF_code: + printf(" move to offset pos\n"); + break; + + case ROBOT_CMD_MOVE_ACCURATE_code: + printf(" move to accurate pos\n"); + break; + + case ROBOT_CMD_UP_code: + printf(" up (with barrel: %d)\n", (cmd_arg & ROBOT_WITH_BARREL) != 0); + break; + + // в эмуляторе не важно где я, поэтому тут обе команды вниз обрабатываются одинаково + case ROBOT_CMD_DOWN_code: + printf(" down (with barrel: %d)\n", (cmd_arg & ROBOT_WITH_BARREL) != 0); + break; + + case ROBOT_CMD_WAIT_code: + printf(" wait %d secs\n", cmd_arg); + break; + + case ROBOT_CMD_TMR_SET_code: + printf(" set barrel timer %d secs\n", cmd_arg); + break; + + case ROBOT_CMD_CORRECT_AXIS_code: + if (cmd_arg == ROBOT_AXIS_X) { + printf(" correct axis: X\n"); + } else if (cmd_arg == ROBOT_AXIS_Z) { + printf(" correct axis: Z\n"); + } else { + printf(" correct axis: INVALID (%d)\n", cmd_arg); + } + break; + + case ROBOT_CMD_INC_ZONE_code: + if (cmd_arg == ROBOT_ZONE_GAL) { + printf(" increment zone: galvanic\n"); + } else if (cmd_arg == ROBOT_ZONE_ETCH) { + printf(" increment zone: etching\n"); + } else { + printf(" increment zone: INVALID (0x%4X)\n", cmd_arg); + } + break; + + default: + printf(" UNKNOWN: 0x%04X\n", code->code[i] & 0xFFFF); + } + } +} +#endif + + +/* +=== ЦИКЛОГРАММА ПЕРЕТАСКИВАНИЯ БАРАБАНА === +// NOTE первой командой на любую транзакцию должна стоять команда опустить траверсу (в 22 зоне мы никогда не закончим, за нее не беспокоится) +* опустить траверсу +если зона изъятия != промывка 3б + если текущая зона != зона изъятия + если зона изъятия == 22 + * встать на смещенную + * поднять траверсу + * съебать в 21 зону + * встать на смещенную + * опустить траверсу не до конца + * съебать в 22 зону + иначе + * съебать в зону изъятия + * поднять траверсу с барабаном + если зона изъятия != 22 и зона изъятия != 1: + * ждать скапывания (зависит от зоны) + * ехать в зону назначения + если зона назначения == 22 + * опустить траверсу не до конца с барабаном + * съебать в 21 зону + если ЭНКОДЕРЫ СТАРЫЕ (по умолчанию) + * поднять траверсу + * опустить траверсу + иначе + * опустить траверсу с барабаном + если зона назначения != 0 + * установить время ожидания барабана (зависит от зоны) +иначе + если текущая зона != промывка 3б + * съебать в промывку 3б + * поднять траверсу с барабаном + * съебать в пассивацию + * опустить траверсу с барабаном + * поставить время ожидания барабана в <время пассивации> + * поднять траверсу с барабаном + * съебать в зону промывка 4а + * опустить траверсу с барабаном + * установить время ожидания барабана (для промывки 4а) + */ +void create_operation(struct robot_code *code, const short barrel_id, const short start_zone, const short dest_zone, + const short current_zone, const short robot_id) { + // создаем код транзакции, пока обычный + code->barrel_id = barrel_id; + short cmd_index = 0; + + // первым делом добавляем команду опустить траверсу + code->code[cmd_index++] = ROBOT_CMD_DOWN(); + + if (dest_zone == ZONE_PASSIVATION) { + // пассивация, тут все просто + if (current_zone != start_zone) { + code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE(ZONE_WASHING_3B); + } + + code->code[cmd_index++] = ROBOT_CMD_UP_WITH_BARREL(); + code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE_WITH_BARREL(ZONE_PASSIVATION); + code->code[cmd_index++] = ROBOT_CMD_DOWN_WITH_BARREL(); + + // NOTE таймер робота работает точнее чем таймер барабанов +// code->code[cmd_index++] = ROBOT_CMD_TMR_SET(barrels[barrel_id].time_passivation); + code->code[cmd_index++] = ROBOT_CMD_WAIT(barrels[barrel_id].time_passivation); + + code->code[cmd_index++] = ROBOT_CMD_UP_WITH_BARREL(); + code->code[cmd_index++] = ROBOT_CMD_WAIT(hla_time_digging); + code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE_WITH_BARREL(ZONE_WASHING_4A); + code->code[cmd_index++] = ROBOT_CMD_DOWN_WITH_BARREL(); + + code->code[cmd_index++] = ROBOT_CMD_TMR_SET(barrels[barrel_id].time_washing_4a); + } else { + // любой другой случай + if (current_zone != start_zone) { + if (start_zone != 22) { + code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE(start_zone); + } else { + code->code[cmd_index++] = ROBOT_CMD_MOVE_OFF(); + code->code[cmd_index++] = ROBOT_CMD_UP(); + code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE(22); + code->code[cmd_index++] = ROBOT_CMD_MOVE_OFF(); + code->code[cmd_index++] = ROBOT_CMD_DOWN(); + code->code[cmd_index++] = ROBOT_CMD_MOVE_ACCURATE(); + } + } + + code->code[cmd_index++] = ROBOT_CMD_UP_WITH_BARREL(); + + // теперь надо определиться с тем, сколько ждать скапывания + switch (start_zone) { + case ZONE_DEGREASING: + case ZONE_ETCHING_1: + case ZONE_ETCHING_2: + case ZONE_GALVANIZING_1: + case ZONE_GALVANIZING_2: + case ZONE_GALVANIZING_3: + case ZONE_GALVANIZING_4: + case ZONE_GALVANIZING_5: + case ZONE_GALVANIZING_6: + case ZONE_GALVANIZING_7: + case ZONE_GALVANIZING_8: + // время скапывания реактивов + code->code[cmd_index++] = ROBOT_CMD_WAIT(hla_time_reagent); + break; + + case ZONE_WASHING_1A: + case ZONE_WASHING_2A: + case ZONE_WASHING_3A: + case ZONE_WASHING_4A: + // время скапывания первого каскада промывок + code->code[cmd_index++] = ROBOT_CMD_WAIT(hla_time_washing_1); + break; + + case ZONE_WASHING_1B: + case ZONE_WASHING_2B: + case ZONE_WASHING_3B: + case ZONE_WASHING_4B: + // время скапывания второго каскада промывок + code->code[cmd_index++] = ROBOT_CMD_WAIT(hla_time_washing_2); + break; + } + + code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE_WITH_BARREL(dest_zone); + + // инкремент зоны (если травление или цинкование) + if (dest_zone == ZONE_ETCHING_1 || dest_zone == ZONE_ETCHING_2) { + code->code[cmd_index++] = ROBOT_CMD_INC_ZONE(ROBOT_ZONE_ETCH); + } else if (dest_zone >= ZONE_GALVANIZING_1 && dest_zone <= ZONE_GALVANIZING_8) { + code->code[cmd_index++] = ROBOT_CMD_INC_ZONE(ROBOT_ZONE_GAL); + } + + code->code[cmd_index++] = ROBOT_CMD_DOWN_WITH_BARREL(); + + if (dest_zone == 22) { + code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE(21); + // NOTE старая механика не позволяет просто опустить траверсу до конца, для новой изменить поведение + code->code[cmd_index++] = ROBOT_CMD_UP(); + code->code[cmd_index++] = ROBOT_CMD_DOWN(); + } else { + + if (dest_zone != 0) { + + // установка времени ожидания барабана + short tmp = -1; + switch (dest_zone) { + case ZONE_DEGREASING: + tmp = barrels[barrel_id].time_degreasing; + break; + + case ZONE_ETCHING_1: + case ZONE_ETCHING_2: + tmp = barrels[barrel_id].time_etching; + break; + + case ZONE_GALVANIZING_1: + case ZONE_GALVANIZING_2: + case ZONE_GALVANIZING_3: + case ZONE_GALVANIZING_4: + case ZONE_GALVANIZING_5: + case ZONE_GALVANIZING_6: + case ZONE_GALVANIZING_7: + case ZONE_GALVANIZING_8: + tmp = barrels[barrel_id].time_galvanizing; + break; + + case ZONE_WASHING_1A: + tmp = barrels[barrel_id].time_washing_1a; + break; + case ZONE_WASHING_2A: + tmp = barrels[barrel_id].time_washing_2a; + break; + case ZONE_WASHING_3A: + tmp = barrels[barrel_id].time_washing_3a; + break; + case ZONE_WASHING_4A: + tmp = barrels[barrel_id].time_washing_4a; + break; + + case ZONE_WASHING_1B: + tmp = barrels[barrel_id].time_washing_1b; + break; + case ZONE_WASHING_2B: + tmp = barrels[barrel_id].time_washing_2b; + break; + case ZONE_WASHING_3B: + tmp = barrels[barrel_id].time_washing_3b; + break; + case ZONE_WASHING_4B: + tmp = barrels[barrel_id].time_washing_4b; + break; + } + + if (tmp > 0) { + if (tmp > 8000) { + tmp = 8000; + } + code->code[cmd_index++] = ROBOT_CMD_TMR_SET(tmp); + } + } + } + } + + if (!_scheduler_one_robot_mode) { + if (robot_id == 1) { + if (dest_zone >= ZONE_GALVANIZING_1) { + // из промывки 2б он перекладывал, пусть едет в промывку 2а + code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE(ZONE_WASHING_2A); + } else if (dest_zone < ZONE_DEGREASING) { + code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE(ZONE_DEGREASING); + } + } else if (robot_id == 2) { + if (dest_zone <= ZONE_WASHING_3A) { + // чтобы из этой зоны можно было переложить барабан первому роботу + code->code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE(ZONE_WASHING_4A); + } + } + } + + code->code[cmd_index++] = ROBOT_CMD_END(); + code->PC = 0; + +#ifdef EMULATOR + printf("INFO: code length is %d\n", cmd_index); +#endif +} diff --git a/utils.h b/utils.h index 507e165..3c3fd85 100644 --- a/utils.h +++ b/utils.h @@ -318,6 +318,19 @@ extern short hla_time_washing_2; #define ROBOT_X_TARGET_ZONE_ADDR (VFD_REG_D0_ADDR + ROBOT_X_TARGET_ZONE_OFFSET) + +char zone_is_busy(short zone); +short can_move(struct barrel* bar); +short get_operation_priority(short barrel_id); +char remove_barrel_from_zone(short zone); + +void create_operation(struct robot_code *code, const short barrel_id, const short start_zone, const short dest_zone, + const short current_zone, const short robot_id); + +#ifdef EMULATOR +void debug_print_robot_code(const struct robot_code* code, const short robot_id); +#endif + #ifdef __cplusplus } #endif