commit 0dd1158b3d6b5bb80e4d74647d5c3a0863e4f952 Author: VladislavOstapov Date: Sat Nov 19 10:21:39 2022 +0300 Initial commit diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..aa3a8bb --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.21) +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) + +add_executable(remote_listener remote_listener.cpp) diff --git a/emulator.cpp b/emulator.cpp new file mode 100644 index 0000000..99c2726 --- /dev/null +++ b/emulator.cpp @@ -0,0 +1,243 @@ +// +// Created by Владислав Остапов on 27.10.2022. +// + +#include +#include +#include +#include "emulator.h" +#include "robot.h" + +struct barrel barrels[BARRELS_COUNT]; + +struct robot_cmd robot1_cmd; +struct robot_cmd robot2_cmd; + +struct robot robot1; +struct robot robot2; + +char schedulerSoftwareTimer = 0; +char schedulerUnloadButton = 0; +char schedulerLoadButton1 = 0; +char schedulerLoadButton2 = 0; +char schedulerOneRobotMode = 1; + +static const int ROWS = 10; +static const int COLS = 23 * 5 + 1; +static char buffer[ROWS][COLS]; +static int current_tic = 0; + +static int sock_fd; +static void send_str(const char* str) { + write(sock_fd, str, strlen(str)); +} + +static void image_init() { + // заполнение всего поля пробелами + memset(buffer, ' ', sizeof(buffer)); +} + +static void image_insert_sprite(int row, int col, const char* str, bool alpha = true) { + for (; row < ROWS; row++) { + for (int curr_col = col;; curr_col++) { + char src = *(str++); + + // конец строки, заканчиваем рисовать + if (src == '\0') { + return; + } + + // перевод строки, перевод на новую строку в этом спрайте + if (src == '\n') { + break; + } + + // чтобы не рисовать мусор + if (src < ' ') { + src = ' '; + } + + if (curr_col < COLS) { + // рисуем, остальные фрагменты будут отброшены + char frag = buffer[row][curr_col]; + + if (alpha) { + // этот режим позволяет пропускать изменение символа, если исходный символ пробел + if (src != ' ') { + frag = src; + } + } else { + frag = src; + } + + buffer[row][curr_col] = frag; + } + } + } +} + +static void image_draw_borders() { + // рамки ванн +// for (int i = 0; i < COLS; i += 5) { +// image_insert_sprite(5, i, "|\n|\n|"); +// } + + // рисование линий + for (int i = 0; i < ROWS - 1; i++) { + buffer[i][0] = '|'; + buffer[i][COLS - 1] = '|'; + } + memset(buffer[1], '=', sizeof(buffer[0])); + + // рамки ванн + char tmp[24]; + for (int i = 0, zone = 0; i < COLS; i += 5, zone++) { + sprintf(tmp, "|\n|\n+----+\n|\n|Z-%02d|", zone); + image_insert_sprite(5, i, tmp, false); + } + + image_insert_sprite(8, 1, "LOAD LOAD DEFA W-1A W-1B ETCH ETCH W-2A W-2B " + " GAL GAL GAL GAL GAL GAL GAL GAL " + "W3-A W3-B PASS W4-A W4-B UNLD", true); + + // счетчик тиков + sprintf(tmp, "tic: %d", current_tic); + image_insert_sprite(0, (int)(COLS - strlen(tmp) - 2), tmp, false); +} + +static void showAll() { + // 10 строк, 10*(\sb%1d\s) = 40 + // барабан хочу показывать так + // + // # Bx | Bx | Bx + // #time|time|time + // + + image_init(); + image_draw_borders(); + char tmp[16]; + + // рисование бочек + for (int i = 0; i < BARRELS_COUNT; i++) { + const auto& b = barrels[i]; + if (b.flags.is_exist) { + if (b.flags.is_up) { + sprintf(tmp, " B%d", i); + image_insert_sprite(4, (b.zone * 5) + 1, tmp); + } else { + sprintf(tmp, " B%d\n%04d", i, b.software_timer); + image_insert_sprite(5, (b.zone * 5) + 1, tmp); + } + + } + } + + // рисуем роботов + sprintf(tmp, "R1"); +// image_insert_sprite(2 + (robot1.mz.is_up ? 0 : 2), (robot1.curr_zone * 5) + 2, tmp); + image_insert_sprite(2 + (robot1.mz.is_up ? 0 : 2), (robot1.curr_zone * 5) + 2, tmp); + +// sprintf(tmp, "R2"); +// image_insert_sprite(3 + (robot2.mz.is_up ? 0 : 2), (robot2.curr_zone * 5) + 2, tmp); + + for (const auto & r : buffer) { + write(sock_fd, r, COLS); + send_str("\n"); + } +} + +static void open_socket() { + sockaddr_in serv_addr{}; + + sock_fd = socket(AF_INET, SOCK_STREAM, 0); + if (sock_fd < 0) { + printf("\n Socket creation error \n"); + exit(-1); + } + + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(40000); + + // Convert IPv4 and IPv6 addresses from text to binary form + if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { + printf("\nInvalid address/ Address not supported \n"); + exit(-1); + } + + if (connect(sock_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { + printf("Connection Failed \n"); + exit(-1); + } +} + +extern "C" struct barrel makeBarrel(short flags, short zone, short timer, short process); + +int main() { + open_socket(); + +// for (auto & b : barrels) { +// b.flags.is_exist = 1; +// b.software_timer = (short)(random() % 50); +// b.zone = (short) abs(random() % 20); +// } + + barrels[5] = makeBarrel(1, 10, 3, PROCESS_WASHING_3A); + + current_tic = 0; + const char* message = nullptr; + while (true) { + schedulerSoftwareTimer = 1; + robot_main(); + scheduler_main(); + send_str("\033c"); + showAll(); + if (message) { + std::cout << message << std::endl; + send_str(message); + send_str("\n"); + message = nullptr; + } + + send_str("cmd >> "); + std::string in; + while (true) { + char tmp[2] = {0, 0}; + ssize_t res = read(sock_fd, tmp, 1); + if (res < 0) { + exit(-1); + } + if (res == 1) { + if (tmp[0] == '\n') { + break; + } + in.append(tmp); + } + } + + if (in == "q") { + break; + } + + if (in.empty()) { + // просто продолжаем циклы + message = "Continue..."; + } else { + if (in == "u") { + schedulerUnloadButton = 1; + message = "Нажата кнопка выгрузки"; + } else if (in == "1") { + schedulerLoadButton1 = 1; + message = "Нажата кнопка загрузки 1"; + } else if (in == "2") { + schedulerLoadButton2 = 1; + message = "Нажата кнопка загрузки 2"; + } else { + message = "Неизвестная команда. q - выход, u - выгрузка, 1 - загрузка 1, 2 - загрузка 2"; + } + } + current_tic++; + } + + close(sock_fd); + return 0; +} \ No newline at end of file diff --git a/emulator.h b/emulator.h new file mode 100644 index 0000000..bb59199 --- /dev/null +++ b/emulator.h @@ -0,0 +1,105 @@ +// +// Created by Владислав Остапов on 27.10.2022. +// + +#ifndef SDP_SHEDULER_EMULATOR_H +#define SDP_SHEDULER_EMULATOR_H + +#include + +#define BARRELS_COUNT 10 + +union flags { + int16_t raw_word; + struct { + char is_exist: 1; + char is_up: 1; // для панели + char robot: 2; // обслуживающий робот + int _unused: 11; + }; +}; + +struct barrel { + union flags flags; + short zone; + short software_timer; + short curr_process; // стадия процесса + + // время процессов + // TODO сделать это как union, чтобы можно было юзать как таблицу + short time_defatting; // Время обезжиривания + short time_washing_1a; // Время промывки 1А + short time_washing_1b; // Время промывки 1Б + short time_etching; // Время травления + short time_washing_2a; // Время промывки 2А + short time_washing_2b; // Время промывки 2Б + short time_galvanizing; // Время цинкования + short time_washing_3a; // Время промывки 3А + short time_washing_3b; // Время промывки 3Б + short time_passivation; // Время пассивации + short time_washing_4a; // Время промывки 4А + short time_washing_4b; // Время промывки 4Б +}; + +enum BarrelProcess { + PROCESS_NONE = 0, // сразу после загрузки + PROCESS_DEFATTING, // обезжиривание + PROCESS_WASHING_1A, // промывка 1А + PROCESS_WASHING_1B, // промывка 1Б + PROCESS_ETCHING, // травление + PROCESS_WASHING_2A, // промывка 2А + PROCESS_WASHING_2B, // промывка 2Б + PROCESS_GALVANIZING, // цинкование + PROCESS_WASHING_3A, // промывка 3А + PROCESS_WASHING_3B, // промывка 3Б + PROCESS_PASSIVATION, // пассивация + PROCESS_WASHING_4B, // промывка 4Б + PROCESS_RETURN_1, // возвращение обратно, стадия 1 - перекладываем в свободную промывку (если есть) + PROCESS_RETURN_2, // возвращение обратно, стадия 2 - перекладываем в свободную загрузку и удалить барабан +}; + +struct robot_cmd { + short cmd; + short args[4]; + short step; +}; + + +union robot_flags_mz { + short raw_word; + struct { + char is_corrected: 1; + char is_up: 1; + }; +}; + +struct robot { + union robot_flags_mz mz; + short curr_zone; // пока будет достаточно этого +}; + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct barrel barrels[BARRELS_COUNT]; + +extern struct robot_cmd robot1_cmd; +extern struct robot_cmd robot2_cmd; + +extern struct robot robot1; +extern struct robot robot2; + +extern char schedulerSoftwareTimer; +extern char schedulerUnloadButton; +extern char schedulerLoadButton1; +extern char schedulerLoadButton2; +extern char schedulerOneRobotMode; + +void scheduler_main(); + +#ifdef __cplusplus +} +#endif + +#endif //SDP_SHEDULER_EMULATOR_H diff --git a/remote_listener.cpp b/remote_listener.cpp new file mode 100644 index 0000000..a0642ee --- /dev/null +++ b/remote_listener.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include +#include +#include +#define PORT 40000 + +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"); + exit(EXIT_FAILURE); + } + +// // Forcefully attaching socket to the port 8080 +// if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { +// perror("setsockopt"); +// exit(EXIT_FAILURE); +// } + + sock_address.sin_family = AF_INET; + sock_address.sin_addr.s_addr = INADDR_ANY; + sock_address.sin_port = htons(PORT); + + // Forcefully attaching socket to the port + if (bind(server_fd, (sockaddr*)&sock_address, sizeof(sock_address)) < 0) { + perror("bind failed"); + exit(EXIT_FAILURE); + } + if (listen(server_fd, 3) < 0) { + perror("listen"); + exit(EXIT_FAILURE); + } +} + +int openSocket() { + int sock; + int addrlen = sizeof(sock_address); + + if ((sock = accept(server_fd, (sockaddr*)&sock_address, (socklen_t*)&addrlen)) < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + + return sock; +} + +static struct pollfd fdList[2]; + +int main() { + createServer(); + + fdList[0].fd = STDIN_FILENO; + fdList[0].events = POLLIN; + + while (true) { + int sock = openSocket(); + printf("Connected fd=%d\n", sock); + fdList[1].fd = sock; + fdList[1].events = POLLIN; + + while (true) { + if (poll(fdList, 2, -1) < 1) { + continue; + } + + if (fdList[1].revents & POLLNVAL) { + printf("POLLNVAL\n"); + break; + } + + if (fdList[1].revents & POLLHUP) { + printf("closed by peer\n"); + close(fdList[1].fd); + break; + } + + if (fdList[1].revents & POLLIN) { + char buff[1024]; + ssize_t size = read(fdList[1].fd, buff, sizeof(buff)); + if (size < 0) { + printf("closed by peer\n"); + break; + } + + write(STDOUT_FILENO, buff, size); + } + + // получены данные из STDIN + if (fdList[0].revents & POLLIN) { + char buff[1024]; + ssize_t size = read(fdList[0].fd, buff, sizeof(buff)); + if (size < 0) { + printf("stdin EOL\n"); + return 0; + } + + write(fdList[1].fd, buff, size); + } + } + + // closing the connected socket + close(sock); + // closing the listening socket + shutdown(server_fd, SHUT_RDWR); + } +} + diff --git a/robot.cpp b/robot.cpp new file mode 100644 index 0000000..4bb29b5 --- /dev/null +++ b/robot.cpp @@ -0,0 +1,182 @@ +// +// Created by Владислав Остапов on 05.11.2022. +// + +#include "robot.h" + +static short get_barrel(char robot_id) { + for (short i = 0; i < BARRELS_COUNT; i++) { + if (barrels[i].flags.robot == robot_id) { + return i; + } + } + return -1; +} + +// true означает что движение закончено +static bool robot_move(robot& r, int target, char robot_id) { + if (r.curr_zone == target) { + return true; + } + + if (r.curr_zone < target) { + r.curr_zone++; + } else { + r.curr_zone--; + } + return false; +} + +static void emulate_robot(robot_cmd& cmd, robot& r, char robot_id) { + auto barrel = get_barrel(robot_id); + switch (cmd.cmd) { + case 1: + // команда просто уехать + switch (cmd.step) { + case 0: + // двигаемся в сторону цели + if (robot_move(r, cmd.args[0], robot_id)) { + cmd.step++; + } + break; + + default: + cmd.cmd = 0; + cmd.step = 0; + } + break; + + case 2: + // команда взять барабан и увезти его куда положено + switch (cmd.step) { + case 0: + // двигаемся в сторону барабана + if (robot_move(r, cmd.args[0], robot_id)) { + cmd.step++; + } + break; + + case 1: + // поднимаем траверсу + r.mz.is_up = 1; + if (barrel != -1) { + barrels[barrel].flags.is_up = 1; + } + cmd.step++; + break; + + case 2: + // двигаемся в сторону выгрузки + if (robot_move(r, cmd.args[1], robot_id)) { + cmd.step++; + } + break; + + case 3: + // опускаем траверсу + r.mz.is_up = 0; + if (barrel != -1) { + barrels[barrel].flags.is_up = 0; + } + // ну и тут же конец + + default: + cmd.cmd = 0; + cmd.step = 0; + } + break; + + case 3: + // команда пассивация + switch (cmd.step) { + case 0: + // двигаемся в 18 зону + if (robot_move(r, 18, robot_id)) { + cmd.step++; + } + break; + + case 1: + // поднимаем траверсу + r.mz.is_up = 1; + if (barrel != -1) { + barrels[barrel].flags.is_up = 1; + } + cmd.step++; + break; + + case 2: + // двигаемся в 19 зону + if (robot_move(r, 19, robot_id)) { + cmd.step++; + } + break; + + case 3: + // опускаем траверсу + r.mz.is_up = 0; + if (barrel != -1) { + barrels[barrel].flags.is_up = 0; + barrels[barrel].software_timer = barrels[barrel].time_passivation; + } + cmd.step++; + break; + + case 4: + // ждем пока барабан отстоит таймер в пассивации + if (barrel != -1) { + if (barrels[barrel].software_timer <= 0) { + cmd.step++; + } + } + break; + + case 5: + // поднимаем траверсу + r.mz.is_up = 1; + if (barrel != -1) { + barrels[barrel].flags.is_up = 1; + } + cmd.step++; + break; + + case 6: + // двигаемся в 20 зону + if (robot_move(r, 20, robot_id)) { + cmd.step++; + } + break; + + case 7: + // опускаем траверсу + r.mz.is_up = 0; + if (barrel != -1) { + barrels[barrel].flags.is_up = 0; + // и тут же ставим время промывки барабана + barrels[barrel].software_timer = barrels[barrel].time_washing_4a; + } + // ну и тут же конец + + default: + cmd.cmd = 0; + cmd.step = 0; + } + break; + + + default: + cmd.cmd = 0; + cmd.step = 0; + } + + if (r.mz.is_up) { + if (barrel != -1) { + barrels[barrel].zone = r.curr_zone; + } + } +} + +void robot_main() { + emulate_robot(robot1_cmd, robot1, 1); + emulate_robot(robot2_cmd, robot2, 2); +} diff --git a/robot.h b/robot.h new file mode 100644 index 0000000..cef7b50 --- /dev/null +++ b/robot.h @@ -0,0 +1,12 @@ +// +// Created by Владислав Остапов on 05.11.2022. +// + +#ifndef SDP_SHEDULER_ROBOT_H +#define SDP_SHEDULER_ROBOT_H + +#include "emulator.h" + +void robot_main(); + +#endif //SDP_SHEDULER_ROBOT_H diff --git a/scheduler.c b/scheduler.c new file mode 100644 index 0000000..0785c5b --- /dev/null +++ b/scheduler.c @@ -0,0 +1,368 @@ +#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; +// } +}