// // Created by Владислав Остапов on 27.10.2022. // #include #include #include #include #include "emulator.h" #include "robot.h" struct barrel barrels[BARRELS_COUNT]; struct robot_regs robot1; struct robot_regs robot2; char _scheduler_software_timer = 0; // кнопка загрузки в зоне 0, означает что барабан надо изъять из этой загрузки (а перед этим создать) char button_load = 0; char button_unload = 0; char button_unload_end = 0; char button_unload_remove = 0; // времена операций для барабанов, устанавливается индивидуально каждому барабану short hla_time_degreasing = 12; short hla_time_washing_1a = 4; short hla_time_washing_1b = 6; short hla_time_etching = 25; short hla_time_washing_2a = 6; short hla_time_washing_2b = 8; short hla_time_galvanizing = 60; short hla_time_washing_3a = 8; short hla_time_washing_3b = 10; short hla_time_passivation = 3; short hla_time_washing_4a = 12; short hla_time_washing_4b = 14; // времена скапывания, одинаковые для всех барабанов short hla_time_digging = 8; short hla_time_reagent = 30; short hla_time_washing_1 = 3; short hla_time_washing_2 = 20; char hla_robot1_en = 1; char hla_robot2_en = 1; char one_robot_mode = (char)(hla_robot1_en ^ hla_robot2_en); char scheduler_en = 1; char scheduler_start_signal = 1; char auto_mode_pause = 0; char hla_night_mode = 0; short etching_zone = 0, galvanizing_zone = 0; static const int ROWS = 10; static const int COLS = 23 * 5 + 1; static char buffer[ROWS][COLS]; static int current_tic = 0; robot_code robot1_code{0, -1}; robot_code robot2_code{0, -1}; short robot1_lock_zone = 0; short robot2_lock_zone = 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 " "W-3A W-3B PASS W-4A W-4B UNLD", true); // счетчик тиков sprintf(tmp, "tic: %d", current_tic); image_insert_sprite(0, (int)(COLS - strlen(tmp) - 2), tmp, false); } static int barrels_count = 0, barrels_time = 0, max_time = 0; static void collectBarrelsStatistic() { barrels_time = 0; barrels_count = 0; for (int i = 0; i < BARRELS_COUNT; i++) { if (barrels[i].flags.is_exist) { barrels_count++; if (barrels[i].zone <= ZONE_LOAD_2 || barrels[i].zone == ZONE_PASSIVATION || barrels[i].zone == ZONE_UNLOAD) { continue; } if (barrels[i].flags.robot == 0 && barrels[i].software_timer < 0) { // время отрицательное, вычитаем его чтобы убрать минус barrels_time -= barrels[i].software_timer; } } } if (max_time < barrels_time) { max_time = barrels_time; } } static void showAll() { // 10 строк, 10*(\sb%1d\s) = 40 // барабан хочу показывать так // // # Bx | Bx | Bx // #time|time|time // image_init(); image_draw_borders(); char tmp[64]; sprintf(tmp, "Lock1=%2d Lock2=%2d", robot1_lock_zone, robot2_lock_zone); image_insert_sprite(0, 2, tmp); collectBarrelsStatistic(); sprintf(tmp, "barrels=%2d time=%d max_time=%d", barrels_count, barrels_time, max_time); image_insert_sprite(0, 25, tmp); sprintf(tmp, "MODE: night=%d pause=%d", hla_night_mode, auto_mode_pause); image_insert_sprite(0, 70, tmp); // рисование бочек for (int i = 0; i < BARRELS_COUNT; i++) { const auto& b = barrels[i]; if (b.flags.is_exist) { char flag_r = ' '; if (b.flags.is_night) { flag_r = 'n'; } if (b.flags.is_empty) { flag_r = 'e'; } char flag_l = ' '; if (b.flags.robot) { flag_l = '*'; } if (b.flags.is_up) { sprintf(tmp, "%cB%d%c", flag_l, i, flag_r); image_insert_sprite(4, (b.zone * 5) + 1, tmp); } else { sprintf(tmp, "%cB%d%c\n%04d", flag_l, i, flag_r, b.software_timer); image_insert_sprite(5, (b.zone * 5) + 1, tmp); } } } // рисуем роботов if (hla_robot1_en) { sprintf(tmp, "R1"); image_insert_sprite(2 + (robot1.mz.is_up ? 0 : 2) - (robot1.mx.correct_sensor ? 1 : 0), (robot1.dx.current_zone * 5) + 2 + (robot1_offset_pos * 2) + (robot1.mx.correct_sensor ? 1 : 0), tmp); } if (hla_robot2_en) { sprintf(tmp, "R2"); image_insert_sprite(2 + (robot2.mz.is_up ? 0 : 2), (robot2.dx.current_zone * 5) + 2 + (robot2_offset_pos * 2) - (robot2.mx.correct_sensor ? 1 : 0), 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("Socket creation error\n"); exit(-1); } // setting socket options int flag = 1; if(setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)) == -1){ printf("setsockopt TCP_NODELAY failed for server socket on address 127.0.0.1\n"); } 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("Invalid 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); } } int main() { robot1.mx.correct_sensor = true; robot2.mx.correct_sensor = true; open_socket(); current_tic = 0; const char* message = nullptr; while (true) { _scheduler_software_timer = 1; // подсчитаем статистику collectBarrelsStatistic(); // грузим не больше 5 барабанов if (barrels_count < 5) { button_load = 1; } robot_main(); scheduler_main(); send_str("\033c"); showAll(); if (message) { std::cout << message << std::endl; send_str(message); send_str("\n"); } if (!one_robot_mode) { if (!robot2.mx.correct_sensor) { if (robot2.dx.current_zone + 1 >= robot1.dx.current_zone) { printf("ASSERTION FAILED: FOUND ROBOTS CORRUPTION\n"); if (robot1_code.PC >= 0) debug_print_robot_code(&robot1_code, 1, 0); if (robot2_code.PC >= 0) debug_print_robot_code(&robot2_code, 2, 0); break; } } } if (robot1_code.PC >= 0) debug_print_robot_code(&robot1_code, 1, sock_fd); if (robot2_code.PC >= 0) debug_print_robot_code(&robot2_code, 2, sock_fd); 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") { button_unload = 1; message = "Нажата кнопка выгрузки"; } else if (in == "1") { button_unload_end = 1; message = "Нажата кнопка загрузки 1"; } else if (in == "2") { button_load = 1; message = "Нажата кнопка загрузки 2"; } else if (in == "n") { hla_night_mode = !hla_night_mode; message = "Переключен ночной режим"; } else if (in == "p") { auto_mode_pause = !auto_mode_pause; message = "Переключен режим паузы"; } else { message = "Неизвестная команда. q - выход, u - выгрузка, 1 - загрузка 1, 2 - загрузка 2"; } } current_tic++; } fsync(sock_fd); close(sock_fd); return 0; }