The Switching Simulator is a local, instructional web application for drawing the single-line diagram of a substation, rehearsing switching operations on it, and assembling the switching plan. At each step it shows what is energized, what is de-energized, and what the system does not know. And it refuses the operation an interlocking rule prohibits, saying which rule it would violate.
It does not connect to SCADA, to a production database, or to real equipment, and it does not replace an electrical study, engineering approval, or an operating procedure. This technical case shows how the electrical engine works, who decides the switching sequence, the errors that became rules, and why the product still has no acceptance. The numbers were checked in the code, in the version of October 27, 2026.
Transparency: the Switching Simulator is designed and developed by Jeremias Cardoso, the author of this blog. The text does not name a company, people, substations, regions, or equipment codes.
Update note (October 30, 2026): this post was rewritten as a technical case. The version of September 24, 2026 said the system was written in TypeScript and drawn on Canvas, that it proposed the switching sequence between two points on its own, and that it ran “hundreds of combinations” of switches. Checked in the code, the system is JavaScript with drawing in SVG, the sequence is assembled by the user with the help of previews and proposals that a person accepts, and the test numbers are below. The correction of October 29 still stands: the disconnect switches are opened first, then the absence of voltage is verified, and only then are the grounding switches closed.
The problem: the switching order does not allow an error
In a substation, each piece of equipment has a role. The circuit breaker interrupts current. The disconnect switch isolates, but it is not made to open while current is flowing, and so it is operated only with the adjacent circuit breaker open. The grounding switch discharges the section that will receive maintenance, and it may close only when that section is de-energized. Reversing the order damages equipment and puts people at risk.
That is why switching is practiced. The simulator exists so the error happens on the screen, where it costs a message, and so that whoever plans a switching operation sees the effect of each step before writing the plan. The project was born inside the repository of SIGCOT, the operations-management system by the same author, and became an independent repository on August 18, 2026.
The system in numbers
Repository numbers on October 27, 2026, the date of the last commit.
| Measure | Value |
|---|---|
| Application code, excluding blank lines and comments | about 79 thousand lines: 68.9 thousand in JavaScript, 8.9 thousand in styles, and 1.2 thousand in pages |
| Test code | 65.9 thousand lines in 475 files; the main command runs 111 of them |
| Tests in the main command (October 19) | 1,371, all passed, in about 22 minutes with a real browser |
| Git history | 166 commits, from August 5 to October 27, 2026, with no version tagged |
| Substations in the simulator | 5 in the same session and in the same plan, 2 of them for reference only |
| Most complete pilot | 173 pieces of equipment and 272 open-and-close sheets for the 136 that have a mechanical position |
| Diagram editor | 20 component types; up to 250 components, 500 connections, and 250 rules per document |
| Pilot package version | 0.1.0 |
The line count leaves out the packages generated by the build, the reports, the documents, and the experiments. Part of the application code describes the diagrams of the modeled substations, and not only rules.
Five things that must not be mixed
The project’s most important modeling decision is in one sentence of the documentation: mechanical position, energization, authorization, topology, and interlocking are distinct dimensions. Knowing that a switch is closed does not say whether there is voltage on it. Knowing that there is voltage does not say whether someone authorized the operation. And none of those answers stands in for another.
From that comes the contract that runs through the entire code: an electrical fact that nobody confirmed stays as null or PENDING, and the system fails closed. It never guesses a position to complete the drawing, and it never treats as de-energized a section it does not know. Confirming one dimension releases only that dimension.
The electrical graph engine
The heart of the simulator is an engine that runs without a browser. It receives three inputs: the switchyard topology, the mechanical position of each switch, and the condition of each source. It returns a frozen “snapshot” of the electrical state. The interface only draws that snapshot: it is a projection, not the source of truth.
The calculation is a graph search, with four simple, strict rules:
- Only a closed switch conducts. The electrical path passes only through closed switches.
- A bus is not a source. The sources are the incoming lines; the bus is live only if there is a closed path to one of them.
- The unknown spreads. A switch or source in an unknown state marks as
PENDINGeverything it reaches. An absent value never becomes “energized.” - Complete is the exception, not the default. The snapshot is marked complete only when no unknown switch or source remains.
The grounding switch does not receive an electrical state: the drawing shows only its position. On the screen, black means an energized path, white means no path, and PENDING is dashed white. The visual convention has a rule that came from an error (further below): shape shows the switch position, and color shows the voltage, never one in place of the other.
The engine has two modes. In automatic energy, energization is recalculated from the declared sources and the connectivity after each switching operation. In reported energy, the user declares that a section is de-energized, and the system treats that as a user premise, not as a calculation or a measurement.
What the engine does not do is also written down: it does not calculate power flow, it does not propagate state between substations, and it has neither telemetry nor remote command. The condition of each line arriving from outside is a scenario input. That limit is not a detail: a system that does not talk to real equipment does not become an entry point for an attack on an industrial controller.
The interlocking rules
On top of the electrical snapshot, the engine evaluates the interlocking rules. The three central ones, as they stand in the architecture documentation:
- the disconnect switch beside a circuit breaker operates only with that circuit breaker open;
- the ground always opens, but it closes only with the circuit de-energized and the isolating disconnect switch open;
- the circuit breaker always opens, but it closes only with both adjacent disconnect switches closed and with every ground it would reach explicitly open.
The third rule has an implementation detail worth explaining. To know which grounds the circuit breaker would reach, the engine builds the graph as if the candidate circuit breaker were already closed and looks for grounds on that hypothetical path. A missing or pending ground on that path blocks the closing.
When the operation is refused, nothing changes: not the position, not the plan, and not the simulated condition. The screen shows the reason and the code of the violated rule, and the same code goes into page attributes, so the information does not depend on color alone. When the operation is accepted, one position changes and exactly one step enters the plan.
Who assembles the switching sequence
The previous version of this text said the system proposed the safety sequence between two points on its own. That is not what the code does, and the difference matters.
The switching plan is born from the operations the user executes: each effective operation becomes a step, and the plan is a list that can be edited. On top of that, the system offers three aids, all with a person in command:
- Clearance preview. On the test bench, a deterministic robot (the connections, the registry, and the recorded rules define what it proposes) calculates a preview for the chosen target, inside the perimeter the author declared: open the circuit breakers, then the disconnect switches, then close the grounds. If the perimeter is incomplete, the preview is refused. Calculating the preview executes nothing: the user is the one who executes, step by step or in sequence.
- Assisted line authoring. When drawing a transmission line with both ends on the same drawing, the author can request a proposal of interlocking rules. The author accepts or rejects it. The system does not infer a piece of equipment’s function from its name, and it does not create positions or presume energization.
- Guided script. It is advisory: it derives from the rules and never executes equipment or writes to the plan.
Several substations, one plan
A real switching operation rarely stays inside one substation: de-energizing a line involves both ends. For that, every element received a global reference, formed by the substation and by its identifier, and the plan came to belong to the global state, not to one of the substations.
The links between substations are structural and never serve as a remote command. Storing a copy of the other end’s state is forbidden. The contract exists for a scenario the simulator needs to show: the operator opens the remote terminal, thinks the line is dead, and it stays live through the local terminal, with the line switch closed.
Files from outside gain no power
The editor imports and exports diagrams in JSON, and the import is treated as untrusted input. The entire file is validated on a temporary copy, and only a safe candidate replaces the draft. An imported JSON cannot give an element switching capability or forge the topology. Reserved names such as __proto__ are refused, and no imported or typed text is executed by the page.
Distribution follows the same line. The package is a portable ZIP, with shortcuts to open the file directly or to serve the application only to the local machine, and a manifest with the SHA-256 hashes. It does not install a program and does not send a file over the internet.
Incidents that became rules
The first complete diagram was built in five rounds, on August 7 and 8, 2026, and the errors from those rounds became a guide for the diagrams that followed. Others came later.
- The bus treated as a source. It was the most serious error and the most persistent: it survived three rounds. With the bus lit on its own, the diagram showed voltage where there was none. Rule: the sources are the lines, and the bus energizes only through a closed path to one of them. With all the lines out, the entire diagram stays white, which is the real behavior.
- The closed circuit breaker identical to the open one. In the second round, the colors for position and for voltage mixed, and a closed circuit breaker looked identical to an open one. Rule: shape for position, color for voltage.
- One boolean for two sides. The line stroke appeared live past an open switch, because a single variable represented both sides of the disconnect switch. Rule: every line switch is a two-sided boundary.
- The ground required in order to reclose. Treating the ground and the disconnect switch as one thing made the model require the ground closed in order to reclose the circuit breaker, the opposite of what is correct. Rule: the ground has its own rule.
- The green suite with the interlocking reversed. It happened twice: every test passed with a rule backwards, because the tests checked the rule as it had been written, not the sequence the operator performs. Rule: the test is born from the operator’s script, not from the code. It is the most dangerous form of silent failure: the green signal exists, and the protection does not.
- Sixteen forgotten conductors. A script that opens every source and lists what stays dark found 16 conductors without the section marking, which no test caught.
- The layout fix that made everything worse (September 1, 2026). A layout fix made without first writing the acceptance case for the affected screen range traded one defect for a worse one, and the acceptance suite passed because it visited only two resolutions. Rule: the range’s test goes into the same commit as the fix.
- The omission that turned into permission (October 23, 2026). A default value caused every asset with no declared owner to be treated as owned. Rule: ownership declared per asset; omission cannot become permission.
There was also a performance problem: a style selector made the cost of each diagram redraw rise about 16 times, and it was fixed by setting the attribute in code, with no visual difference.
Tests: green that does not know how to turn red
The tests use Node’s native runner, and the interface tests open a real Chromium. Without the browser, the suite fails, instead of passing without testing. On October 19, 2026 the main command recorded 1,371 tests, all passed, in about 22 minutes. On the same day, an earlier round had stopped with 8 failures before reaching that result.
Besides the suite, there are scenario batteries. The largest consolidates 485 scenarios and 18 minimum combinations, and it observed all 24 rule codes applicable to the reference substation. Inside the editor itself, the Test button runs one line’s cases on an isolated copy of the drawing; one of the examples gathers 140 cases.
The rule the project wrote for its own tests is the best lesson of the case: verification that does not know how to turn red is not green. Every new check has to be proved by mutation, deliberately breaking what it should catch and watching it fail.
Status: no acceptance, and written that way
On August 20, 2026, an internal release note declared itself a “Validated Release Candidate.” Later documentation contradicts that seal, and the author’s site kept repeating “completed and validated” until this revision. The real state, checked in the records:
- there were partial acceptances: the complete bench and the first pilot substation were accepted by the author on September 29, 2026;
- in the reassessment of October 16, 2026, the product was left as not approved;
- on October 20 the work was paused, with no product acceptance and no automatic continuation, and on October 25 the round underway closed as blocked, by the author’s decision;
- every local publication carries the seal “LOCAL / NOT VALIDATED,” and publication to the organization does not exist;
- hosting, institutional identity, and operational publication are left for later stages.
The project memory went so far as to describe a batch of models as “100% Complete and Validated”; today that batch is recorded as an unqualified candidate. Writing the state as it is is part of the product: a safety simulator that declares itself ready when it is not teaches the wrong thing.
What is still unresolved
- Product acceptance. It does not exist, and no version has been tagged in Git.
- Use in training. The repository records neither classes nor trained operators; the text does not claim a learning gain.
- Engine scope. Without power flow and without propagation between substations, the simulator teaches interlocking logic, not the complete electrical behavior of the grid.
- Line authoring. The rehearsal supports one circuit breaker per end and requires both ends on the same drawing.
- Suite time. Twenty-two minutes per round weigh on the change cycle.
Lessons that hold outside the substation
- Separate the dimensions of state. Position, energy, and authorization answer different questions.
- The unknown is a state, not a void. Mark it, propagate it, and block on it.
- The interface projects; the engine decides. A rule that exists only on the screen is not a rule.
- A refusal changes nothing. A denied operation cannot leave a trace on the plan.
- Test from the script of the person who operates, not from the code you just wrote.
- Prove that the check fails before trusting that it passes.
- A ready seal only with acceptance. Self-declaration ages badly.
Sources
- Code, documentation, architecture decisions, incidents, and test records: Switching Simulator repository in the version of October 27, 2026, read by the author; extract with the passages, the counting method, and the limits of the verification, verified on 2026-10-30.


