# Учебная редакция: назначение, единицы и этапы алгоритма пояснены по-русски.
# Исходная программа остаётся самостоятельным свидетельством прежней проверки.
# Порядок команд соответствует нативной программе курса II00-2.py.
# run_case вызывает FEMM только при явной передаче настоящего API.
def run_case(api, source_model, working_model, circuit="Катушка", current_a=2.0):
    """Рассчитать свежий режим катушки через переданный словарь команд FEMM.
    
    source_model и working_model — разные пути к исходной и рабочей моделям.
    В исходной модели уже должна существовать указанная цепь. Ток — в А;
    возвращаются ток, А, напряжение полевой модели, В, и потокосцепление, Вб."""
    if source_model == working_model:
        raise ValueError("Use a separate working model")
    api["opendocument"](source_model)
    # Рабочую копию сохраняем до изменения источника тока.
    api["mi_saveas"](working_model)
    api["mi_modifycircprop"](circuit, 1, current_a)
    # Исключение завершает функцию: старое решение не возвращается как новое.
    api["mi_analyze"](1)
    api["mi_loadsolution"]()
    # Читаем только что загруженное решение; порядок величин задан API FEMM.
    current, voltage, linkage = api["mo_getcircuitproperties"](circuit)
    api["mo_close"]()
    return {"current_A": current, "voltage_V": voltage, "linkage_Wb": linkage}


def run_checks():
    """Проверить порядок вызовов на имитации API и прекращение чтения после отказа."""
    calls = []
    def command(name, value=None):
        """Создать регистрирующую замену команды API с заданным возвращаемым значением."""
        def invoke(*args):
            """Записать имя вызванной команды; аргументы здесь не моделируют FEMM."""
            calls.append(name)
            return value
        return invoke
    names = ["opendocument", "mi_saveas", "mi_modifycircprop", "mi_analyze", "mi_loadsolution", "mo_getcircuitproperties", "mo_close"]
    api = dict((name, command(name)) for name in names)
    api["mo_getcircuitproperties"] = command("mo_getcircuitproperties", (2., 0., .24))
    assert run_case(api, "source.fem", "work.fem")["linkage_Wb"] == .24
    # Проверяется последовательность API, а не физическая точность полевого решения.
    assert calls == names
    calls[:] = []
    def failure(*args):
        """Изобразить отказ решателя для проверки запрета чтения устаревшего решения."""
        calls.append("mi_analyze")
        raise ValueError("Controlled solver failure")
    api["mi_analyze"] = failure
    rejected = False
    try:
        run_case(api, "source.fem", "work.fem")
    except ValueError:
        rejected = True
    assert rejected and "mi_loadsolution" not in calls and "mo_getcircuitproperties" not in calls
    return {"scope": "API order mock only; native probe is recorded separately", "no_stale_read_after_failure": True}
