Architecture & System Context
Prerequisite: Chapter 00 read (repo map, eight projects, the M/V/Glob terminology). Goal: by the end you can sketch Flux’s runtime architecture on a whiteboard, name the major cross-cutting subsystems (settings, rendering, undo, persistence, hub), and explain why certain design choices were made. Reading time: 60—90 minutes.
1. The big picture in three diagrams
1.1 Static module dependencies
┌───────────────────────────────────┐
│ Flux.exe │
│ (entry point — Flux.App) │
└─────────┬─────────────────────────┘
│
▼
┌───────────────────────────────────┐
│ Flux.UI │
│ Globs (controllers) + WPF │
└─────────┬─────────────────────────┘
│
▼
┌───────────────────────────────────┐ ┌───────────┐
│ Flux.Core │◄───┤ Flux.API │
│ Models + VModels + domain logic │ │ (3rd-party│
└─────────┬─────────────────────────┘ │ facade) │
│ └───────────┘
▼
┌───────────────────────────────────┐
│ Flux.Base │
│ Geo, Mesh, Pline, Lux (render), │
│ Undo, IO primitives, FText │
└─────────┬─────────────────────────┘
│
▼
┌───────────────────────────────────┐
│ Flux.Hula64.dll (C++/CLI) │
│ wraps HOOPS for 3D import │
└───────────────────────────────────┘
Side projects (bind to Core):
Flux.Test ─► Core Flux.Migrate ─► Core Flux.Console ─► Core
1.2 Runtime layers (a single Flux process)
┌───────────────────────────────────────────────────────────────────┐
│ Process: Flux.exe │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Main window (App) │ │
│ │ Tabs ─ each tab hosts one open Part │ │
│ │ Selection state, Undo stack, Glob panel │ │
│ └────────────┬─────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Open Parts (Part.Active : List<Part>) │ │
│ │ Part.Current ─ the active one │ │
│ │ each Part has Dwg / Model / BendTech / FoldTech / ... │ │
│ └────────────┬─────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Cross-cutting services (singletons / static gateways) │ │
│ │ Hub — global glue / dependency injection │ │
│ │ Sheaf — cascaded settings (per-machine, per-tech) │ │
│ │ FX.Notes — persistent user notes (recent items) │ │
│ │ Lux — rendering pipeline (OpenGL) │ │
│ │ Lib — wad-resource loader │ │
│ │ Trek — out-of-process MetaCAM bridge │ │
│ └──────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────┘
│
▼ (file I/O, no network in Flux core)
X:\Data, X:\Wad, X:\Resources, user data folder
1.3 Pipeline for a typical CAM job
user opens 3D file ─► IO (DXFReader / GeoReader / HoopsReader-via-Hula+HOOPS)
│
▼
Part.Model (B-Rep / mesh / E3 entities)
│
┌──────────────┼──────────────┐
▼ ▼ ▼
BendTech FlatTech FoldTech
(press brake) (laser/punch) (panel bender)
│ │ │
▼ ▼ ▼
BendTooler LaserAuto/ FoldAuto
(auto-tool) AutoRouter (sequencing)
│ │ │
└──────────────┼──────────────┘
▼
Postprocessor
(C# writer in Src\API\Bend\Post\,
picked by post name from BMachine.Post)
│
▼
NC code file
2. The five layers explained
2.1 Flux.Base — foundations
Flux.Base is the CAD/CAM-agnostic foundation. It contains:
| Folder | What |
|---|---|
|
Point/Vector/Matrix, |
|
3D triangle mesh primitives ( |
|
Polyline-specific helpers |
|
The OpenGL rendering layer — |
|
Au/Gold binary serialization runtime |
|
Shared value types and enums |
|
Undo/redo infrastructure |
|
Low-level IO primitives (streams, encoding) |
|
Rich text / FTex text rendering |
|
Lower-level OpenGL bindings |
|
Generic 3D math beyond |
Rule of thumb: if a class lives in Flux.Base, it should have no awareness of bending, laser cutting, or any CAM concept. If you find such leakage, that’s a smell.
2.2 Flux.Core — the CAM engine
This is where the domain logic lives. The folder structure (16 folders) was listed in chapter 00 §4. Key points:
-
Models and VModels live together.
BendOp(model) andBendOpVM(view) sit next to each other. This is not an accident — see §3 below. -
Part(Src\Core\Part\Part.cs) is the central job aggregate, holdingModel,Dwg,BendTech,FoldTech,CutTech, machine references, etc. -
Sheaf(Src\Core\Settings\Sheaf.cs) is a cascaded settings dictionary: every domain object that has settings implementsISheafSite(stringSHName, dictionary, parent fallback). -
Hub(Src\Core\Hub.cs) is the central dependency-injection point. It exposes staticFunc<>/Action<>slots (e.g.BigChooser,ClosePartByJobID,APIWrapper) that are filled in at startup by the App or API layer. This is the replacement for the old "Hub whiteboard" hack and is now the official cross-layer wiring mechanism.
2.3 Flux.UI — the controllers
Flux.UI provides:
-
Globs — see §3.
-
WPF dialogs and editors —
TurretEditor,SheafEditor,LaserDBEditor,MachineEditor, etc. -
Agents — input handlers per domain (
BendAgent,LaserAgent). -
Sheets — list/grid views (
BendSheet).
Convention: a domain’s UI files match the Src\UI\<domain>\<Domain>*UI.cs pattern. Files in this layer can reference Core freely; the reverse (Core → UI) is forbidden by the module dependency.
2.4 Flux.App — the shell
This is the top of the stack and the actual Flux.exe content. Responsibilities:
-
Main window, tabs, status bar.
-
Selection state, undo lifecycle, glob panel.
-
Document lifecycle (open/save/close
Part). -
Loading the right
Flux.flavorand feature bits at startup. -
Wiring up
Hubslots so Core/UI can call into App services.
2.5 Flux.API — the controlled exposure
Flux.API is a narrow facade for 3rd-party automation. It:
-
Mirrors a sub-set of Core types behind API-level wrappers.
-
Bundles its dependencies (Core, Base, Hula) so consumers see only
Flux.API.dll. -
Has its own folder structure (
Src\API\Job\,Src\API\Cut\,Src\API\Part\, …) mirroring Core.
If you’re ever asked "can a 3rd party do X?", the answer is in Src\API\ — not in Core.
3. Why model and view live together
This is the most non-obvious decision in Flux’s architecture, and it is intentional. The full rationale is in Notes\Dev\1002.BuildingFlux.ftx lines 31—61. Short version:
-
Flux objects are CAD/CAM objects tightly coupled to their visual representation. An
E3Planeis meaningless without a way to render it. -
The earlier architecture put VMs in a separate higher layer ("UI layer renders, Core only knows data"). This forced ugly workarounds for any operation that needed rendering — every setup-sheet generation, every thumbnail in an FX file, every PDF report. The result was the "Hub whiteboard": a global object bag used to fake the missing dependency.
-
The trade-off was reversed. Today, models and VMs sit in the same DLL (
Flux.Core), and the rendering layer (Lux) is inFlux.Baseso both can see it. Globs (controllers) remain separate inFlux.UI, because they really do need WPF/keyboard/mouse and shouldn’t be dragged into headless usage (Engine flavor, batch jobs).
Practical consequence: when adding a feature, expect to touch model + VModel + Glob. When debugging a rendering issue, you’ll be in
Flux.Core, notFlux.UI. When debugging an interaction issue (mouse, keyboard, properties panel), you’ll be inFlux.UI.
4. The Glob system — controllers in detail
A Glob is a controller object that:
-
Wraps a domain object (
new BendOpGlob (op)). -
Exposes properties via
GElement(abstract) →PanelMakertranslates to WPF. -
Exposes commands (e.g.
BendOpGlob.AddRegripinSrc\UI\Bend\BendOpUI.csline 22). -
Supports composites — when multiple objects are selected, the system constructs a composite glob showing only the shared properties, and operations apply to all wrapped objects.
A glob is decorated with attributes like:
[Glob (Draw = true, Mouse = EMouse.LDown)]
public class BendOpGlob : Glob {
public BendOpGlob (BendOp op) : base (op) { ... }
}
Draw = true means the glob has its own drawing behavior; Mouse = EMouse.LDown lets it consume mouse events. The full attribute model is in Src\UI\Glob.cs.
5. The Sheaf — cascaded settings everywhere
Every domain object that has settings implements ISheafSite:
public interface ISheafSite {
string SHName { get; } // e.g. "Machine 135" or "Bend 3"
Dictionary<SH, object> SHDict { get; set; } // local settings (may be null)
ISheafSite SHParent { get; } // fallback site
}
Reading a setting walks SHDict → SHParent.SHDict → … until found. This means:
-
Machine-level defaults are inherited by all bend techs that target the machine.
-
Per-tech overrides mask machine defaults.
-
Per-op overrides mask per-tech.
BendOp, BendTech, BendMachine, Part, and many others all implement ISheafSite. The keys are values of the SH enum (a domain-wide closed set of setting identifiers).
Why this matters when reading code: you’ll often see
Settings.RamVBend,Settings.BGTransition, etc. accessed on aBendOp. These are resolved through the sheaf cascade — the value may have been set on the parent machine, not the op itself.
6. The Hub — wiring layers without circular dependencies
Src\Core\Hub.cs defines a static class with public delegate slots:
public static Func<string, string, IList<string>, int, bool, object, int> BigChooser;
public static Action CloseBlankMainWindow;
public static Func<string, bool, int> ClosePartByJobID;
// ... and many more
At startup, Flux.App (and optionally Flux.API) fills in these slots with concrete implementations. Core and UI then invoke them. This is dependency injection done the simple way — no DI container.
The pattern matters because:
-
It lets Core call back into App without a project reference cycle.
-
It cleanly handles the headless case (Engine flavor):
Hub.BigChooseris simplynull, and Core falls back to a default.
When you see Hub.ClosePartByJobID?.Invoke (...) somewhere in Core, that’s the pattern in action.
7. Rendering — the Lux pipeline
All drawing flows through Src\Base\Lux\:
| File | Role |
|---|---|
|
Static entry point — |
|
Lower-level draw primitives. |
|
The hosting WPF panel. |
|
The scene graph, manages live |
|
Base class for view-models. |
|
Scene-graph node — geometry, transform, or mood override. |
The lifecycle of a VModel
-
Construction. A VModel wraps a domain object (
new BendMountVM (mount)). -
Plug into a screen. When the part is opened in a tab, the screen visits the model and calls
VModel.Draw(). At first draw, the VM gets a uniqueIdandSGNodes are created. -
Live state. While
Id != 0, the VM is "live" — re-renders go through cached scene-graph nodes. State changes setDirtyflags and triggerLux.Redraw. -
Detach. When the part closes, scene-graph resources are released.
Layers (ELayer.BendMount, ELayer.LaserCut, …) and moods (EMood.Selected, EMood.Normal) provide global filtering and styling.
Why this matters for debugging
If a bend setup looks wrong on screen, the bug is never in WPF — it’s in the corresponding *VM.Draw(). Set a breakpoint there, and inspect what Lux.DrawMesh is asked to render.
8. Persistence model
| Format | Used for | Where |
|---|---|---|
Curl |
Machine definitions, tool catalogs, settings |
|
Au / Gold |
Compact binary for |
|
FX |
Native Flux file (zipped Curl/Au + thumbnail) |
mentioned in 1002 |
DXF / DWG |
2D drawing exchange |
|
GEO |
Trumpf geometry exchange |
|
HOOPS-driven 3D |
CATIA / Parasolid / ACIS / Pro-E / STEP via HOOPS |
|
STL |
Mesh exchange |
|
BMC |
Trumpf machine format |
|
DLD |
Trumpf NC format |
|
[PostLoad] — the migration hook
Au-deserialized objects can declare a [PostLoad] method that runs after deserialization. It is the standard place to migrate older versions of the same object (AuType.ObjVersion tells you which version came in). Example from BendSetup.cs:
[PostLoad]
void PostLoad () {
mMounts.RemoveIf (a => a.Tool == null);
mMounts.Changed += MountsChanged;
if (mClampPos != null && AuType.ObjVersion < 4 && BMachine != null) {
// forward-migrate older clamp positions
}
}
When you bump a domain class in a way that changes the on-disk shape, you almost certainly need a [PostLoad] migration.
9. Threading model
Flux is predominantly single-threaded UI + opportunistic worker threads. The common patterns:
-
UI thread. Owns the main window, all WPF, all Lux rendering. The vast majority of domain code runs here.
-
Worker tasks. Long operations (auto-tooler, full simulation, large imports) may be
Task.Run'd, but their results must be marshalled back to the UI thread before mutating live state. -
Static state.
Part.Current,Part.Active,BendMachine.Default, and theHubslots are static. Treat them as UI-thread-only unless documented otherwise. -
Cancellation. Long-running operations check
Part.Flags & Part.EFlags.AbortTooling(seeBendTooler.DoTooling) — there is noCancellationTokenplumbing throughout.
Practical rule: do not introduce
Task.Runlightly. Most "concurrency bugs" in Flux are actually re-entrancy bugs (two property setters racing on the same domain object during user interaction).
10. Undo / redo
Src\Base\Undo\ provides the undo infrastructure:
-
Each user-visible mutation is wrapped in an undo unit.
-
Globs are the canonical place to register undos (the controller knows the user intent).
-
A part’s undo stack is part-scoped — switching tabs switches stacks.
When you see code like BA.Set (ref field, value), the BA (an internal helper) is also responsible for change notification and undo bookkeeping. It is _not a plain field assignment.
11. Errors, logging, and "FX"
A small but pervasive utility class is FX (in Flux.Base). It wraps:
-
FX.Out (EOut.Info, msgId, args)— log output with a numeric message ID. -
FX.Notes— a persistent settings store (key-value, used for "recent X"). -
FX.Testing— a global flag set when running under Flit. -
FX.LoadTestPart,FX.TestFile,FX.TmpTestFile— test helpers. -
FX.CopyFields— reflection-based shallow-copy helper used in shallow-copy constructors.
Two recurring exception types:
-
CodingError— internal invariant violation. Should never fire in production. Carries a numeric ID for grep-ability. -
EResult— a result enum used by methods that can fail (EResult.OK, plus various failure modes). Preferred over throwing for predictable failure paths.
Notes\Tech\2015.MessageIDs.ftx documents the message-ID convention.
12. Configuration, flavors, and feature bits at runtime
Startup sequence (simplified):
-
Flux.exestarts (entry inFlux.App). -
Reads
Flux.flavornext to the EXE → picks aEFlavorvalue. -
Loads feature bits from the flavor (e.g.
Bend,Laser,Punch,Trumpf). -
Validates licensing (Aegis or Trumpf WIBU/Sentinel, depending on flavor).
-
Locates the data folder (
FLUXDATAenv var on dev machines,C:\ProgramData\…on installed flavors). -
Loads the wad (
FLUXWADenv var orFlux.wadnext to the EXE). -
Bootstraps
Hubslots, opens the main window. -
Restores last-open documents (Engine flavor: skips UI bootstrap entirely).
Code paths to inspect:
-
Solution startup project:
Flux.App→ main entry typically namedApp.csor similar. -
Notes\Tech\2013.FluxFlavors.ftxfor the feature-bit table. -
Notes\Tech\2009.FluxConfiguration.ftxfor configuration.
13. Cross-cutting modules at a glance
| Cross-cutting | Where | Touchpoints |
|---|---|---|
Settings (Sheaf) |
|
Every domain object via |
Hub (DI) |
|
App fills slots; Core/UI invoke them |
Rendering (Lux) |
|
Every |
Undo |
|
Globs and |
Persistence (Au/Curl) |
|
All persisted models |
IO formats |
|
Per-format Reader/Writer |
Licensing |
|
App startup |
Flux.wad |
|
Resource loader at startup |
Logging / messages |
|
Pervasive |
14. The architecture’s three pain points (be aware)
-
The dual crash engines in Bend (
BendOpCrash.csvs.NBendCrash.cs) — both alive, both ~70 KB. A migration in flight; treat with care. -
partialclasses spanning many files.BendOp(210 KB) is one example;BendTechandPartare similar. AGrepfor a method name will lead you to the right partial — but the whole class lives across files. -
Static singletons (
Part.Current,BendMachine.Default,Hub.).* These are not unit-testable in isolation. The Flit framework works around it by settingBA.Testing = trueand substituting fixtures —BendMachine.Defaultreturns machine 99 in test mode for that reason.
15. Self-check questions
After this chapter you should be able to answer all of these without looking back. If not, re-read the relevant section.
-
Why are model and VModel in the same assembly? (§3)
-
How does Core invoke a UI dialog without a project reference to UI? (§6 — Hub)
-
What does
ISheafSite.SHParentgive you, in one sentence? (§5) -
Where does
VModel.Draw()actually draw to? (§7) -
If a bug only appears on Trumpf machines, which two files are likely involved? (§4 of chapter 02 —
BendCalcswitch viaIsTrumpf) -
How does the Engine flavor (no UI) avoid bootstrapping WPF? (§6 + §12 — null Hub slots, no main window)
-
After deserializing an old
BendSetup, how does it forward-migrate? (§8 —[PostLoad]) -
What happens when the user has multiple bends selected and edits one property? (§4 — composite Glob)
16. Reading order for chapter 01
If you’re reading these notes for the first time, do this in order:
-
Notes\Dev\1002.BuildingFlux.ftxlines 31—95 (the architecture rationale). -
Src\Core\Hub.cs— skim, see the slot pattern. -
Src\Core\Settings\Sheaf.cslines 1—80 — the cascade. -
Src\Base\Lux\VModel.cslines 1—80 — the render base. -
Src\Core\Part\Part.cslines 1—80 — the part aggregate.
That’s about 30 minutes of focused reading and gives you the spine of the system.
17. If you take just one thing from this chapter
Flux is built around a
Partaggregate that owns multiple*Techprojections (BendTech, FoldTech, CutTech). Cross-cutting state is reached throughHub(DI) andSheaf(settings). Rendering isLux. Once you internalize those four words — Part, Tech, Hub, Sheaf — the whole codebase becomes navigable.
Last updated: 2026-05-04. Verified against Src\Core\Hub.cs, Src\Core\Settings\Sheaf.cs, Src\Base\Lux\VModel.cs, Src\Core\Part\Part.cs, and Notes 1002/1003/1005/2013/2015.