diff --git a/src/PepperDash.Essentials.MobileControl/ConnectedClientVersionInfo.cs b/src/PepperDash.Essentials.MobileControl/ConnectedClientVersionInfo.cs
deleted file mode 100644
index 68f5e36e..00000000
--- a/src/PepperDash.Essentials.MobileControl/ConnectedClientVersionInfo.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System;
-using Newtonsoft.Json;
-
-namespace PepperDash.Essentials
-{
- ///
- /// Represents the version information reported by a connected Mobile Control UI client
- ///
- public class ConnectedClientVersionInfo
- {
- ///
- /// Gets or sets the client id
- ///
- [JsonProperty("clientId")]
- public string ClientId { get; set; }
-
- ///
- /// Gets or sets the room key the client joined
- ///
- [JsonProperty("roomKey")]
- public string RoomKey { get; set; }
-
- ///
- /// Gets or sets the touchpanel key the client joined as, if any
- ///
- [JsonProperty("touchpanelKey")]
- public string TouchpanelKey { get; set; }
-
- ///
- /// Gets or sets the app version reported by the client (e.g. the React app's build-time APP_VERSION)
- ///
- [JsonProperty("appVersion")]
- public string AppVersion { get; set; }
-
- ///
- /// Gets or sets the expected app version from the system config's versions.touchpanelWrapperApp, if configured
- ///
- [JsonProperty("expectedAppVersion")]
- public string ExpectedAppVersion { get; set; }
-
- ///
- /// Gets or sets the UTC time the client last reported this version
- ///
- [JsonProperty("lastSeen")]
- public DateTime LastSeen { get; set; }
-
- ///
- /// Returns a copy of this instance, safe for callers outside the owning lock to hold/mutate
- /// without affecting the internally tracked instance
- ///
- public ConnectedClientVersionInfo Clone() => new ConnectedClientVersionInfo
- {
- ClientId = ClientId,
- RoomKey = RoomKey,
- TouchpanelKey = TouchpanelKey,
- AppVersion = AppVersion,
- ExpectedAppVersion = ExpectedAppVersion,
- LastSeen = LastSeen
- };
- }
-}
diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs
index 8de9d9eb..ea3b02d1 100644
--- a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs
+++ b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs
@@ -58,32 +58,11 @@ namespace PepperDash.Essentials
private readonly Dictionary _defaultMessengers =
new Dictionary();
- private readonly Dictionary _connectedClientVersions =
- new Dictionary(StringComparer.InvariantCultureIgnoreCase);
-
- private readonly object _connectedClientVersionsLock = new object();
-
///
/// Get the custom messengers
///
public ReadOnlyDictionary Messengers => new ReadOnlyDictionary(_messengers);
- ///
- /// Gets the most recently reported UI app version for each connected client, keyed by clientId
- ///
- public ReadOnlyDictionary ConnectedClientVersions
- {
- get
- {
- lock (_connectedClientVersionsLock)
- {
- return new ReadOnlyDictionary(
- _connectedClientVersions.ToDictionary(kv => kv.Key, kv => kv.Value.Clone())
- );
- }
- }
- }
-
///
/// Get the default messengers
///
@@ -1028,27 +1007,13 @@ namespace PepperDash.Essentials
);
}
- var connectedClientVersions = ConnectedClientVersions;
+ var expectedAppVersion = ConfigReader.ConfigObject?.Versions?.TouchpanelWrapperApp?.Version;
+ var userAppPath = Global.FilePathPrefix + "mcUserApp" + Global.DirectorySeparator;
+ var versionCheck = TouchpanelWrapperAppVersionChecker.CheckDeployedVersion(userAppPath, expectedAppVersion);
- if (connectedClientVersions.Count == 0)
- {
- CrestronConsole.ConsoleCommandResponse("\r\nUI Client App Versions: None reported yet\r\n");
- }
- else
- {
- CrestronConsole.ConsoleCommandResponse("\r\nUI Client App Versions:\r\n");
- foreach (var kv in connectedClientVersions)
- {
- var v = kv.Value;
- var match = string.IsNullOrEmpty(v.ExpectedAppVersion) || string.Equals(v.ExpectedAppVersion, v.AppVersion, StringComparison.OrdinalIgnoreCase);
-
- CrestronConsole.ConsoleCommandResponse(
- $" Client: {v.ClientId} Touchpanel: {v.TouchpanelKey} Room: {v.RoomKey}\r\n" +
- $" Reported: {v.AppVersion} Expected: {(string.IsNullOrEmpty(v.ExpectedAppVersion) ? "(not configured)" : v.ExpectedAppVersion)} Match: {(match ? "Yes" : "NO - MISMATCH")}\r\n" +
- $" Last Seen (UTC): {v.LastSeen:yyyy-MM-dd HH:mm:ss}\r\n"
- );
- }
- }
+ CrestronConsole.ConsoleCommandResponse(
+ $"\r\nUI Wrapper App Deployed Version Check:\r\n {versionCheck.Summary}\r\n"
+ );
}
///
@@ -1445,8 +1410,6 @@ namespace PepperDash.Essentials
var roomKey = content["roomKey"].Value();
var touchpanelKey = content.SelectToken("touchpanelKey");
- TrackClientAppVersion(clientId, roomKey, touchpanelKey?.Value(), content.SelectToken("appVersion")?.Value());
-
if (_roomCombiner == null)
{
var message = new MobileControlMessage
@@ -1518,50 +1481,6 @@ namespace PepperDash.Essentials
SendTouchpanelKey(clientId, touchpanelKey);
}
- ///
- /// Records the app version reported by a connecting UI client (e.g. the mobile control React app's
- /// build-time APP_VERSION) and compares it against the configured versions.touchpanelWrapperApp version.
- ///
- private void TrackClientAppVersion(string clientId, string roomKey, string touchpanelKey, string appVersion)
- {
- if (string.IsNullOrEmpty(appVersion))
- {
- return;
- }
-
- var expectedVersion = ConfigReader.ConfigObject?.Versions?.TouchpanelWrapperApp?.Version;
-
- var info = new ConnectedClientVersionInfo
- {
- ClientId = clientId,
- RoomKey = roomKey,
- TouchpanelKey = touchpanelKey,
- AppVersion = appVersion,
- ExpectedAppVersion = expectedVersion,
- LastSeen = DateTime.UtcNow
- };
-
- lock (_connectedClientVersionsLock)
- {
- _connectedClientVersions[clientId] = info;
- }
-
- if (!string.IsNullOrEmpty(expectedVersion) && !string.Equals(expectedVersion, appVersion, StringComparison.OrdinalIgnoreCase))
- {
- this.LogWarning(
- "Client {clientId} (touchpanel {touchpanelKey}) reported UI app version {appVersion}, which does not match configured versions.touchpanelWrapperApp version {expectedVersion}",
- clientId, touchpanelKey, appVersion, expectedVersion
- );
- }
- else
- {
- this.LogVerbose(
- "Client {clientId} (touchpanel {touchpanelKey}) reported UI app version {appVersion}",
- clientId, touchpanelKey, appVersion
- );
- }
- }
-
private void SendTouchpanelKey(string clientId, JToken touchpanelKeyToken)
{
if (touchpanelKeyToken == null)
diff --git a/src/PepperDash.Essentials.MobileControl/TouchpanelWrapperAppVersionChecker.cs b/src/PepperDash.Essentials.MobileControl/TouchpanelWrapperAppVersionChecker.cs
new file mode 100644
index 00000000..1368b788
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl/TouchpanelWrapperAppVersionChecker.cs
@@ -0,0 +1,141 @@
+using System;
+using System.IO;
+using System.Linq;
+
+namespace PepperDash.Essentials
+{
+ ///
+ /// Determines whether the touchpanel wrapper app (the mobile control React app) deployed to this
+ /// processor's mcUserApp folder matches the version configured in the system config's
+ /// versions.touchpanelWrapperApp.
+ ///
+ ///
+ /// This reads the app's built .js/.html files directly from disk, so unlike the previous
+ /// client-self-reported-version approach, it does not depend on any UI client being connected and
+ /// does not accumulate per-client state over the life of the program.
+ ///
+ public static class TouchpanelWrapperAppVersionChecker
+ {
+ private static readonly string[] SearchPatterns = { "*.js", "*.html" };
+
+ ///
+ /// Scans the deployed touchpanel wrapper app's .js/.html files under
+ /// for the literal string (the version build tooling bakes
+ /// into the app at build time).
+ ///
+ /// The path to the deployed mcUserApp folder
+ /// The expected version, from config's versions.touchpanelWrapperApp.version
+ public static TouchpanelWrapperAppVersionCheckResult CheckDeployedVersion(string appPath, string expectedVersion)
+ {
+ if (string.IsNullOrEmpty(expectedVersion))
+ {
+ return TouchpanelWrapperAppVersionCheckResult.NotConfigured(appPath);
+ }
+
+ if (string.IsNullOrEmpty(appPath) || !Directory.Exists(appPath))
+ {
+ return TouchpanelWrapperAppVersionCheckResult.AppNotDeployed(appPath, expectedVersion);
+ }
+
+ try
+ {
+ var files = SearchPatterns
+ .SelectMany(pattern => Directory.GetFiles(appPath, pattern, SearchOption.AllDirectories))
+ .ToList();
+
+ if (files.Count == 0)
+ {
+ return TouchpanelWrapperAppVersionCheckResult.AppNotDeployed(appPath, expectedVersion);
+ }
+
+ foreach (var file in files)
+ {
+ var contents = File.ReadAllText(file);
+ if (contents.IndexOf(expectedVersion, StringComparison.OrdinalIgnoreCase) >= 0)
+ {
+ return TouchpanelWrapperAppVersionCheckResult.Match(appPath, expectedVersion, file);
+ }
+ }
+
+ return TouchpanelWrapperAppVersionCheckResult.Mismatch(appPath, expectedVersion, files.Count);
+ }
+ catch (Exception ex)
+ {
+ return TouchpanelWrapperAppVersionCheckResult.Error(appPath, expectedVersion, ex.Message);
+ }
+ }
+ }
+
+ ///
+ /// The outcome of a check
+ ///
+ public class TouchpanelWrapperAppVersionCheckResult
+ {
+ ///
+ /// The mcUserApp path that was checked
+ ///
+ public string AppPath { get; }
+
+ ///
+ /// The expected version from config, if any was configured
+ ///
+ public string ExpectedVersion { get; }
+
+ ///
+ /// True if any files were found deployed at
+ ///
+ public bool AppDeployed { get; }
+
+ ///
+ /// True if was found in one of the deployed files
+ ///
+ public bool VersionMatched { get; }
+
+ ///
+ /// The file the expected version was found in, if is true
+ ///
+ public string MatchedFile { get; }
+
+ ///
+ /// The number of files scanned
+ ///
+ public int FilesScanned { get; }
+
+ ///
+ /// A human-readable summary of the outcome, suitable for console output
+ ///
+ public string Summary { get; }
+
+ private TouchpanelWrapperAppVersionCheckResult(string appPath, string expectedVersion, bool appDeployed,
+ bool versionMatched, string matchedFile, int filesScanned, string summary)
+ {
+ AppPath = appPath;
+ ExpectedVersion = expectedVersion;
+ AppDeployed = appDeployed;
+ VersionMatched = versionMatched;
+ MatchedFile = matchedFile;
+ FilesScanned = filesScanned;
+ Summary = summary;
+ }
+
+ internal static TouchpanelWrapperAppVersionCheckResult NotConfigured(string appPath) =>
+ new TouchpanelWrapperAppVersionCheckResult(appPath, null, false, false, null, 0,
+ "versions.touchpanelWrapperApp.version is not configured; skipping check");
+
+ internal static TouchpanelWrapperAppVersionCheckResult AppNotDeployed(string appPath, string expectedVersion) =>
+ new TouchpanelWrapperAppVersionCheckResult(appPath, expectedVersion, false, false, null, 0,
+ $"No app files found at '{appPath}'; expected version {expectedVersion}");
+
+ internal static TouchpanelWrapperAppVersionCheckResult Match(string appPath, string expectedVersion, string matchedFile) =>
+ new TouchpanelWrapperAppVersionCheckResult(appPath, expectedVersion, true, true, matchedFile, 1,
+ $"Deployed app matches configured version {expectedVersion} (found in '{matchedFile}')");
+
+ internal static TouchpanelWrapperAppVersionCheckResult Mismatch(string appPath, string expectedVersion, int filesScanned) =>
+ new TouchpanelWrapperAppVersionCheckResult(appPath, expectedVersion, true, false, null, filesScanned,
+ $"Deployed app at '{appPath}' does NOT contain expected version {expectedVersion} ({filesScanned} file(s) scanned) - MISMATCH");
+
+ internal static TouchpanelWrapperAppVersionCheckResult Error(string appPath, string expectedVersion, string errorMessage) =>
+ new TouchpanelWrapperAppVersionCheckResult(appPath, expectedVersion, false, false, null, 0,
+ $"Error checking deployed app version at '{appPath}': {errorMessage}");
+ }
+}