This repository has been archived on 2024-09-18. You can view files and clone it, but cannot push or open issues or pull requests.
sdp-scheduler/utils.c

551 lines
23 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// Created by Владислав Остапов on 13.12.2022.
//
#ifdef EMULATOR
#include <stdio.h>
#include "emulator.h"
#else
#include "main.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 свободна, то диапазон начнется с
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
}