diff --git a/docs/ADOPTING-AT-ANOTHER-SITE.md b/docs/ADOPTING-AT-ANOTHER-SITE.md index e4e98e3..2ec363a 100644 --- a/docs/ADOPTING-AT-ANOTHER-SITE.md +++ b/docs/ADOPTING-AT-ANOTHER-SITE.md @@ -351,6 +351,69 @@ been uploaded for that surface, not that the PC is misconfigured. --- +--- + +## Part 3: bootstrapping GE-Enforce itself + +The two parts above assume the tools are already on the PC. GE-Enforce is what +puts them there and keeps them there - so the question is how GE-Enforce gets +onto a bay in the first place. Two paths are supported, and they are the two +that actually occur: imaging time, and a management plane such as Intune. + +Whichever you use, the end state is the same three things: + +1. The client files in `C:\Program Files\GE\Shopfloor`. +2. `HKLM:\SOFTWARE\GE\ShopDB` carrying `BaseUrl`, and `ApiToken` where the scope + is served over HTTPS. +3. A scheduled task running the enforce cycle as SYSTEM. + +### At imaging time (the usual path) + +The shop-floor imaging pipeline registers the enforce task as its last step, +once the PC type is known - the type is what decides which manifest scope the +bay enforces. Self-contained types are skipped deliberately: a display kiosk +gets its configuration at imaging time and no share, so registering an enforce +task on one would give it a cycle with nothing to do. + +If you are building your own imaging pipeline, the equivalent step is: copy the +engine into a runtime directory, write the PC type where the client can read it, +and register the task. That is what `Install-GEEnforce.ps1` does in one call. + +### Through Intune + +`Install-GEEnforce.ps1` is parameterised for exactly this, and this is how the +display cohort is deployed today - those PCs are Entra-joined, have no file +share, and fetch their manifest over HTTPS: + +```powershell +.\Install-GEEnforce.ps1 ` + -PCType 'gea-shopfloor-common' ` + -ShopdbUrl 'https://shopdb.example.net' ` + -ShopdbToken 'shopdb_pat_REPLACE_WITH_YOUR_TOKEN' +``` + +Package it as a Win32 app with the client files. Detection rule: the scheduled +task exists **and** `HKLM:\SOFTWARE\GE\ShopDB\BaseUrl` matches your server, so a +PC that was imaged for another site is repaired rather than skipped. + +The token is a managed service token scoped to the GE-Enforce fetch scope, not +an admin credential. Scope it that way and a token read off a bay buys the +reader a manifest they could have read anyway. + +### With DSC (Machine Configuration) + +There is no shipped DSC configuration, but nothing about the end state resists +one - it is two registry values, a set of files and a scheduled task, which is +ordinary DSC territory. Express it the same way as the reporter example above: +`Registry` resources for `BaseUrl` and `ApiToken`, `File` resources for the +client, and a `Script` resource that registers the task. + +One caution. DSC converges state on a schedule and GE-Enforce is itself a +convergence loop, so running both against the same PC means two things fighting +to own the same configuration. Use DSC to install and configure the client, and +let GE-Enforce own everything downstream of that. Deciding which tool owns what +is the whole job; splitting it by layer is what keeps it answerable. + ## Which path to choose | You have | Use | diff --git a/docs/EVENTSAVER.md b/docs/EVENTSAVER.md new file mode 100644 index 0000000..6ced26b --- /dev/null +++ b/docs/EVENTSAVER.md @@ -0,0 +1,132 @@ +# EventSaver + +A rotating-image screensaver for shop-floor PCs. Idle bays show the slides +managed in ShopDB, so a safety notice or a shift message reaches the floor +without anyone walking to each machine. + +It is a small C# WinForms screensaver (`.scr`), not part of the ShopDB server. +It is documented here because ShopDB serves its content and because a site +adopting the fleet tools needs to know how it is configured. + +For how to deploy it at a new site, see +[ADOPTING-AT-ANOTHER-SITE.md](ADOPTING-AT-ANOTHER-SITE.md). This page is what it +is, how it decides what to show, and how to diagnose it. + +## Two sources, one of them preferred + +EventSaver reads `EventSaver.ini` from the directory it lives in, **on every +launch**. Retargeting it never needs a recompile. + +```ini +# HTTP mode: pull the playlist from ShopDB and cache it locally. +url=https://shopdb.example.net/api/slides/feed?surface=shopfloor + +# Folder mode: used only when url is blank. SMB or local path. +# folder=\\fileserver\shopfloor\tv + +interval=10 # seconds per image, unless a slide carries its own time +shuffle=0 # 1 = random order, 0 = ordered +fadems=600 # crossfade length in ms, reserved (v1 hard-cuts) +``` + +**HTTP mode is the one to use.** Slides are managed in ShopDB (Slides), the +feed is public so the screensaver needs no credentials, and each PC caches what +it fetched under its own `LocalApplicationData`. A bay that cannot reach the +server keeps showing the last set it saw instead of going black - which matters, +because the failure would otherwise be visible to the whole floor. + +**Folder mode** predates the server and stays as a fallback for a site with no +ShopDB instance yet, or for content nobody wants in the database. + +> If `EventSaver.ini` is missing, or both `url` and `folder` are blank, the +> binary falls back to a path compiled into `EventSaver.cs` - and that path +> belongs to the site it was first built for. Ship the ini. A missing ini is not +> a neutral default. + +## What decides the running order + +| Situation | Behaviour | +|---|---| +| No `order.txt` | Every image shows, sorted by filename. Number them `001_`, `002_` to sequence. | +| `shuffle=1` | Random order, ignored when a playlist is present. | +| `order.txt` present | ONLY the listed files show, in the listed order, looped. | + +`order.txt` is the strict-sequence option and is the whole playlist - a file not +listed does not appear. Per-slide duration is appended with a pipe: + +``` +# Lines starting with # or ; are ignored. +001_welcome.jpg +002_safety_week.jpg|15 +003_quality_board.jpg +``` + +In HTTP mode you do not write `order.txt` by hand: ShopDB's feed carries the +order and each slide's own seconds, and EventSaver writes the equivalent +`order.txt` into its cache so the same playlist logic runs either way. + +A running screensaver rescans about every 30 seconds, so a slide change reaches +the floor without touching any PC. + +## Command-line switches + +Windows calls a screensaver with these, and EventSaver answers all three: + +| Switch | Meaning | +|---|---| +| `/s` | Show fullscreen. What Windows uses on idle. | +| `/c` | Configuration. Reports the resolved source and cache directory, and points at the ini. | +| `/p ` | Preview pane. Deliberately a no-op - it keeps the Settings dialog happy without drawing a thumbnail nobody looks at. | + +`EventSaver.scr /c` is the first diagnostic: it says which source it resolved +and where it is caching, which answers most "why is it showing the wrong thing" +questions immediately. + +## Where it should and should not run + +A screensaver over a live display is a defect, not a feature. The reference +fleet excludes: + +- **Kiosks and lobby displays**, which show a live dashboard. +- **Metrology bays** - CMM, Genspect, Keyence, wax-trace - where an operator + watches a measurement run. +- **Bays that sleep**, where the screensaver never wins anyway. + +Targeting is per PC type in the GE-Enforce manifest, or by group assignment in +Intune. Decide it deliberately: the cost of getting it wrong is a screensaver +covering something someone needed to see. + +## Building it + +No SDK required - it compiles with the in-box .NET Framework compiler on any +Windows 10 or 11 machine: + +``` +C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe ^ + /target:winexe /out:EventSaver.scr ^ + /reference:System.dll,System.Drawing.dll,System.Windows.Forms.dll ^ + EventSaver.cs +``` + +Ship `EventSaver.scr` and `EventSaver.ini` together. A `.scr` is an executable +with a different extension, so it goes to `C:\Windows\System32\`. + +## Diagnosing it + +| Symptom | Cause to check first | +|---|---| +| Blank screen, no images | Source unreachable and cache empty. Run `/c` and open the `url` in a browser on that PC. | +| Old slides only | Feed reachable at first run, not since. The cache is doing its job; fix the network path. | +| Wrong content | Pointing at another surface, or another site's server. `/c` reports which. | +| Local ini edits revert | Correct behaviour if the config is enforced by hash. Edit the copy on the share and update its `DetectionValue`. | +| Screensaver never starts | Per-user setting missing. Check `HKCU:\Control Panel\Desktop` for `SCRNSAVE.EXE`, `ScreenSaveActive` and `ScreenSaveTimeOut`. | + +The feed is a plain public endpoint, so it can always be checked from the PC +itself: + +``` +https://shopdb.example.net/api/slides/feed?surface=shopfloor +``` + +An empty `slides` array means no slides have been uploaded for that surface - +the PC is configured correctly and there is nothing to show.