Internal Documentation
This section looks at the internals of EDPF, and how things work under the hood.
To start off, EDPF uses Tauri. This implies a Backend written in Rust, with a Frontend written with a Frontend technology. All Web Views defined by EDPF use React.
This overview might be a bit too simplified, and expressing it like this doesn’t do it justice. Let’s split up the overview into two — the Journal path, and the Plugin Loading and Asset Handling path.
Journal
Section titled “Journal”EDPF uses rster2002’s wonderful ed-journals crate to handle listening and emitting journal / status file changes.
These updates are then emitted using Tauri’s IPC capabilities. A plugin’s context is subscribed to the relevant event.
ed-journals provides features to listen towards changes on the entire Journal directory, to iterate over just one file, and to listen for just one file, among other things.
It allows you to parse each Log Item into a Rust Struct, but we don’t use it because the conversion from JSON → ed-journals → JSON results in changes.
In addition to that, reliance on the Structs means we require an EDPF update to handle newer files. Realisitically, Plugins would expect Journal items to not have been altered,
which is why we rely on the Raw family of listeners and iterators provided by ed_journals.
Event Watchdog Implementation
EDPF keeps a mapping of CMDR Name → Active Journal. It periodically checks for new Journals.
If a new Journal is written, we get the CMDR associated with it, evict the old reader (if any exists), and create a new ed_journals::logs::asynchronous::RawLiveLogFileReader. “Raw” here means that the Journal Item is not parsed to ed_journals’ structs, but instead remains a serde_json::Value (a generic JSON Object).
During instantiation we move into a separate Task, meaning we have multiple Readers running in parallel.
Each File Reader keeps its own Channel, in which it will push Event items as they come. This channel bufferes events, to send out events to all plugins in a batch. EDPF keeps an internal timer per CMDR that will delay and reset a timer after 100ms. To prevent constant events from blocking emits, there is a hard timer of 500ms.
If the timer triggers, the channel is drained, and all events are sent to the Frontend via Tauri’s Event Infrastructure using the journal_events name.
On the Frontend side, each Plugin Context has a listener subscribed to this event, assuming the plugin requested it. From this listener, the Plugin’s callback is invoked.
Note that the journal_events Message is unencrypted.
This is in contrast to other events like setting updates. The rationale here is that all Plugins have access to event updates anyways,
and these events will be the vast majority of events transmitted, meaning the encryption overhead is wildly unneccesary.
Plugin Lifecycle
Section titled “Plugin Lifecycle”Each Plugin in EDPF is set up to be Startable and Stoppable. Is the user deletes a Plugin or stops it from the settings, it’s Web Component is no longer run. Note that Plugins running has nothing to do with them being visible. If the User decides that a Plugin does not belong on the UI, the Web Component is still created, just hidden away.
This Page walks you through the process of Discovery to Startup, back to Shutdown.
Also, take a look at plugins::reconciler_utils.
The there-defined ReconcileAction is that is used as the backbone of Reconciliation.
Discovering Plugins (Adopting)
Section titled “Discovering Plugins (Adopting)”Every 30s, a background task is run to look at the user’s plugin folder.
Each folder that contains a valid manifest.json is considered.
If the Plugin is not known yet (using the plugin ID as a matcher), it is Adopted. There is no difference between Plugins being loaded at startup or during the Program’s runtime.
Adoption means that the Plugin is taken in the Program’s internal state. This allows for the HTTP Server to reference the ES-Module, meanining the Frontend can display the Settings and Main component.
If the Plugin was started beforehand, this Action will also Start the plugin.
Starting a Plugin
Section titled “Starting a Plugin”This is a brief State and you usually shouldn’t even see it.
The Plugin is started. This causes an Event to be sent from the Backend to the Frontend. The Frontend is instructed to Load the ES-Module and spawn the Web Component, pass it all the relevent information, hook up Journal Events, Push this File’s Events (if the Plugin desires), and notify the Main Component to move the Web Component to where it needs to be in the UI (or hidden).
Start Failed
Section titled “Start Failed”If the Web Component could not be mounted, we go into the Start Failed Phase. The Reconciler will retry to Start the Plugin in each loop over and over. The Settings-Page should contain a reason as to why the Reconcile has failed.
It can be one one of the following reasons:
- the Frontend ES-Module could not be imported (either because it doesn’t exist or is malformed)
- the ES-Module does not have a
defaultexport - the ES-Module’s
defaultexport is not a class definition which inheritsHTMLElement. - something failed registering the Web Component as a Custom Element
- Spawning an Instance of the
HTMLElementfailed. - The spawned instance does not contain an
initPluginMethod
Running
Section titled “Running”The Plugin has been loaded, the Web Component is active, the Plugin is running.
The Reconciler will still check every 30s. As part of that check the a hash is derived from the frontend Folder. Is that hash changes, a Restart is triggered.
Restart
Section titled “Restart”Basically stopping and starting up a Plugin. This is done either Manually via the Settings Panel, or automatically if the Hash of the frontend Folder changes.
This will unload the Component as described in Stop and then started up again as described in Starting
The Plugin is loaded and should be stopped. Is is removed from the UI. If the Plugin was part of the visible UI, a placeholder will be made in its place.
SyncInPlace
Section titled “SyncInPlace”This is a special reconciliation action that doesn’t do anything on the Frontend. It only modifies the Plugin’s internal state if it is disabled.
This gets EDPF to “forget” about the plugin. This action only happens if you delete a Plugin from the User Plugins Folder. Note that this does not delete the Plugin from the active list, meaning it will automatically start should it ever appear again; it also won’t delete the Plugin’s settings.