Сохранить состояние TESTLAMP при кратких провалах входа и исключить повторный вход Timer1 во время передачи индикации. Явно сбрасывать служебный обмен CAN при запуске и выбирать соседнюю фазу по локальному индексу канала АЦП. Сохранить текущую последовательность DATA/CLK и проверочные варианты таймингов, добавить поведенческие тесты и обновить образ прошивки с картой памяти.
91 lines
15 KiB
C++
91 lines
15 KiB
C++
/* Модель проверяет выбор фаз и диагностику на заданных установившихся RMS, а не точность аналогового тракта. */
|
||
#include <math.h> /* Даёт настоящим im_calc и Current_count вычисление квадратного корня. */
|
||
#include <stdio.h> /* Выводит имя сценария и конкретное условие при регрессии. */
|
||
#include <string.h> /* Обнуляет независимое состояние перед каждым сценарием. */
|
||
#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 о любом нарушении проверяемого поведения. */
|
||
} /* Завершает исполняемую модель без записи файлов или изменения прошивки. */
|