"""Синтетические записи УМП для проверки графиков без подключения платы.""" import math from set_devices import ump_logger as codec def sample(time_ms, period_ms=20): """Повторяемый цикл: пуск, разгон 6–16 мА, отключение и спад тока.""" phase = time_ms % 6000 started = 1000 <= phase < 5000 reset = 5000 <= phase < 5300 # Как в прошивке: фронт команды включает выход на 1000 мс. start_out = 1000 <= phase < 2000 stop_out = 5000 <= phase < 6000 ramp = max(0, min(2500, phase - 1000)) if started: current = 6 + 10 * ramp / 2500 + 0.2 * math.sin(phase / 90) elif phase >= 5000: current = 4 + 12 * max(0, 1 - (phase - 5000) / 300) else: current = 4 words = [0] * codec.WORDS words[0], words[1] = time_ms & 65535, (time_ms >> 16) & 65535 words[2] = 1 | (0x100 if 5000 <= phase < 5000 + period_ms else 0) words[3] = (int(started) << 7) | (int(reset) << 6) words[4] = int(1200 <= phase < 5100) | (int(started) << 1) | (int(reset) << 2) words[5] = int(start_out) | (int(stop_out) << 1) | (int(started) << 2) words[6] = (2000 - phase) if start_out else ((6000 - phase) if stop_out else 0) words[7] = max(0, 250 - phase) words[8] = round(500 + (current - 4) * 3000 / 16) words[9] = round(current * 10) for channel in range(4): measured = round(120 * math.sin(phase / (180 + channel * 70) + channel)) words[10 + channel] = measured & 65535 words[14 + channel] = (1 << channel) if 5100 <= phase < 5400 else 0 words[36 + channel] = 2048 + measured * 4 words[18:24] = [0xE07F, words[3], 6, 16, (time_ms // 100) & 65535, 0] words[24:31] = [6, 16, 500, 3500, 2000, 500, 3500] words[31:36] = [ramp if started else 0, 2500, 60, 160, round(ramp * 1000 / 2500)] return codec.sample(words) def capture(period_ms): """Пример архива: до 399 точек предыстории, отключение и 1 с после него.""" before = list(range(5000, -1, -period_ms))[1:400][::-1] after = list(range(5000 + period_ms, 6000, period_ms)) + [6000] available = codec.CAPACITY - len(before) - 1 truncated = len(after) > available if truncated: after = after[:available - 1] + after[-1:] records = [sample(t, period_ms) for t in before + [5000] + after] # Последний снимок относится к концу постинтервала, а не новому циклу. records[-1]['event'] = 5 | (0x200 if truncated else 0) records[-1]['post_truncated'] = int(truncated) records[-1]['off_trigger'] = 0 return records