Improved logging to indicate which touchscreen slot is used during Mpc4TouchpanelController initialization. Updated error log to report processor.TouchscreenType for clearer diagnostics.
Updated Mpc4TouchpanelController constructor to assign _touchpanel from the first available slot among MPC4x102, MPC4x201, MPC4x301, or MPC4x302 touchscreen slots, improving compatibility with various MPC4 touchscreen models.
Replaced switch statement for touchscreen type selection with a single cast assignment to MPC3Basic. This streamlines initialization and removes explicit handling for each touchscreen type. Error logging is retained for failed casts.
Change _touchpanel type to MPC3Basic. Update constructor to select touchscreen slot by TouchscreenType using switch. Improve error logging with error level and detailed type info.
Introduced Mpc4TouchpanelController to manage touchpanel behavior for MPC4 class processors, including button initialization, feedback, and event handling. Updated ControlSystem.cs to detect MPC4 models, deserialize button configs, and register the controller with DeviceManager.
- ConnectedClientVersions now deep-copies each ConnectedClientVersionInfo
when snapshotting, so external callers can't mutate the internally
tracked, lock-protected instances (Copilot review).
- ShowInfo now prints "(not configured)" for an empty ExpectedAppVersion,
matching the match/mismatch calculation which already treats empty the
same as not-configured (Copilot review).
- GetPackageManifestRequestHandler.PopulatePackages filters out null
entries from the config-supplied packages list before processing, so a
malformed "packages": [null, ...] in user-edited config JSON degrades
gracefully instead of throwing (Copilot review).
Extend the /system/clientJoined handler to accept an optional
`appVersion` field reported by connecting UI clients (e.g. the
React app's build-time APP_VERSION), so Essentials can record and
validate what's actually running against the configured
versions.touchpanelWrapperApp version.
- Add ConnectedClientVersionInfo to capture clientId, roomKey,
touchpanelKey, reported/expected app version, and last-seen time.
- Add MobileControlSystemController.TrackClientAppVersion(), storing
results in a new thread-safe ConnectedClientVersions dictionary and
logging a warning on version mismatch.
- Expose ConnectedClientVersions as a public read-only property for
diagnostics.
- Surface reported vs. expected versions per client in the
`mobileinfo` console command output.
No wire protocol changes required; content is passed as-is over the
existing clientJoined message.
- Determine config version by presence of "system" and "template" nodes
instead of "versions", since a v2 config can also include "versions"
and was previously being skipped from merging as a result.
- Preserve the "versions" node after merging a v1 config, since
PortalConfigReader.MergeConfigs does not carry it forward.
WHAT / WHY
----------
GetPackageManifestRequestHandler previously identified a loaded plugin's
NuGet PackageId by fuzzy-matching AssemblyTitle/AssemblyName against the
config's packageId - a chain that silently fails for any plugin whose
AssemblyTitle/AssemblyName doesn't happen to match its PackageId (verified
against real shipped plugin DLLs; see FINDINGS-nuget-packageid-gaps.md).
epi-symetrix-dsp and epi-shure-mxa have already backported a
Directory.Build.props change that embeds
`<AssemblyMetadata Include="PackageId" Value="$(PackageId)" />`, giving
Essentials an unambiguous, authoritative PackageId via
AssemblyMetadataAttribute("PackageId", ...) instead of guessing. Validated
by building both plugins and inspecting the generated AssemblyInfo.cs.
Changes:
- src/Directory.Build.props: add the same AssemblyMetadata PackageId item,
so every Essentials-owned assembly (Core, Essentials, Devices.Common,
MobileControl, MobileControl.Messengers) now embeds its real PackageId
too - previously none of them did.
- GetPackageManifestRequestHandler.cs:
- PopulatePackages: read AssemblyMetadataAttribute("PackageId", ...) from
loaded plugin assemblies and use it as the first-priority match/identity
signal, ahead of the AssemblyTitle -> AssemblyName -> AssemblyName-minus-
".4Series" fallback chain. Loaded-but-unconfigured plugins that carry
this metadata now report a PackageId in the manifest instead of null.
- PopulateEssentials: replace the hardcoded "PepperDash.Essentials"
fallback (which matched none of the real PackageIds) with the reflected
value from PepperDash.Essentials.Core's own assembly metadata.
Fully backward compatible: plugins without the updated Directory.Build.props
(most existing epi-* repos today) fall through to the prior fallback chain
unchanged.
RECOMMENDATIONS - Essentials & sub-projects (this repo)
--------------------------------------------------------
- AssemblyName/AssemblyTitle drift from PackageId across sub-projects
(confirmed via generated AssemblyInfo.cs, not assumed):
PepperDash.Essentials.Core: PackageId "PepperDash.Essentials.Core"
vs AssemblyName "PepperDash_Essentials_Core"
PepperDash.Essentials.Devices.Common: PackageId "PepperDash.Essentials.Devices.Common"
vs AssemblyName "Essentials Devices Common"
PepperDash.Essentials.MobileControl: PackageId "PepperDash.Essentials.MobileControl"
vs AssemblyName "epi-essentials-mobile-control"
PepperDash.Essentials.MobileControl.Messengers: PackageId "...Messengers"
vs AssemblyName "mobile-control-messengers"
Only PepperDash.Essentials and PepperDash.Core happen to agree. Fixing
AssemblyName changes the physical .dll filename for existing consumers,
so this needs a deliberate, versioned decision - not bundled here.
- Once this ships and bakes for a release or two, consider deleting the
now-redundant "PepperDash.Essentials" hardcoded string entirely and the
Product/AssemblyTitle-based Name fallback, since AssemblyMetadata PackageId
supersedes both for any assembly built after this change.
RECOMMENDATIONS - EPI plugin repos (epi-*)
-------------------------------------------
- Backport `<AssemblyMetadata Include="PackageId" Value="$(PackageId)" />`
into every existing epi-* repo's src/Directory.Build.props (recommendation
E from FINDINGS-nuget-packageid-gaps.md). This is opt-in and additive -
repos that skip it keep working via the existing fallback chain, but gain
nothing until they backport it and cut a new release.
- Land the corresponding fix in EssentialsPluginTemplate
(src/Directory.Build.props + src/epi-make-model.4Series.csproj) so all
*new* plugin repos get this by default, and fix the template's own
AssemblyTitle/PackageId drift ("Plugin" vs "Plugins") while there.
- Already-published plugin versions can't be retroactively fixed - this only
takes effect on a plugin's next release after adopting the template change.
WORKFLOW RECOMMENDATIONS
-------------------------
- Extend workflow-templates' essentialsplugins-4Series-builds.yml "Check
Package Name" step to validate the built DLL's embedded
AssemblyMetadataAttribute("PackageId", ...) (and/or AssemblyTitle as a
fallback) against the repo-derived expected package name - today it only
compares the .nupkg filename, which would not have caught drift like
epi-display-samsung-mdc's AssemblyTitle mismatch.
- Sequence this after the template + per-repo backports have landed and
baked for a release cycle, otherwise it will fail CI for every epi-*
repo that hasn't picked up the Directory.Build.props change yet. Gate it
behind the existing bypassPackageCheck input for repos not yet ready.
New GET https://{ip}/cws/app{xx}/api/packageManifest route returning a JSON
package manifest (Essentials + plugins + user interfaces) shaped to hydrate
the vsce-essentials-version-manager extension's VersionsSnapshot object.
- EssentialsConfig.cs: add [JsonProperty("versions")] to EssentialsConfig.Versions
(was attribute-less); add NugetVersion.Name; NullValueHandling.Ignore on
PackageId/RepoUrl/Name.
- New GetPackageManifestRequestHandler: deep-copies the config's VersionData
(never mutates the live config object), then enriches it via reflection:
- essentials: version from Global.AssemblyVersion, repoUrl/name from the
RepositoryUrl AssemblyMetadata + AssemblyProduct of
PepperDash.Essentials.Core's own assembly (PluginLoader.EssentialsAssembly.Assembly
is null at runtime due to a pre-existing name-matching bug, so this route
reads its own loaded assembly instead), packageId from config or a constant.
- packages[]: merges PluginLoader.EssentialsPluginAssemblies (matched to config
packages by packageId via AssemblyTitle -> AssemblyName -> AssemblyName minus
a trailing .4Series suffix) with reflection supplying version and filling
missing repoUrl/name; unmatched loaded assemblies are emitted without a
packageId; configured-but-not-loaded packages pass through unchanged.
- userInterfaces/touchpanelWrapperApp are passed through from config as-is.
- Entries with no resolvable version are skipped (the extension's parser drops
entries whose version isn't a string).
- EssentialsWebApi.cs: register the new packageManifest route next to versions.
Build verified clean (Core + Essentials program, 0 errors). Not yet tested on
hardware.
Add touchpanelWrapperApp, userInterfaces, and repoUrl to VersionData config schema
VersionData/NugetVersion previously only modeled the essentials and
packages entries under the versions node. This adds support for the
touchpanelWrapperApp (single object) and userInterfaces (array) nodes,
plus a repoUrl property on NugetVersion so it round-trips instead of
being silently dropped, matching the schema produced by the
vsce-essentials-version-manager extension.