62 lines
3.0 KiB
Python
62 lines
3.0 KiB
Python
# Синтетический снимок регистратора УМП для работы GUI без контроллера. Данные формируются в
|
||
# ожидаемом представлении клиента, чтобы проверять выбор каналов и отображение одной и той же
|
||
# моделью.
|
||
|
||
"""Синтетические записи УМП для проверки графиков без подключения платы."""
|
||
import math
|
||
|
||
from set_devices import ump_logger as codec
|
||
|
||
|
||
def sample(time_ms, period_ms=20, *, repeat=True):
|
||
"""Повторяемый цикл: пуск, разгон 6–16 мА, отключение и спад тока."""
|
||
phase = time_ms % 6000 if repeat else time_ms
|
||
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):
|
||
"""Пример архива: 10% ёмкости банка записывается после отключения."""
|
||
post_points = codec.CAPACITY // 10
|
||
before = list(range(5000, -1, -period_ms))[1:codec.CAPACITY - post_points][::-1]
|
||
after = [5000 + i * period_ms for i in range(1, post_points + 1)]
|
||
records = [sample(t, period_ms) for t in before + [5000]]
|
||
for t in after:
|
||
# Continue the stopped state instead of starting another demo cycle.
|
||
record = sample(t, period_ms, repeat=False)
|
||
record['event'] = 1
|
||
record['off_trigger'] = 0
|
||
records.append(record)
|
||
records[-1]['event'] = 5
|
||
return records
|