Files
Balsam_167/tests/current_measure/model.inc
Andrey 04a9d989fb Исправить TESTLAMP, запуск CAN и расчёт фазных токов
Сохранить состояние TESTLAMP при кратких провалах входа и исключить повторный вход Timer1 во время передачи индикации.
Явно сбрасывать служебный обмен CAN при запуске и выбирать соседнюю фазу по локальному индексу канала АЦП.
Сохранить текущую последовательность DATA/CLK и проверочные варианты таймингов, добавить поведенческие тесты и обновить образ прошивки с картой памяти.
2026-10-13 14:13:43 +03:00

91 lines
15 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.
/* Модель проверяет выбор фаз и диагностику на заданных установившихся 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 о любом нарушении проверяемого поведения. */
} /* Завершает исполняемую модель без записи файлов или изменения прошивки. */