16. Automatizando o Instrutor pelo Python¶
Tudo que a Estação do Instrutor faz, um script pode fazer: carregar um cenário, iniciar a sessão, injetar uma falha, agir como o treinando, encerrar o exercício e salvar o relatório. As classes da sessão vivem no extender do OTS e são classes .NET comuns, então o pythonnet as aciona do mesmo jeito que os exemplos de FluentAPI deste site acionam o motor.
O que você vai aprender
- Carregar uma planta e seu pacote de cenários sem a interface
- Rodar uma sessão sem janelas e ler seu estado
- Agir como o operador por
OtsSession.Execute - Gravar o relatório e o diário de cada cenário numa pasta
Pré-requisitos
Por que automatizar¶
- Regressão: depois de resintonizar uma planta ou atualizar o DWSIM, rode o pacote inteiro sem operador e confira que cada falha ainda produz seus alarmes mais ou menos no mesmo instante. Os tempos das tabelas da página 13 foram produzidos assim.
- Linhas de base e gabaritos: um relatório de "ninguém agiu" e um de "a ação certa na hora certa" para cada cenário, para o treinando comparar.
- Correção em lote: um operador roteirizado que modela uma política (agir no primeiro alarme, agir no HH, não fazer nada) mostra quanto cada política pontua.
O script¶
examples/features/ots_batch.py roda todos os cenários de um arquivo:
set DWSIM_BIN=C:\Users\voce\AppData\Local\DWSIM
python ots_batch.py ots_two_stage_separation.dwxmz --out reports
python ots_batch.py ots_two_stage_separation.dwxmz --out reports_scripted --scripted --only "3."
Ele imprime um bloco por cenário, como a aba Exercise faz:
7 scenarios, 3 interlocks
3. Gas blow-by: 480 steps in 135 s, failed 0, score 45/70
No HH pressure in V-200: Passed (never happened)
No low level trip: Failed (interlock 'V-100 low level trip' tripped happened at 00:06:35)
Take LIC-100 to manual: Failed (no matching action before 00:07:00)
HP level held above L: Passed (reached at 00:00:00)
e grava <cenário>.md (o relatório) e <cenário>.journal.csv para cada um.
Como funciona, peça por peça¶
Carregar. Automation3.LoadFlowsheet abre o arquivo sem janela. O pacote de cenários e a lógica de intertravamento são lidos do arquivo com as mesmas classes de armazenamento que a Estação do Instrutor usa:
from DWSIM.Automation import Automation3
from DWSIM.Extensions.OperatorTraining.Scenarios import ScenarioStore
from DWSIM.Extensions.OperatorTraining.Interlocks import InterlockStore
auto = Automation3()
flowsheet = auto.LoadFlowsheet(path)
scenarios = ScenarioStore.Load(flowsheet)
interlocks = InterlockStore.Load(flowsheet)
A sessão. Um OtsSession por corrida. LoadScenario restaura o estado inicial, arma as falhas e zera os objetivos; Start(True) roda a partir desse estado numa thread de fundo. O script espera consultando StepIndex; SpeedFactor 20 é o mais rápido que a sessão se permite.
from DWSIM.Extensions.OperatorTraining.Runtime import OtsSession, SessionState
session = OtsSession(flowsheet)
session.Interlocks.Load(interlocks)
session.LoadScenario(scenario)
session.SpeedFactor = 20.0
session.Start(True)
while session.StepIndex < steps and session.State == SessionState.Running:
time.sleep(0.05)
report = session.EndExercise() # congela, decide os objetivos, devolve o relatório Markdown
Enquanto roda, session.SimTime, session.Exercise.States (um por objetivo, com Status, Score, Detail), session.Alarms, session.Interlocks e session.Journal são os objetos vivos que as janelas leem.
Agir como o treinando. Toda ação do operador é um OperatorCommand construído pela fábrica Ops e executado por session.Execute, que o enfileira para o próximo passo e registra no diário na categoria Operator, então os objetivos OperatorAction e o relatório o enxergam:
from DWSIM.Extensions.OperatorTraining.Remote import Ops
lic = find(flowsheet, "LIC-100") # o objeto do controlador PID, pelo tag
session.Execute(Ops.Mode(lic, True)) # LIC-100 mode -> MANUAL
session.Execute(Ops.ManualOutput(lic, 20.0))
session.Execute(Ops.SetPoint(find(flowsheet, "PIC-100"), 28.0))
session.Execute(Ops.ValveOpening(find(flowsheet, "FV-100"), 35.0))
session.Execute(Ops.AckAll())
session.Execute(Ops.InterlockReset("V-100 low level trip", False, "script"))
O scripted_operator do script é chamado depois de cada passo (session.StepCompleted) e decide, pelo session.SimTime e pelo nome do cenário, o que fazer. Troque-o pela sua política.
Falhas à mão. As falhas agendadas vêm do cenário; as extras são ativadas do jeito que a aba Malfunctions faz:
from DWSIM.Extensions.OperatorTraining.Malfunctions import MalfunctionCatalog
fault = MalfunctionCatalog.Create("ValveStuck", "LV-100", 1.0, 0.0) # tipo, tag alvo, severidade, rampa
session.Malfunctions.Activate(fault, session.SimTime)
session.Malfunctions.Deactivate(fault)
Snapshots e backtrack são session.SaveSnapshot(nome), session.RestoreSnapshot(nome) e session.Backtrack(segundos), com a sessão congelada (session.Freeze()).
Escrever um pacote de cenários. As classes de armazenamento gravam além de ler. Um cenário é só dados:
from DWSIM.Extensions.OperatorTraining.Scenarios import Scenario, ScheduledMalfunction, Objective, ObjectiveKind
from DWSIM.Extensions.OperatorTraining.Interlocks import InterlockCondition, ConditionKind
sc = Scenario()
sc.Name = "8. Duas falhas"
sc.InitialStateId = "a"; sc.ScheduleId = "schedule1"; sc.SpeedFactor = 5.0
m = ScheduledMalfunction(); m.Kind = "ValveStuck"; m.TargetObject = "LV-100"; m.AtSeconds = 60.0
sc.Malfunctions.Add(m)
o = Objective(); o.Name = "Sem nível HH"; o.Kind = ObjectiveKind.Avoid; o.Points = 20.0
o.Condition.Kind = ConditionKind.AlarmActive; o.Condition.Target = "LIT-100"; o.Condition.AlarmLevel = "HH"
sc.Objectives.Add(o)
scenarios.Add(sc)
ScenarioStore.Save(flowsheet, scenarios)
auto.SaveFlowsheet(flowsheet, path, True)
Os alvos podem ser dados pelo tag (LV-100) ou pelo nome interno; a Estação do Instrutor grava nomes, e os dois resolvem.
Observações¶
- A sessão se paceja mesmo sem janela: 20x com passo de 5 s são quatro passos por segundo, então um cenário de 40 minutos leva uns dois minutos. Os overruns são contados mas não atrapalham aqui.
- Um fluxograma, uma sessão de cada vez. Duas plantas num processo funcionam (é assim que os testes de estação remota rodam); duas sessões no mesmo fluxograma, não.
auto.ReleaseResources()no fim; senão o processo mantém as threads do motor vivas.- As mesmas classes são alcançáveis do C# ou dos blocos de script Python do DWSIM; só o bootstrap muda.
A última página reúne as perguntas que os instrutores mais fazem.