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/scheduler.c

585 lines
24 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.

#include <stdio.h>
#include <stdlib.h>
#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;
}
// 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;
}
if (!schedulerOneRobotMode) {
printf("WARMING: нет проверки того, что для перемещения барабана не мешает второй робот\n");
}
// дальше нужно проверить, что можно передвигать бочку
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(ZONE_ETCHING_1 + etching_zone)) {
return 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(ZONE_GALVANIZING_1 + galvanizing_zone)) {
return 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 <= 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 (schedulerOneRobotMode) {
// нужно промывку загрузку 0
if (!zone_is_busy(17)) {
return 17;
}
}
break;
}
return -1;
}
struct scheduler_task {
short start_zone; // стартовая зона
short dest_zone; // конечная зона
short priority; // приоритет, чем больше тем выше, по умолчанию 0
};
// выставляет приоритет операции
short get_operation_priority(short barrel_id) {
// сделать приоритет на барабан, который больше всего ждет
// task->priority = 0;
// switch (barrel_process) {
// case PROCESS_RETURN_2:
// break;
// }
return 0;
}
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 = 12;
b.time_washing_1a = 4;
b.time_washing_1b = 6;
b.time_etching = 16;
b.time_washing_2a = 6;
b.time_washing_2b = 8;
b.time_galvanizing = 30;
b.time_washing_3a = 8;
b.time_washing_3b = 10;
b.time_passivation = 3;
b.time_washing_4a = 12;
b.time_washing_4b = 14;
return b;
}
static short scheduler_find_task(struct scheduler_task* tasks, const short curr_pos) {
// ищем первый барабан слева, и ближайший справа
short left = -1, right = -1;
for (short i = 0; i < BARRELS_COUNT; i++) {
short target = tasks[i].start_zone; // фактическая зона откуда тащить барабан
if (tasks[i].dest_zone == -2) {
target = 18;
} else if (tasks[i].dest_zone < 0) {
continue;
}
if (curr_pos <= target) {
// это таск справа, надо найти ближайший
if (right == -1) {
right = i;
} else {
if (barrels[right].zone > target) {
right = i;
}
}
} else {
// таск слева, ищем максимально дальний (с минимальной зоной)
if (left == -1) {
left = i;
} else {
if (barrels[left].zone > target) {
left = i;
}
}
}
}
// итого есть результат: есть ли таски, которые надо тащить вперед (и если надо то какой ближний), и есть первый таск
if (left < 0) {
return right; // вернем таск справа (если его нет, в переменной будет -1)
}
if (right < 0) {
return left; // если вдруг задачи справа не оказалось, вернем задачу слева если есть
}
// вычисляем что ближе
short ld = curr_pos - left; // левая дельта
short rd = right - curr_pos; // правая дельта
// дальше сравниваем дельты
// по идее если они равны то с бОльшим приоритетом робот поедет в левую часть
// а левую дельту вообще уменьшу на 1, чтобы цель слева казалась ближе
if (rd > ld - 1) {
return left;
} else {
return right;
}
}
void debug_print_robot1_code() {
// printf("INFO: code length is %d\n", cmd_index);
printf("Code for R0, B%d:\n", robot1_code.barrel_id);
for (int i = 0; i < 16; i++) {
const short cmd_arg = robot1_code.code[i] & (~ROBOT_CMD_MASK);
if ((robot1_code.code[i] & ROBOT_CMD_MASK) == ROBOT_CMD_END) {
printf(" END\n");
break;
}
switch (robot1_code.code[i] & ROBOT_CMD_MASK) {
case ROBOT_CMD_MOVE_TO_ZONE:
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:
printf(" move to offset pos\n");
break;
case ROBOT_CMD_UP:
printf(" up (with barrel: %d)\n", (cmd_arg & ROBOT_WITH_BARREL) != 0);
break;
// в эмуляторе не важно где я, поэтому тут обе команды вниз обрабатываются одинаково
case ROBOT_CMD_DOWN:
printf(" down (with barrel: %d)\n", (cmd_arg & ROBOT_WITH_BARREL) != 0);
break;
case ROBOT_CMD_DOWN_2:
printf(" down 2 (with barrel: %d)\n", (cmd_arg & ROBOT_WITH_BARREL) != 0);
break;
case ROBOT_CMD_WAIT:
printf(" wait %d secs\n", cmd_arg);
break;
case ROBOT_CMD_TMR_SET:
printf(" set barrel timer %d secs\n", cmd_arg);
i++;
break;
case ROBOT_CMD_SET_LOCK_ZONE:
printf(" set lock zone %d\n", cmd_arg);
break;
case ROBOT_CMD_CORRECT_AXIS:
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:
if (cmd_arg == ROBOT_ZONE_ETCH) {
printf(" increment: etching\n");
} else if (cmd_arg == ROBOT_ZONE_GAL) {
printf(" increment: galvanic\n");
} else {
printf(" increment: INVALID (%d)\n", cmd_arg);
}
break;
default:
printf(" UNKNOWN: 0x(%04X)\n", cmd_arg);
}
}
}
void schedule_robot_1() {
// формируем список задач
struct scheduler_task tasks[BARRELS_COUNT];
for (short i = 0; i < BARRELS_COUNT; i++) {
// для каждой задачи:
tasks[i].start_zone = barrels[i].zone;
// определяем можно ли ее выполнить и что вообще нужно выполнить
tasks[i].dest_zone = can_move(barrels + i);
}
// найти подходящую задачу
if (schedulerOneRobotMode) {
short target_task = scheduler_find_task(tasks, robot1.dx.current_zone);
if (target_task >= 0) {
// создаем код транзакции, пока обычный
robot1_code.barrel_id = target_task;
short cmd_index = 0;
// первым делом добавляем команду опустить траверсу
robot1_code.code[cmd_index++] = ROBOT_CMD_DOWN;
if (tasks[target_task].start_zone != ZONE_WASHING_3B) {
if (robot1.dx.current_zone != tasks[target_task].start_zone) {
if (tasks[target_task].start_zone == 22) {
robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_OFF;
robot1_code.code[cmd_index++] = ROBOT_CMD_UP;
robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE | 21;
robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_OFF;
robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE | 22;
} else {
robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE | tasks[target_task].start_zone;
}
}
robot1_code.code[cmd_index++] = ROBOT_CMD_UP_WITH_BARREL;
// теперь надо определиться с тем, сколько ждать скапывания
switch (tasks[target_task].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:
// время скапывания реактивов
// TODO добавить переменные времен скапывания
robot1_code.code[cmd_index++] = ROBOT_CMD_WAIT | 30;
break;
case ZONE_WASHING_1A:
case ZONE_WASHING_2A:
case ZONE_WASHING_3A:
case ZONE_WASHING_4A:
// время скапывания 1-го каскада промывок
robot1_code.code[cmd_index++] = ROBOT_CMD_WAIT | 3;
break;
case ZONE_WASHING_1B:
case ZONE_WASHING_2B:
case ZONE_WASHING_3B:
case ZONE_WASHING_4B:
// время скапывания 2-го каскада промывок
robot1_code.code[cmd_index++] = ROBOT_CMD_WAIT | 20;
break;
}
robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE_WITH_BARREL | tasks[target_task].dest_zone;
if (tasks[target_task].dest_zone == 22) {
robot1_code.code[cmd_index++] = ROBOT_CMD_DOWN_2_WITH_BARREL;
robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE | 21;
// NOTE старая механика не позволяет просто опустить траверсу до конца, для новой изменить поведение
robot1_code.code[cmd_index++] = ROBOT_CMD_UP;
robot1_code.code[cmd_index++] = ROBOT_CMD_DOWN;
} else {
robot1_code.code[cmd_index++] = ROBOT_CMD_DOWN_WITH_BARREL;
if (tasks[target_task].dest_zone != 0) {
// установка времени ожидания барабана
short tmp = -1;
switch (tasks[target_task].dest_zone) {
case ZONE_DEGREASING:
tmp = barrels[target_task].time_degreasing; break;
case ZONE_ETCHING_1:
case ZONE_ETCHING_2:
tmp = barrels[target_task].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[target_task].time_galvanizing; break;
case ZONE_WASHING_1A:
tmp = barrels[target_task].time_washing_1a; break;
case ZONE_WASHING_2A:
tmp = barrels[target_task].time_washing_2a; break;
case ZONE_WASHING_3A:
tmp = barrels[target_task].time_washing_3a; break;
case ZONE_WASHING_4A:
tmp = barrels[target_task].time_washing_4a; break;
case ZONE_WASHING_1B:
tmp = barrels[target_task].time_washing_1b; break;
case ZONE_WASHING_2B:
tmp = barrels[target_task].time_washing_2b; break;
case ZONE_WASHING_3B:
tmp = barrels[target_task].time_washing_3b; break;
case ZONE_WASHING_4B:
tmp = barrels[target_task].time_washing_4b; break;
}
if (tmp != -1) {
robot1_code.code[cmd_index++] = ROBOT_CMD_TMR_SET;
robot1_code.code[cmd_index++] = tmp;
}
}
}
} else {
// пассивация, тут все просто
if (robot1.dx.current_zone != tasks[target_task].start_zone) {
robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE | ZONE_WASHING_3B;
}
robot1_code.code[cmd_index++] = ROBOT_CMD_UP_WITH_BARREL;
robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE_WITH_BARREL | ZONE_PASSIVATION;
robot1_code.code[cmd_index++] = ROBOT_CMD_DOWN_WITH_BARREL;
robot1_code.code[cmd_index++] = ROBOT_CMD_TMR_SET;
robot1_code.code[cmd_index++] = barrels[target_task].time_passivation;
robot1_code.code[cmd_index++] = ROBOT_CMD_UP_WITH_BARREL;
robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE_WITH_BARREL | ZONE_WASHING_4A;
robot1_code.code[cmd_index++] = ROBOT_CMD_DOWN_WITH_BARREL;
robot1_code.code[cmd_index++] = ROBOT_CMD_TMR_SET;
robot1_code.code[cmd_index++] = barrels[target_task].time_washing_4a;
}
robot1_code.code[cmd_index++] = ROBOT_CMD_END;
robot1_code.PC = 0;
printf("INFO: code length is %d\n", cmd_index);
debug_print_robot1_code();
}
} else {
printf("WARMING: support only one robot mode\n");
}
}
// вернет 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);
return 0;
}
}
return 1;
}
/*
=== ЦИКЛОГРАММА ПЕРЕТАСКИВАНИЯ БАРАБАНА ===
// NOTE первой командой на любую транзакцию должна стоять команда опустить траверсу (в 22 зоне мы никогда не закончим, за нее не беспокоится)
* опустить траверсу
если зона изъятия != промывка 3б
если текущая зона != зона иъятия
если зона изъятия == 22
* встать на смещенную
* поднять траверсу
* съебать в 21 зону
* встать на смещенную
* опустить траверсу не до конца
* съебать в 22 зону
иначе
* съебать в зону изъятия
* поднять траверсу с барабаном
если зона изъятия != 22 и зона изъятия != 1:
* ждать скапывания (зависит от зоны)
* ехать в зону назначения
если зона назначения == 22
* опустить траверсу не до конца с барабаном
* съебать в 21 зону
если ЭНКОДЕРЫ СТАРЫЕ (по умолчанию)
* поднять траверсу
* опустить траверсу
иначе
* опустить траверсу с барабаном
если зона назначения != 0
* установить время ожидания барабана (зависит от зоны)
иначе
если текущая зона != промывка 3б
* съебать в промывку 3б
* поднять траверсу с барабаном
* съебать в пассивацию
* опустить траверсу с барабаном
* поставить время ожидания барабана в <время пассивации>
* поднять траверсу с барабаном
* съебать в зону промывка 4а
* опустить траверсу с барабаном
* установить время ожидания барабана (для промывки 4а)
*/
void scheduler_main() {
// тут должно быть удаление барабана из зоны 1, если он там есть
// 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_code.PC < 0) {
schedule_robot_1();
}
// пока без второго робота
// if (robot2_cmd.cmd == 0) {
// robot2_cmd.cmd = 2; // пиздуем в зону
// robot2_cmd.args[0] = rand() % 23;
// }
}