Подготовка к переписыванию планировщика: добавлены переменные на ПЛК, добавлена система команд для роботов

This commit is contained in:
VladislavOstapov 2022-12-02 22:29:36 +03:00
parent aa8d949323
commit 8f19d1b277
8 changed files with 461 additions and 120 deletions

View File

@ -4,6 +4,6 @@ project(sdp_sheduler C CXX)
set(CMAKE_C_STANDARD 17) set(CMAKE_C_STANDARD 17)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
add_executable(sdp_sheduler scheduler.c emulator.cpp emulator.h robot.cpp robot.h) add_executable(sdp_sheduler scheduler.c emulator.cpp emulator.h robot.cpp robot.h utils.c utils.h)
add_executable(remote_listener remote_listener.cpp) add_executable(remote_listener remote_listener.cpp)

View File

@ -8,13 +8,8 @@
#include "emulator.h" #include "emulator.h"
#include "robot.h" #include "robot.h"
struct barrel barrels[BARRELS_COUNT]; struct robot_regs robot1;
struct robot_regs robot2;
struct robot_cmd robot1_cmd;
struct robot_cmd robot2_cmd;
struct robot robot1;
struct robot robot2;
char schedulerSoftwareTimer = 0; char schedulerSoftwareTimer = 0;
char schedulerUnloadButton = 0; char schedulerUnloadButton = 0;
@ -27,6 +22,13 @@ static const int COLS = 23 * 5 + 1;
static char buffer[ROWS][COLS]; static char buffer[ROWS][COLS];
static int current_tic = 0; 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 int sock_fd;
static void send_str(const char* str) { static void send_str(const char* str) {
write(sock_fd, str, strlen(str)); write(sock_fd, str, strlen(str));
@ -135,7 +137,9 @@ static void showAll() {
// рисуем роботов // рисуем роботов
sprintf(tmp, "R1"); 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);
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.dx.current_zone * 5) + 2 + (robot1_offset_pos * 2),
tmp);
// sprintf(tmp, "R2"); // sprintf(tmp, "R2");
// image_insert_sprite(3 + (robot2.mz.is_up ? 0 : 2), (robot2.curr_zone * 5) + 2, tmp); // image_insert_sprite(3 + (robot2.mz.is_up ? 0 : 2), (robot2.curr_zone * 5) + 2, tmp);
@ -176,7 +180,7 @@ int main() {
open_socket(); open_socket();
// for (auto & b : barrels) { // for (auto & b : barrels) {
// b.flags.is_exist = 1; // b.barrel_flags.is_exist = 1;
// b.software_timer = (short)(random() % 50); // b.software_timer = (short)(random() % 50);
// b.zone = (short) abs(random() % 20); // b.zone = (short) abs(random() % 20);
// } // }

View File

@ -1,94 +1,14 @@
//
// Created by Владислав Остапов on 27.10.2022.
//
#ifndef SDP_SHEDULER_EMULATOR_H #ifndef SDP_SHEDULER_EMULATOR_H
#define SDP_SHEDULER_EMULATOR_H #define SDP_SHEDULER_EMULATOR_H
#include <stdint.h> #include "utils.h"
#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 #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
extern struct barrel barrels[BARRELS_COUNT]; extern struct robot_regs robot1;
extern struct robot_regs robot2;
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 schedulerSoftwareTimer;
extern char schedulerUnloadButton; extern char schedulerUnloadButton;
@ -98,6 +18,46 @@ extern char schedulerOneRobotMode;
void scheduler_main(); void scheduler_main();
// Флаги, которые есть в оригинальной программе на ПЛК, в "C global variables"
// Кнопки с панели
extern char hla_auto_mode;
extern char hla_night_mode;
extern char hla_pause;
extern char hla_correct_command;
// кнопки управления роботом с панели
// кнопка загрузки в зоне 0, означает что барабан надо изъять из этой загрузки (а перед этим создать)
extern char button_load;
// разрешающий сигнал подавать не буду, он не нужен поскольку планировщик нужен всегда
// кнопка выгрузки, означает что барабан нужно вернуть обратно
extern char button_unload;
// кнопка загрузки в зоне 1, означает что барабан в этой зоне подлежит удалению
extern char button_unload_end;
// кнопка загрузки в зоне 22, означает что барабан в этой зоне подлежит удалению
extern char button_unload_remove;
// Переменные, которые надо добавить в C Global variables
// код для роботов
extern robot_code robot1_code;
extern robot_code robot2_code;
// lock-зоны, нельзя двигаться за них и за робота
extern short robot1_lock_zone;
extern short robot2_lock_zone;
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

111
robot.cpp
View File

@ -3,6 +3,11 @@
// //
#include "robot.h" #include "robot.h"
#include <iostream>
bool robot1_offset_pos = false;
bool robot2_offset_pos = false;
static short get_barrel(char robot_id) { static short get_barrel(char robot_id) {
for (short i = 0; i < BARRELS_COUNT; i++) { for (short i = 0; i < BARRELS_COUNT; i++) {
@ -14,20 +19,27 @@ static short get_barrel(char robot_id) {
} }
// true означает что движение закончено // true означает что движение закончено
static bool robot_move(robot& r, int target, char robot_id) { static bool robot_move(robot_regs& r, int target, char robot_id) {
if (r.curr_zone == target) { // после перемещения мы явно в точной позиции
if (robot_id == 1) {
robot1_offset_pos = false;
} else {
robot2_offset_pos = false;
}
if (r.dx.current_zone == target) {
return true; return true;
} }
if (r.curr_zone < target) { if (r.dx.current_zone < target) {
r.curr_zone++; r.dx.current_zone++;
} else { } else {
r.curr_zone--; r.dx.current_zone--;
} }
return false; return false;
} }
static void emulate_robot(robot_cmd& cmd, robot& r, char robot_id) { static void emulate_robot(robot_cmd& cmd, robot_regs& r, char robot_id) {
auto barrel = get_barrel(robot_id); auto barrel = get_barrel(robot_id);
switch (cmd.cmd) { switch (cmd.cmd) {
case 1: case 1:
@ -171,11 +183,96 @@ static void emulate_robot(robot_cmd& cmd, robot& r, char robot_id) {
if (r.mz.is_up) { if (r.mz.is_up) {
if (barrel != -1) { if (barrel != -1) {
barrels[barrel].zone = r.curr_zone; barrels[barrel].zone = r.dx.current_zone;
} }
} }
} }
static void emulate_robot_v2(robot_code* code, robot_regs& r, char robot_id) {
if (code->PC < 0) {
return;
}
const short cmd_arg = code->code[code->PC] & (~ROBOT_CMD_MASK);
switch (code->code[code->PC] & ROBOT_CMD_MASK) {
case ROBOT_CMD_MOVE_TO_ZONE:
// двигаемся в сторону цели
if (robot_move(r, cmd_arg, robot_id)) {
code->PC++;
}
break;
case ROBOT_CMD_MOVE_OFF:
if (robot_id == 1) {
robot1_offset_pos = true;
} else {
robot2_offset_pos = true;
}
code->PC++;
break;
case ROBOT_CMD_UP:
if (code->barrel_id >= 0) {
barrels[code->barrel_id].flags.is_up = true;
r.mz.is_up = 1;
}
code->PC++;
break;
// в эмуляторе не важно где я, поэтому тут обе команды вниз обрабатываются одинаково
case ROBOT_CMD_DOWN:
case ROBOT_CMD_DOWN_2:
if (code->barrel_id >= 0) {
barrels[code->barrel_id].flags.is_up = false;
r.mz.is_up = 0;
}
code->PC++;
break;
case ROBOT_CMD_WAIT:
std::cout << "robot " << robot_id << " wait " << cmd_arg << " secs..." << std::endl;
code->PC++;
break;
case ROBOT_CMD_TMR_SET:
if (code->barrel_id >= 0) {
barrels[code->barrel_id].software_timer = code->code[code->PC + 1];
r.mz.is_up = 0;
}
code->PC += 2;
break;
case ROBOT_CMD_SET_LOCK_ZONE:
if (robot_id == 1) {
robot1_lock_zone = cmd_arg;
} else {
robot2_lock_zone = cmd_arg;
}
code->PC += 2;
break;
case ROBOT_CMD_CORRECT_X:
std::cout << "robot " << robot_id << " correct axis X..." << std::endl;
r.dz.current_zone = 0;
r.mx.correct_status = true;
code->PC++;
break;
case ROBOT_CMD_CORRECT_Z:
std::cout << "robot " << robot_id << " correct axis Z..." << std::endl;
r.mz.is_up = true;
r.mz.correct_status = true;
code->PC++;
break;
case ROBOT_CMD_END:
default:
code->PC = -1;
}
}
void robot_main() { void robot_main() {
emulate_robot(robot1_cmd, robot1, 1); emulate_robot(robot1_cmd, robot1, 1);
emulate_robot(robot2_cmd, robot2, 2); emulate_robot(robot2_cmd, robot2, 2);

View File

@ -7,6 +7,9 @@
#include "emulator.h" #include "emulator.h"
extern bool robot1_offset_pos;
extern bool robot2_offset_pos;
void robot_main(); void robot_main();
#endif //SDP_SHEDULER_ROBOT_H #endif //SDP_SHEDULER_ROBOT_H

View File

@ -22,8 +22,9 @@ static short get_robot_barrel(char robot_id) {
return -1; return -1;
} }
// TODO обновить метод
// вернет можно ли ехать и главное куда ехать, если можно (нельзя если вернулось значение < 0) // вернет можно ли ехать и главное куда ехать, если можно (нельзя если вернулось значение < 0)
// -1 вренет что перемещать нельзя // -1 вернет что перемещать нельзя
// -2 вернет если требуется атомарная операция пассивации // -2 вернет если требуется атомарная операция пассивации
short can_move(struct barrel* bar) { short can_move(struct barrel* bar) {
// сразу отсекаем варианты, при которых невозможно переместить барабан // сразу отсекаем варианты, при которых невозможно переместить барабан
@ -90,7 +91,6 @@ short can_move(struct barrel* bar) {
case PROCESS_WASHING_2B: case PROCESS_WASHING_2B:
// промывка 2Б, нужно цинкование (зоны 9-16) // промывка 2Б, нужно цинкование (зоны 9-16)
// TODO сделать приоритет на барабан, который больше всего ждет
if (!zone_is_busy(9 + galvanizing_zone)) { if (!zone_is_busy(9 + galvanizing_zone)) {
return 9 + galvanizing_zone; return 9 + galvanizing_zone;
} }
@ -164,34 +164,48 @@ short can_move(struct barrel* bar) {
} }
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, short process) { struct barrel makeBarrel(short flags, short zone, short timer, short process) {
struct barrel b; struct barrel b;
b.flags.raw_word = flags; b.flags.raw_word = flags;
b.zone = zone; b.zone = zone;
b.software_timer = timer; b.software_timer = timer;
b.curr_process = process; b.curr_process = process;
b.time_defatting = 6; b.time_defatting = 12;
b.time_washing_1a = 2; b.time_washing_1a = 4;
b.time_washing_1b = 3; b.time_washing_1b = 6;
b.time_etching = 8; b.time_etching = 16;
b.time_washing_2a = 3; b.time_washing_2a = 6;
b.time_washing_2b = 4; b.time_washing_2b = 8;
b.time_galvanizing = 15; b.time_galvanizing = 30;
b.time_washing_3a = 4; b.time_washing_3a = 8;
b.time_washing_3b = 5; b.time_washing_3b = 10;
b.time_passivation = 3; b.time_passivation = 3;
b.time_washing_4a = 6; b.time_washing_4a = 12;
b.time_washing_4b = 7; b.time_washing_4b = 14;
return b; return b;
} }
struct scheduler_task {
short start_zone; // стартовая зона
short dest_zone; // конечная зона
};
static short scheduler_find_task(struct scheduler_task* tasks, const short curr_pos) { static short scheduler_find_task(struct scheduler_task* tasks, const short curr_pos) {
// ищем первый барабан слева, и ближайший справа // ищем первый барабан слева, и ближайший справа
short left = -1, right = -1; short left = -1, right = -1;
@ -264,7 +278,7 @@ void schedule_robot_1() {
// найти подходящую задачу // найти подходящую задачу
if (schedulerOneRobotMode) { if (schedulerOneRobotMode) {
short target_task = scheduler_find_task(tasks, robot1.curr_zone); short target_task = scheduler_find_task(tasks, robot1.dx.current_zone);
if (target_task >= 0) { if (target_task >= 0) {
// создаем транзакцию // создаем транзакцию
@ -390,6 +404,48 @@ char create_barrel_in_load(short zone) {
return 1; return 1;
} }
/*
=== ЦИКЛОГРАММА ПЕРЕТАСКИВАНИЯ БАРАБАНА ===
// NOTE первой командой на любую транзакцию должна стоять команда опустить траверсу (в 22 зоне мы никогда не закончим, за нее не беспокоится)
* опустить траверсу
если зона изъятия != промывка 3б
если текущая зона != зона иъятия
если зона изъятия == 22
* встать на смещенную
* поднять траверсу
* съебать в 21 зону
* встать на смещенную
* опустить траверсу не до конца
* съебать в 22 зону
иначе
* съебать в зону изъятия
* поднять траверсу
если зона изъятия != 22 и зона изъятия != 0:
* ждать скапывания (зависит от зоны)
* ехать в зону назначения
если зона назначения == 22
* опустить траверсу не до конца
* съебать в 21 зону
если ЭНКОДЕРЫ СТАРЫЕ (по умолчанию)
* поднять траверсу
* опустить траверсу
иначе
* опустить траверсу
если зона назначения != 1
* установить время ожидания барабана (зависит от зоны)
иначе
если текущая зона != промывка 3б
* съебать в промывку 3б
* поднять траверсу
* съебать в пассивацию
* опустить траверсу
* поставить время ожидания барабана в <время пассивации>
* поднять траверсу
* съебать в зону промывка 4а
* опустить траверсу
* установить время ожидания барабана (для промывки 4а)
*/
void scheduler_main() { void scheduler_main() {
// тут должно быть удаление барабана из зоны 1, если он там есть // тут должно быть удаление барабана из зоны 1, если он там есть
// if (schedulerLoadButton1) { // if (schedulerLoadButton1) {

6
utils.c Normal file
View File

@ -0,0 +1,6 @@
#include "utils.h"
struct barrel barrels[BARRELS_COUNT];
struct robot_cmd robot1_cmd;
struct robot_cmd robot2_cmd;

215
utils.h Normal file
View File

@ -0,0 +1,215 @@
#ifndef SDP_SCHEDULER_UTILS_H
#define SDP_SCHEDULER_UTILS_H
// Все зоны линии
enum Zones {
ZONE_LOAD_1 = 0, // загрузка 0, используется как точка появления барабанов
ZONE_LOAD_2, // загрузка 1, используется как конечная точка барабанов
ZONE_DEGREASING, // обезжиривание
ZONE_WASHING_1A, // промывка 1А
ZONE_WASHING_1B, // промывка 1Б
ZONE_ETCHING_1, // травление 1
ZONE_ETCHING_2, // травление 2
ZONE_WASHING_2A, // промывка 2А
ZONE_WASHING_2B, // промывка 2Б
ZONE_GALVANIZING_1, // цинкование 1
ZONE_GALVANIZING_2, // цинкование 2
ZONE_GALVANIZING_3, // цинкование 3
ZONE_GALVANIZING_4, // цинкование 4
ZONE_GALVANIZING_5, // цинкование 5
ZONE_GALVANIZING_6, // цинкование 6
ZONE_GALVANIZING_7, // цинкование 7
ZONE_GALVANIZING_8, // цинкование 8
ZONE_WASHING_3A, // промывка 3А
ZONE_WASHING_3B, // промывка 3Б
ZONE_PASSIVATION, // пассивация
ZONE_WASHING_4A, // промывка 4A
ZONE_WASHING_4B, // промывка 4Б
ZONE_UNLOAD, // зона выгрузки
};
#define BARRELS_COUNT 10
union barrel_flags {
short raw_word;
struct {
char is_exist: 1;
char is_up: 1; // для панели
char robot: 2; // обслуживающий робот
char is_night: 1; // ночной барабан, имеет такой статус если он в промывке в ночном режиме или в промывке,
// но пока не дождался очереди на выход из ночного режима
char is_empty: 1; // пустой барабан, нужен для работы операции возврата
};
};
struct barrel {
union barrel_flags flags;
short zone;
// программный таймер, тикает сразу у всех барабанов,
short software_timer;
short curr_process; // стадия процесса
// время процессов
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Б
};
// deprecated: do not use!
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[2];
short step;
};
union robot_regs_mx {
short raw_word;
struct {
char correct_status: 1;
char last_cmd_executed: 1;
char correct_sensor: 1;
char move_to_zone: 1;
char move_to_offset: 1;
char move_to_precise: 1;
char do_correct: 1;
char do_parking: 1;
char hla_left: 1;
char hla_right: 1;
char auto_mode: 1;
char error: 1;
};
};
union robot_regs_dx {
short raw_data[6];
struct {
short current_zone;
short target_zone;
short left_max;
short right_max;
short output_current;
short output_freq;
};
};
union robot_regs_mz {
short raw_word;
struct {
char correct_status: 1;
char last_cmd_executed: 1;
char is_up: 1;
char move_up: 1;
char move_down: 1;
char do_correct: 1;
char hla_up: 1;
char hla_down: 1;
char auto_mode: 1;
char error: 1;
};
};
union robot_regs_dz {
short raw_data[3];
struct {
short current_zone;
short output_current;
short output_freq;
};
};
struct robot_regs {
union robot_regs_mx mx;
union robot_regs_dx dx;
union robot_regs_mz mz;
union robot_regs_dz dz;
};
#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;
#define ROBOT_CMD_MASK 0xF000
#define ROBOT_CMD_END 0x0000
#define ROBOT_CMD_MOVE_TO_ZONE 0x1000
#define ROBOT_CMD_MOVE_OFF 0x2000
#define ROBOT_CMD_UP 0x3000
#define ROBOT_CMD_DOWN 0x4000
#define ROBOT_CMD_DOWN_2 0x5000
#define ROBOT_CMD_WAIT 0x6000
#define ROBOT_CMD_TMR_SET 0x7000
#define ROBOT_CMD_SET_LOCK_ZONE 0x8000
#define ROBOT_CMD_CORRECT_X 0x9000
#define ROBOT_CMD_CORRECT_Z 0xA000
// NOTE первой командой на любую транзакцию должна стоять команда опустить траверсу (в 22 зоне по идее никогда не закончим)
struct robot_code {
short barrel_id; // нужен ID барабана, если
short PC; // когда -1, код не выполняется
/*
* система команд, которая нужна: (квадратные скобки - аргумент это младший байт, фигурные - отдельное слово)
* 0: конец
* 1 [зона]: съебаться в зону
* 2: встать на смещенную позицию
* 3 [с барабаном]: поднять траверсу (перед выполнением ожидать если таймер барабана не истек)
* 4 [с барабаном]: опустить траверсу
* 5 [с барабаном]: опустить траверсу не до конца
* 6 [сек]: пауза на нужное количество секунд
* 7 {время}: установить таймер барабану
* 8 [зона]: установить зону блокировки
* 9: скорректировать ос X
* 10: скорректировать ос Z
*
* формат команды: (команда, старший байт) [младший байт, аргумент команды (если есть)] [слово, аргумент если команда требует]
*/
short code[16]; // формат кода: [команды] <команда 0>
};
#ifdef __cplusplus
}
#endif
#endif //SDP_SCHEDULER_UTILS_H