diff --git a/robot.cpp b/robot.cpp index d4ad64e..22028bc 100644 --- a/robot.cpp +++ b/robot.cpp @@ -41,12 +41,12 @@ static void emulate_robot(robot_code &code, robot_regs& r, char robot_id) { barrels[code.barrel_id].flags.robot = robot_id; } - const short cmd_arg = code.code[code.PC] & (~ROBOT_CMD_MASK); + const auto cmd_arg = (short)(code.code[code.PC] & (~ROBOT_CMD_MASK)); - switch (code.code[code.PC] & ROBOT_CMD_MASK) { - case ROBOT_CMD_MOVE_TO_ZONE: + switch ((short)(code.code[code.PC] & (short)ROBOT_CMD_MASK)) { + case ROBOT_CMD_MOVE_TO_ZONE_code: // двигаемся в сторону цели - if (robot_move(r, cmd_arg & (~ROBOT_WITH_BARREL), robot_id)) { + if (robot_move(r, (short)(cmd_arg & (~ROBOT_WITH_BARREL)), robot_id)) { code.PC++; } if (cmd_arg & ROBOT_WITH_BARREL && code.barrel_id >= 0) { @@ -54,16 +54,27 @@ static void emulate_robot(robot_code &code, robot_regs& r, char robot_id) { } break; - case ROBOT_CMD_MOVE_OFF: + case ROBOT_CMD_MOVE_OFF_code: if (robot_id == 1) { robot1_offset_pos = true; } else { robot2_offset_pos = true; } + std::cout << "robot " << robot_id << " move to offset pos" << std::endl; code.PC++; break; - case ROBOT_CMD_UP: + case ROBOT_CMD_MOVE_ACCURATE_code: + if (robot_id == 1) { + robot1_offset_pos = false; + } else { + robot2_offset_pos = false; + } + std::cout << "robot " << robot_id << " move to accurate position" << std::endl; + code.PC++; + break; + + case ROBOT_CMD_UP_code: if (code.barrel_id >= 0 && cmd_arg) { // не давать ехать перед тем как истечет таймер if (barrels[code.barrel_id].software_timer > 0) { @@ -76,8 +87,7 @@ static void emulate_robot(robot_code &code, robot_regs& r, char robot_id) { break; // в эмуляторе не важно где я, поэтому тут обе команды вниз обрабатываются одинаково - case ROBOT_CMD_DOWN: - case ROBOT_CMD_DOWN_2: + case ROBOT_CMD_DOWN_code: if (code.barrel_id >= 0 && cmd_arg) { barrels[code.barrel_id].flags.is_up = false; r.mz.is_up = 0; @@ -85,20 +95,20 @@ static void emulate_robot(robot_code &code, robot_regs& r, char robot_id) { code.PC++; break; - case ROBOT_CMD_WAIT: + case ROBOT_CMD_WAIT_code: std::cout << "robot " << robot_id << " wait " << cmd_arg << " secs..." << std::endl; code.PC++; break; - case ROBOT_CMD_TMR_SET: + case ROBOT_CMD_TMR_SET_code: if (code.barrel_id >= 0) { barrels[code.barrel_id].software_timer = code.code[code.PC + 1]; r.mz.is_up = 0; } - code.PC += 2; + code.PC++; break; - case ROBOT_CMD_SET_LOCK_ZONE: + case ROBOT_CMD_SET_LOCK_ZONE_code: if (robot_id == 1) { robot1_lock_zone = cmd_arg; } else { @@ -107,7 +117,7 @@ static void emulate_robot(robot_code &code, robot_regs& r, char robot_id) { code.PC += 2; break; - case ROBOT_CMD_CORRECT_AXIS: + case ROBOT_CMD_CORRECT_AXIS_code: if (cmd_arg == ROBOT_AXIS_X) { std::cout << "robot " << robot_id << " correct axis X..." << std::endl; r.dz.current_zone = 0; @@ -117,28 +127,36 @@ static void emulate_robot(robot_code &code, robot_regs& r, char robot_id) { r.mz.is_up = true; r.mz.correct_status = true; } else { - std::cout << "ERROR: R" << robot_id << " AXIS CORRECT - INCORRECT ARGUMENT VALUE " << cmd_arg << std::endl; + std::cout << "ERROR: R" << robot_id << " AXIS CORRECT - INVALID ARGUMENT VALUE " << cmd_arg << std::endl; } code.PC++; break; - case ROBOT_CMD_INC_ZONE: + case ROBOT_CMD_INC_ZONE_code: // TODO сделать так, чтобы зоны переключались с учетом отключенных зон if (cmd_arg == ROBOT_ZONE_ETCH) { std::cout << "robot " << robot_id << " increment etching..." << std::endl; - etching_zone = (etching_zone + 1) & 1; + etching_zone = (short)((etching_zone + 1) & 1); } else if (cmd_arg == ROBOT_ZONE_GAL) { std::cout << "robot " << robot_id << " increment galvanic..." << std::endl; - galvanizing_zone = (galvanizing_zone + 1) & 0x07; + galvanizing_zone = (short)((galvanizing_zone + 1) & 0x07); } else { - std::cout << "ERROR: R" << robot_id << " INCREMENT ZONE - INCORRECT ARGUMENT VALUE " << cmd_arg << std::endl; + std::cout << "ERROR: R" << robot_id << " INCREMENT ZONE - INVALID ARGUMENT VALUE " << cmd_arg << std::endl; } code.PC++; break; - case ROBOT_CMD_END: + case ROBOT_CMD_END_code: + code.PC = -1; + // де-приватизируем бочку + if (code.barrel_id >= 0) { + barrels[code.barrel_id].flags.robot = 0; + } + break; + default: + printf("ERROR: R%d INVALID INSTRUCTION: PC=%d CODE=0x%04X\n", robot_id, code.PC, code.code[code.PC] & 0xFFFF); code.PC = -1; // де-приватизируем бочку if (code.barrel_id >= 0) { diff --git a/scheduler.c b/scheduler.c index 8a802a0..f4012ad 100644 --- a/scheduler.c +++ b/scheduler.c @@ -271,48 +271,50 @@ 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); + const short cmd_arg = (short)(robot1_code.code[i] & (short)(~ROBOT_CMD_MASK)); - if ((robot1_code.code[i] & ROBOT_CMD_MASK) == ROBOT_CMD_END) { + printf(" %3d: 0x%04X", i, robot1_code.code[i] & 0xFFFF); + + if ((robot1_code.code[i] & ROBOT_CMD_MASK) == ROBOT_CMD_END_code) { printf(" END\n"); break; } - switch (robot1_code.code[i] & ROBOT_CMD_MASK) { - case ROBOT_CMD_MOVE_TO_ZONE: + switch ((short)(robot1_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: - printf(" move to offset pos\n"); + case ROBOT_CMD_MOVE_OFF_code: + if (cmd_arg) { + printf(" move to offset pos\n"); + } else { + printf(" move to accurate pos\n"); + } break; - case ROBOT_CMD_UP: + case ROBOT_CMD_UP_code: printf(" up (with barrel: %d)\n", (cmd_arg & ROBOT_WITH_BARREL) != 0); break; // в эмуляторе не важно где я, поэтому тут обе команды вниз обрабатываются одинаково - case ROBOT_CMD_DOWN: + case ROBOT_CMD_DOWN_code: 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: + case ROBOT_CMD_WAIT_code: printf(" wait %d secs\n", cmd_arg); break; - case ROBOT_CMD_TMR_SET: - printf(" set barrel timer %d secs\n", robot1_code.code[++i]); + case ROBOT_CMD_TMR_SET_code: + printf(" set barrel timer %d secs\n", cmd_arg); break; - case ROBOT_CMD_SET_LOCK_ZONE: + case ROBOT_CMD_SET_LOCK_ZONE_code: printf(" set lock zone %d\n", cmd_arg); break; - case ROBOT_CMD_CORRECT_AXIS: + case ROBOT_CMD_CORRECT_AXIS_code: if (cmd_arg == ROBOT_AXIS_X) { printf(" correct axis: X\n"); } else if (cmd_arg == ROBOT_AXIS_Z) { @@ -322,18 +324,18 @@ void debug_print_robot1_code() { } 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"); + 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: INVALID (%d)\n", cmd_arg); + printf(" increment zone: INVALID (0x%4X)\n", cmd_arg); } break; default: - printf(" UNKNOWN: 0x(%04X)\n", cmd_arg); + printf(" UNKNOWN: 0x%04X\n", robot1_code.code[i] & 0xFFFF); } } } @@ -363,45 +365,41 @@ void schedule_robot_1() { short cmd_index = 0; // первым делом добавляем команду опустить траверсу - robot1_code.code[cmd_index++] = ROBOT_CMD_DOWN; + robot1_code.code[cmd_index++] = ROBOT_CMD_DOWN(); if (tasks[target_task].dest_zone == ZONE_PASSIVATION) { // пассивация, тут все просто 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_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_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_TMR_SET(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_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_TMR_SET(barrels[target_task].time_washing_4a); } else { // любой другой случай 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_TO_ZONE | tasks[target_task].start_zone; + robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE(tasks[target_task].start_zone); } else { - robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_OFF; - robot1_code.code[cmd_index++] = ROBOT_CMD_UP; - if (robot1.dx.current_zone != 21) { - 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_DOWN_2; - robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_TO_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(22); + robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_OFF(); + robot1_code.code[cmd_index++] = ROBOT_CMD_DOWN(); + robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_ACCURATE(); } } - robot1_code.code[cmd_index++] = ROBOT_CMD_UP_WITH_BARREL; + robot1_code.code[cmd_index++] = ROBOT_CMD_UP_WITH_BARREL(); // теперь надо определиться с тем, сколько ждать скапывания switch (tasks[target_task].start_zone) { @@ -418,7 +416,7 @@ void schedule_robot_1() { case ZONE_GALVANIZING_8: // время скапывания реактивов // TODO добавить переменные времен скапывания - robot1_code.code[cmd_index++] = ROBOT_CMD_WAIT | 30; + robot1_code.code[cmd_index++] = ROBOT_CMD_WAIT(30); break; case ZONE_WASHING_1A: @@ -426,7 +424,7 @@ void schedule_robot_1() { case ZONE_WASHING_3A: case ZONE_WASHING_4A: // время скапывания 1-го каскада промывок - robot1_code.code[cmd_index++] = ROBOT_CMD_WAIT | 3; + robot1_code.code[cmd_index++] = ROBOT_CMD_WAIT(3); break; case ZONE_WASHING_1B: @@ -434,21 +432,30 @@ void schedule_robot_1() { case ZONE_WASHING_3B: case ZONE_WASHING_4B: // время скапывания 2-го каскада промывок - robot1_code.code[cmd_index++] = ROBOT_CMD_WAIT | 20; + 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; + robot1_code.code[cmd_index++] = ROBOT_CMD_MOVE_TO_ZONE_WITH_BARREL(tasks[target_task].dest_zone); + + // инкремент зоны (если травление или цинкование) + if (tasks[target_task].dest_zone == ZONE_ETCHING_1 || tasks[target_task].dest_zone == ZONE_ETCHING_2) { + robot1_code.code[cmd_index++] = ROBOT_CMD_INC_ZONE(ROBOT_ZONE_ETCH); + } else if (tasks[target_task].dest_zone >= ZONE_GALVANIZING_1 && tasks[target_task].dest_zone <= ZONE_GALVANIZING_8) { + robot1_code.code[cmd_index++] = ROBOT_CMD_INC_ZONE(ROBOT_ZONE_GAL); + } + + robot1_code.code[cmd_index++] = ROBOT_CMD_DOWN_WITH_BARREL(); 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; + 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; + 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) { @@ -499,15 +506,17 @@ void schedule_robot_1() { break; } - if (tmp != -1) { - robot1_code.code[cmd_index++] = ROBOT_CMD_TMR_SET; - robot1_code.code[cmd_index++] = tmp; + if (tmp > 0) { + if (tmp > 8000) { + tmp = 8000; + } + robot1_code.code[cmd_index++] = ROBOT_CMD_TMR_SET(tmp); } } } } - robot1_code.code[cmd_index++] = ROBOT_CMD_END; + robot1_code.code[cmd_index++] = ROBOT_CMD_END(); robot1_code.PC = 0; printf("INFO: code length is %d\n", cmd_index); diff --git a/utils.h b/utils.h index 0cff38d..5924738 100644 --- a/utils.h +++ b/utils.h @@ -139,38 +139,57 @@ extern struct barrel barrels[BARRELS_COUNT]; //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_AXIS 0x9000 -#define ROBOT_CMD_INC_ZONE 0xA000 +// появилась идея сделать пачку short-команд, то есть вставлять не полный код операции, а код 0x7000 | код операции 0x0X00 + +#define ROBOT_CMD_MASK (short)0xF000 +#define ROBOT_CMD_END_code (short)0x0000 +#define ROBOT_CMD_MOVE_TO_ZONE_code (short)0x1000 +#define ROBOT_CMD_MOVE_OFF_code (short)0x2000 +#define ROBOT_CMD_MOVE_ACCURATE_code (short)0x3000 +#define ROBOT_CMD_UP_code (short)0x4000 +#define ROBOT_CMD_DOWN_code (short)0x5000 +#define ROBOT_CMD_WAIT_code (short)0x6000 +#define ROBOT_CMD_TMR_SET_code (short)0x7000 +#define ROBOT_CMD_SET_LOCK_ZONE_code (short)0x8000 +#define ROBOT_CMD_CORRECT_AXIS_code (short)0x9000 +#define ROBOT_CMD_INC_ZONE_code (short)0xA000 // перемещение с барабаном #define ROBOT_WITH_BARREL 0x0800 -// алиасы перемещений с барабаном -#define ROBOT_CMD_MOVE_TO_ZONE_WITH_BARREL (ROBOT_CMD_MOVE_TO_ZONE | ROBOT_WITH_BARREL) -#define ROBOT_CMD_UP_WITH_BARREL (ROBOT_CMD_UP | ROBOT_WITH_BARREL) -#define ROBOT_CMD_DOWN_WITH_BARREL (ROBOT_CMD_DOWN | ROBOT_WITH_BARREL) -#define ROBOT_CMD_DOWN_2_WITH_BARREL (ROBOT_CMD_DOWN_2 | ROBOT_WITH_BARREL) +// опции зон +#define ROBOT_ZONE_ETCH 0x0400 +#define ROBOT_ZONE_GAL 0x0200 +#define ROBOT_ZONE_PARKING 0x0100 +// опции коррекции осей #define ROBOT_AXIS_X 1 -#define ROBOT_CMD_CORRECT_AXIS_X (ROBOT_CMD_CORRECT_AXIS | ROBOT_AXIS_X) #define ROBOT_AXIS_Z 2 -#define ROBOT_CMD_CORRECT_AXIS_Z (ROBOT_CMD_CORRECT_AXIS | ROBOT_AXIS_Z) -#define ROBOT_ZONE_ETCH 1 -#define ROBOT_CMD_INC_ZONE_ETCH (ROBOT_CMD_INC_ZONE | ROBOT_ZONE_ETCH) -#define ROBOT_ZONE_GAL 2 -#define ROBOT_CMD_INC_ZONE_GAL (ROBOT_CMD_INC_ZONE | ROBOT_ZONE_GAL) + +// макросы для генерации команд +#define ROBOT_CMD_END() (ROBOT_CMD_END_code) + +#define ROBOT_CMD_MOVE_TO_ZONE(zone) ((ROBOT_CMD_MOVE_TO_ZONE_code) | (short)(zone & 0x00FF)) +#define ROBOT_CMD_MOVE_TO_ZONE_WITH_BARREL(zone) ((ROBOT_CMD_MOVE_TO_ZONE_code) | (short)(ROBOT_WITH_BARREL) | (short)(zone & 0x00FF)) +#define ROBOT_CMD_MOVE_TO_PARKING() ((short)(ROBOT_CMD_MOVE_TO_ZONE_code) | (short)(ROBOT_ZONE_PARKING)) + +#define ROBOT_CMD_MOVE_OFF() (ROBOT_CMD_MOVE_OFF_code) +#define ROBOT_CMD_MOVE_ACCURATE() (ROBOT_CMD_MOVE_ACCURATE_code) + +#define ROBOT_CMD_UP() (ROBOT_CMD_UP_code) +#define ROBOT_CMD_UP_WITH_BARREL() ((ROBOT_CMD_UP_code) | (short)(ROBOT_WITH_BARREL)) +#define ROBOT_CMD_DOWN() (ROBOT_CMD_DOWN_code) +#define ROBOT_CMD_DOWN_WITH_BARREL() ((ROBOT_CMD_DOWN_code) | (short)(ROBOT_WITH_BARREL)) + +#define ROBOT_CMD_WAIT(time) ((ROBOT_CMD_WAIT_code) | (short)(time & 0x0FFF)) +#define ROBOT_CMD_TMR_SET(time) ((ROBOT_CMD_TMR_SET_code) | (short)(time & 0x0FFF)) + +#define ROBOT_CMD_SET_LOCK_ZONE(zone) ((short)(ROBOT_CMD_SET_LOCK_ZONE_code) | (short)(zone)) +#define ROBOT_CMD_CORRECT_AXIS(axis) ((short)(ROBOT_CMD_CORRECT_AXIS_code) | (short)axis) +#define ROBOT_CMD_INC_ZONE(arg) ((ROBOT_CMD_INC_ZONE_code) | (short)(arg)) + // NOTE первой командой на любую транзакцию должна стоять команда опустить траверсу (в 22 зоне по идее никогда не закончим) struct robot_code { @@ -178,18 +197,15 @@ struct robot_code { short PC; // когда -1, код не выполняется /* - * система команд, которая нужна: (квадратные скобки - аргумент это младший байт, фигурные - отдельное слово) - * 0: конец - * 1 (опция с барабаном) [зона]: съебаться в зону - * 2: встать на смещенную позицию - * 3 (опция с барабаном): поднять траверсу (перед выполнением ожидать если таймер барабана не истек) - * 4 (опция с барабаном): опустить траверсу - * 5 (опция с барабаном): опустить траверсу не до конца - * 6 [сек]: пауза на нужное количество секунд - * 7 {время}: установить таймер барабану - * 8 [зона]: установить зону блокировки - * 9 [X(1) | Y(2)]: скорректировать ось - * 10 [ETCH(1) |GAL (2)]: сделать инкремент зоны гальваники или обезжиривания + * система команд, которая нужна: (квадратные скобки - аргумент это младшие 14 бит) + * 0 (смещение: да () ? нет): установить смещение (да - встанет в смещенную позицию, нет - в точную) + * 1 (опция с барабаном) [поднять (1) | опустить]: поднять/опустить траверсу (перед поднятием ожидать если таймер барабана не истек) + * 2 (опция с барабаном) [зона]: съебаться в зону + * 3 [сек]: пауза на нужное количество секунд + * 4 [время]: установить таймер барабану + * 5 (ETCH(1) | GAL (2)) [зона]: установить зону блокировки, умеет использовать автоинкримент зоны гальваники или обезжиривания + * 6 [X(1) | Y(2)]: скорректировать ось + * 15: конец * * формат команды: (команда, старший байт) [младший байт, аргумент команды (если есть)] [слово, аргумент если команда требует] */