/* Модель проверяет выбор фаз и диагностику на заданных установившихся RMS, а не точность аналогового тракта. */ #include /* Даёт настоящим im_calc и Current_count вычисление квадратного корня. */ #include /* Выводит имя сценария и конкретное условие при регрессии. */ #include /* Обнуляет независимое состояние перед каждым сценарием. */ #define interrupt /* Убирает аппаратный атрибут ISR из включаемого production-заголовка на хосте. */ typedef float FILTERBAT; /* Заменяет состояние внешнего сглаживающего фильтра: его динамика не относится к выбору пар фаз. */ #include "measure.h" /* Использует реальные ERROR, FLAG, частоту АЦП, маски ошибок и коды типов датчиков. */ #include "package.h" /* Использует реальные типы плат, смещения COMM и адреса технологических регистров. */ int modbus[129], Desk, pow_addr, Curr_Edge; /* Предоставляет штатное регистровое пространство и выбранную конфигурацию измерений. */ int sens_type[24], sens_pair[24], lev_count[6], adc_table_lem[4]; /* Сохраняет production-размеры глобальных датчиков и локальных массивов двух трёхфазных вводов. */ float lev_quadr[6], zer_count[4], powK[4]; /* Содержит RMS-накопители, нулевые уровни и коэффициенты физических каналов. */ FILTERBAT out_filter[24], zer_filter[4]; /* Предоставляет ячейки фильтров по тем же индексам, что в Current_count. */ unsigned int err_count[6], low_count[2], time_3sec; /* Сохраняет настоящие счётчики выдержки фазных ошибок и пониженного напряжения. */ long WAKE, WAKE_TIME; /* Позволяет запускать диагностику после завершения начальной паузы прошивки. */ FLAG chk, sig; /* Предоставляет реальные агрегированные диагностические флаги для reset_errs. */ static int failures; /* Накапливает ошибки всех сценариев, чтобы мутация проверяла сразу передачу и диагностику. */ static float filterbat(FILTERBAT *state, float value) /* Пропускает заданное значение через заглушку внешнего фильтра без изменения исследуемых RMS-накопителей. */ { /* Ограничивает заглушку сглаживания, используемую только вне проверяемого выбора пар. */ (void)state; /* Состояние фильтра не влияет на сценарии с заданными установившимися RMS. */ return value; /* Сохраняет нулевой уровень и амплитуду без дополнительной динамики. */ } /* Завершает единственную заглушку математического окружения. */ /* HANDLERS */ static void check(int condition, const char *what, const char *scenario) /* Записывает конкретную регрессию, продолжая проверку остальных требований. */ { /* Начинает обработку одного ожидаемого поведения. */ if (!condition) { /* Не засоряет вывод успешными отдельными сравнениями. */ printf("FAIL %s: %s\n", what, scenario); /* Имена Modbus и Wry позволяют мутационному запуску различать два последствия ошибки. */ failures++; /* Делает итоговый код завершения ненулевым при любой неудаче. */ } /* Заканчивает регистрацию нарушенного условия. */ } /* Завершает независимую проверку одного свойства. */ static void run_case(int desk, int feed, int ignore, const char *name) /* Проверяет выбранный ввод; feed=-1 задаёт симметрию без перекоса. */ { /* Создаёт полностью независимый сценарий COMM или LOAD. */ int targets[6] = {360, 360, 360, 360, 360, 360}; /* Задаёт симметрию выше порогов COMM200 и LOAD300; третьи фазы расположены в ячейках4 и5. */ int i, tick, local, base, expected_wry, previous = failures; /* Хранит индексы каналов, адрес регистра и число ошибок до сценария. */ memset(modbus, 0, sizeof(modbus)); /* Убирает старые данные и флаги, в том числе Bypas, между сценариями. */ memset(err_count, 0, sizeof(err_count)); /* Начинает выдержку перекоса с нуля, чтобы проверить её реальное накопление. */ memset(low_count, 0, sizeof(low_count)); /* Исключает влияние предыдущей проверки защиты LOAD по нижнему порогу. */ memset(zer_count, 0, sizeof(zer_count)); /* Согласует средний уровень АЦП с нулевыми входными отсчётами. */ memset(adc_table_lem, 0, sizeof(adc_table_lem)); /* Нулевые мгновенные отсчёты позволяют точно задать RMS через состояние фильтра. */ Desk = desk; /* Выбирает настоящую ветку обработки полярности COMM или защиты LOAD. */ pow_addr = desk == dsk_COMM ? TPL_POW * 2 + OWEN_DT : 0; /* Получает глобальное смещение16 для COMM из production-констант и нулевое для LOAD. */ Curr_Edge = desk == dsk_COMM ? 200 : 300; /* Повторяет штатный порог включения контроля перекоса в единицах измеряемой величины. */ time_3sec = 3 * ADC_FREQ; /* Сохраняет настоящую выдержку: три секунды при частоте из measure.h. */ WAKE = WAKE_TIME = 0; /* Снимает стартовую блокировку диагностики и обновления измеренных значений. */ chk.all = sig.all = 0; /* Убирает агрегированные флаги предыдущего сценария. */ for (i = 0; i < 24; i++) { /* Готовит именно глобальную таблицу датчиков, на которой проявлялась ошибка COMM. */ sens_pair[i] = i; /* Оставляет температурным позициям пары с самими собой, как Init_sensors. */ sens_type[i] = VOLTAGE; /* Выбирает ветку напряжения, не затрагивая управление током нагрузки. */ } /* Завершает подготовку глобального пространства датчиков. */ for (i = 0; i < 4; i++) { /* Настраивает четыре физических канала двух вводов. */ sens_pair[pow_addr + i] = pow_addr + (i ^ 1); /* Создаёт штатные глобальные пары; мутация ошибочно обратится к температурной области COMM. */ sens_error[pow_addr + i].bit.Ignor = ignore; /* Проверяет как разрешённый Stop, так и штатное для COMM игнорирование остановки. */ sens_hi_edge[pow_addr + i] = 1000; /* Не допускает посторонней защиты LOAD по верхнему порогу в этих фазных сценариях. */ powK[i] = 1.0f; /* Нулевой отсчёт после вычитания нуля должен давать нулевое мгновенное значение. */ } /* Завершает подготовку физических каналов и их диагностических режимов. */ if (feed >= 0) { /* Задаёт перекос только одному вводу, сохраняя второй симметричным. */ targets[feed * 2] = desk == dsk_COMM ? 250 : 500; /* Первая фаза превышает соответствующий порог включения диагностики. */ targets[feed * 2 + 1] = desk == dsk_COMM ? 149 : 299; /* Вторая фаза ниже порога; историческая ошибка исключала её сравнение с первой. */ targets[4 + feed] = desk == dsk_COMM ? 100 : 200; /* Третья фаза ниже первой на60%, выявляя неверное накопление общего счётчика. */ } /* Завершает выбор контрольных RMS для одного перекошенного ввода. */ for (tick = 0; tick < time_3sec + 2; tick++) { /* Даёт каждому физическому каналу пройти полную выдержку er_anal, включая проверку достижения порога. */ for (i = 0; i < 6; i++) { /* Фиксирует установившиеся RMS отдельно от проверяемой логики пар и счётчиков. */ float rms = targets[i] + 0.25f; /* Запас четверть единицы исключает случайную потерю единицы при округлении float в int. */ lev_count[i] = targets[i]; /* Задаёт значения ещё не обработанных в текущем проходе каналов, как на установившемся сигнале. */ lev_quadr[i] = rms * rms * ADC_FREQ / (ADC_FREQ - 1.0f); /* Компенсирует один шаг экспоненциального RMS-фильтра при нулевом мгновенном отсчёте. */ } /* Завершает подготовку всех шести RMS без вмешательства в диагностические счётчики. */ for (i = 0; i < 4; i++) Current_count(i); /* Повторяет реальный порядок вызовов из ADC.c, включая общий счётчик третьей фазы. */ } /* Завершает выдержку на настоящей production-диагностике. */ for (i = 0; i < 2; i++) { /* Проверяет оба ввода, в том числе отсутствие взаимного влияния их данных. */ local = i * 2; /* Переводит номер ввода в первый из двух физических каналов. */ base = 0x68 + i * 3; /* Адресует штатную тройку фаз:0x68–0x6A или0x6B–0x6D. */ check(modbus[base] == targets[local] && modbus[base + 1] == targets[local + 1] && modbus[base + 2] == targets[4 + i], "Modbus phases", name); /* Требует передачу A/B/C без подмены первой фазы второй. */ expected_wry = i == feed; /* Только выбранный перекошенный ввод должен получить диагностику Wry. */ check(sens_error[pow_addr + local + 1].bit.Wry == expected_wry, "Wry phase B", name); /* Проверяет восстановление сравнения второй фазы с действительным максимумом. */ check(sens_error[pow_addr + local].bit.Wry == expected_wry, "Wry phase C", name); /* Проверяет общий счётчик третьей фазы, который исторически чередовал увеличение и уменьшение. */ check(sens_error[pow_addr + local + 1].bit.Stop == (expected_wry && !ignore), "Stop respects Ignor", name); /* Разделяет диагностику перекоса и разрешение остановки. */ } /* Завершает проверки обоих вводов после выдержки. */ if (failures == previous) printf("PASS %s\n", name); /* Показывает полное прохождение сценария только при выполнении всех ожиданий. */ } /* Завершает один сценарий со сброшенным и затем выдержанным состоянием. */ int main(void) /* Объединяет адресацию обоих вводов, два режима Ignor, симметрию и регрессию LOAD. */ { /* Запускает одинаковый набор сценариев для production и временной исторической мутации. */ run_case(dsk_COMM, 0, 0, "COMM feed 1: 250/149/100, Ignor=0"); /* Проверяет первую тройку регистров и разрешённый Stop. */ run_case(dsk_COMM, 1, 0, "COMM feed 2: 250/149/100, Ignor=0"); /* Проверяет вторую тройку без подмены данных между вводами. */ run_case(dsk_COMM, 0, 1, "COMM feed 1: 250/149/100, Ignor=1"); /* Требует Wry при выключенной реакции Stop для первого ввода. */ run_case(dsk_COMM, 1, 1, "COMM feed 2: 250/149/100, Ignor=1"); /* Требует такую же независимость Wry от Ignor на втором вводе. */ run_case(dsk_COMM, -1, 0, "COMM symmetric: 360/360/360"); /* Исключает ложный перекос при симметрии выше порога диагностики. */ run_case(dsk_LOAD, 0, 0, "LOAD feed 1: 500/299/200, pow_addr=0"); /* Проверяет сохранение штатных локальных пар LOAD и его порога300. */ run_case(dsk_LOAD, 1, 0, "LOAD feed 2: 500/299/200, pow_addr=0"); /* Проверяет вторую пару LOAD при нулевом глобальном смещении. */ run_case(dsk_LOAD, -1, 0, "LOAD symmetric: 360/360/360"); /* Исключает ложное срабатывание LOAD на симметрии выше его порога. */ return failures ? 1 : 0; /* Сообщает вызывающему Python о любом нарушении проверяемого поведения. */ } /* Завершает исполняемую модель без записи файлов или изменения прошивки. */