mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-08-31 19:08:29 +00:00
refactor: replace client-reported UI app version tracking with disk-based check
Remove ConnectedClientVersionInfo and the per-client version dictionary in MobileControlSystemController, which grew unbounded and depended on a UI client connecting and self-reporting its APP_VERSION. Add TouchpanelWrapperAppVersionChecker, which instead scans the deployed mcUserApp files on disk for the configured versions.touchpanelWrapperApp version string. This works without any client connected and doesn't accumulate state. Wired into the mobileinfo console command output.
This commit is contained in:
parent
e7406dbead
commit
4631d3a537
3 changed files with 147 additions and 148 deletions
|
|
@ -1,61 +0,0 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the version information reported by a connected Mobile Control UI client
|
||||
/// </summary>
|
||||
public class ConnectedClientVersionInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the client id
|
||||
/// </summary>
|
||||
[JsonProperty("clientId")]
|
||||
public string ClientId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the room key the client joined
|
||||
/// </summary>
|
||||
[JsonProperty("roomKey")]
|
||||
public string RoomKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the touchpanel key the client joined as, if any
|
||||
/// </summary>
|
||||
[JsonProperty("touchpanelKey")]
|
||||
public string TouchpanelKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the app version reported by the client (e.g. the React app's build-time APP_VERSION)
|
||||
/// </summary>
|
||||
[JsonProperty("appVersion")]
|
||||
public string AppVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the expected app version from the system config's versions.touchpanelWrapperApp, if configured
|
||||
/// </summary>
|
||||
[JsonProperty("expectedAppVersion")]
|
||||
public string ExpectedAppVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the UTC time the client last reported this version
|
||||
/// </summary>
|
||||
[JsonProperty("lastSeen")]
|
||||
public DateTime LastSeen { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of this instance, safe for callers outside the owning lock to hold/mutate
|
||||
/// without affecting the internally tracked instance
|
||||
/// </summary>
|
||||
public ConnectedClientVersionInfo Clone() => new ConnectedClientVersionInfo
|
||||
{
|
||||
ClientId = ClientId,
|
||||
RoomKey = RoomKey,
|
||||
TouchpanelKey = TouchpanelKey,
|
||||
AppVersion = AppVersion,
|
||||
ExpectedAppVersion = ExpectedAppVersion,
|
||||
LastSeen = LastSeen
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -58,32 +58,11 @@ namespace PepperDash.Essentials
|
|||
private readonly Dictionary<string, IMobileControlMessenger> _defaultMessengers =
|
||||
new Dictionary<string, IMobileControlMessenger>();
|
||||
|
||||
private readonly Dictionary<string, ConnectedClientVersionInfo> _connectedClientVersions =
|
||||
new Dictionary<string, ConnectedClientVersionInfo>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
private readonly object _connectedClientVersionsLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Get the custom messengers
|
||||
/// </summary>
|
||||
public ReadOnlyDictionary<string, IMobileControlMessenger> Messengers => new ReadOnlyDictionary<string, IMobileControlMessenger>(_messengers);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the most recently reported UI app version for each connected client, keyed by clientId
|
||||
/// </summary>
|
||||
public ReadOnlyDictionary<string, ConnectedClientVersionInfo> ConnectedClientVersions
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_connectedClientVersionsLock)
|
||||
{
|
||||
return new ReadOnlyDictionary<string, ConnectedClientVersionInfo>(
|
||||
_connectedClientVersions.ToDictionary(kv => kv.Key, kv => kv.Value.Clone())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the default messengers
|
||||
/// </summary>
|
||||
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1445,8 +1410,6 @@ namespace PepperDash.Essentials
|
|||
var roomKey = content["roomKey"].Value<string>();
|
||||
var touchpanelKey = content.SelectToken("touchpanelKey");
|
||||
|
||||
TrackClientAppVersion(clientId, roomKey, touchpanelKey?.Value<string>(), content.SelectToken("appVersion")?.Value<string>());
|
||||
|
||||
if (_roomCombiner == null)
|
||||
{
|
||||
var message = new MobileControlMessage
|
||||
|
|
@ -1518,50 +1481,6 @@ namespace PepperDash.Essentials
|
|||
SendTouchpanelKey(clientId, touchpanelKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,141 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the touchpanel wrapper app (the mobile control React app) deployed to this
|
||||
/// processor's <c>mcUserApp</c> folder matches the version configured in the system config's
|
||||
/// <c>versions.touchpanelWrapperApp</c>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public static class TouchpanelWrapperAppVersionChecker
|
||||
{
|
||||
private static readonly string[] SearchPatterns = { "*.js", "*.html" };
|
||||
|
||||
/// <summary>
|
||||
/// Scans the deployed touchpanel wrapper app's .js/.html files under <paramref name="appPath"/>
|
||||
/// for the literal <paramref name="expectedVersion"/> string (the version build tooling bakes
|
||||
/// into the app at build time).
|
||||
/// </summary>
|
||||
/// <param name="appPath">The path to the deployed mcUserApp folder</param>
|
||||
/// <param name="expectedVersion">The expected version, from config's versions.touchpanelWrapperApp.version</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The outcome of a <see cref="TouchpanelWrapperAppVersionChecker.CheckDeployedVersion"/> check
|
||||
/// </summary>
|
||||
public class TouchpanelWrapperAppVersionCheckResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The mcUserApp path that was checked
|
||||
/// </summary>
|
||||
public string AppPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The expected version from config, if any was configured
|
||||
/// </summary>
|
||||
public string ExpectedVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True if any files were found deployed at <see cref="AppPath"/>
|
||||
/// </summary>
|
||||
public bool AppDeployed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True if <see cref="ExpectedVersion"/> was found in one of the deployed files
|
||||
/// </summary>
|
||||
public bool VersionMatched { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The file the expected version was found in, if <see cref="VersionMatched"/> is true
|
||||
/// </summary>
|
||||
public string MatchedFile { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of files scanned
|
||||
/// </summary>
|
||||
public int FilesScanned { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A human-readable summary of the outcome, suitable for console output
|
||||
/// </summary>
|
||||
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}");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue