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
2022-11-19 10:21:39 +03:00

369 lines
13 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;
}
// вернет можно ли ехать и главное куда ехать, если можно (нельзя если вернулось значение < 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;
// }
}