Presence, AD/SCCM/DNS reasons, monitors, snapshots, new-device priority

Worker: pings every tracked device on a presence sweep (hourly in business
hours plus midnight) and records last online; inventory reports whether a
device was found in AD and SCCM; monitors from WmiMonitorID; snapshots of
the last reachable state replace "Cached"; newly tracked devices are swept
and published first; totals count source devices only; per-type timing in
Worker-State.

Front end: details dialog shows presence, reasons instead of N/A,
"Waiting for worker...", monitors and snapshot times, and refreshes in
place; Custom View gains Days Offline, Last Online, Found In and monitor
columns; manual pair changes show immediately; sync dialog shows timing by
type. Custom View gets a large scrollbar; "Exactly ticked" filter.
This commit is contained in:
Johan Sandoval committed 2026-10-06 19:09:49 +00:00
1 parent 50c2a18a68
commit 7868948bfc
10 files changed
+1230 -356

No files matched your search

+34 -13
View File
@@ -342,9 +342,9 @@
return { tagMap, manualPairs, skipTags };
}
function blankRow(src, dst, tags) {
function blankRow(src, dst, tags, pairing) {
return {
"Source Device": src, "Destination Device": dst, "Tags": tags,
"Source Device": src, "Destination Device": dst, "Tags": tags, "Pairing": pairing,
"Source Total Space": "N/A", "Source Free Space": "N/A", "USMT Status": "N/A", "Backup Status": "N/A",
"Source Online": "N/A", "Destination Online": "N/A",
"Source Scanstate Line": "N/A", "Destination Loadstate Line": "N/A",
@@ -376,9 +376,17 @@
if (a.IP && a.IP !== 'N/A') { const p = a.IP.split('.'); a.IP = `${p[0]}.${p[1]}.${10 + Math.floor(rand(name, 'ip3') * 240)}.${10 + Math.floor(rand(name, 'ip4') * 240)}`; }
if (a.Disk) a.Disk = a.Disk.replace(/S\/N: [^<]*$/, 'S/N: ' + hex(name + 'sn', 8).join('').replace(/(.{4})/g, '$1_').replace(/_$/, '.'));
for (const k of ['LastUser', 'SCCMPrimaryUsers']) if (typeof a[k] === 'string') a[k] = renameIn(a[k], templateName, name).replace(templateName.toLowerCase(), name.toLowerCase());
a.FirstChecked = new Date().toISOString();
return a;
}
// What the worker records for a name nothing knows (a typo): not in AD, SCCM or DNS
function unknownAdv() {
const now = new Date().toISOString();
return { IP: "N/A", MAC: "N/A", OU: "N/A", Groups: "N/A", Model: "N/A", RAM: "N/A", Disk: "N/A", ClientStatus: "N/A", MonitorCount: "N/A", Monitors: "N/A",
RoomType: "N/A", InAD: false, InSCCM: false, InDNS: false, Fetched: now, LastChecked: now, LastPing: "Not Found", FirstChecked: now, LastOnline: null };
}
// Builds the Data.json rows the worker would produce for a roster, plus synthetic
// advanced/software entries for devices the fleet doesn't know.
function buildRows(tracked, serverRows, serverAdv) {
@@ -393,7 +401,7 @@
const srow = bySrc[dev];
if (tagMap[dev].some(t => skipTags.has(t))) {
const r = blankRow(dev, "N/A", tags);
const r = blankRow(dev, "N/A", tags, "Skipped");
Object.assign(r, { "USMT Status": "Skipped", "Backup Status": "Skipped", "Source Online": "Skipped", "Destination Online": "-" });
out.push(r);
if (!serverAdv[dev]) { const t = pickTemplate(dev, serverRows); if (t && SCHEME.test(dev)) { adv[dev] = synthAdv(dev, t["Source Device"], serverAdv); software[dev] = t["Source Device"]; } }
@@ -403,12 +411,14 @@
if (manualPairs[dev]) {
const dst = manualPairs[dev];
if (srow && srow["Destination Device"] === dst && srow["USMT Status"] === "N/A") { out.push(Object.assign(clone(srow), { Tags: tags })); continue; }
const r = blankRow(dev, dst, tags);
for (const [n, col] of [[dev, "Source Online"], [dst, "Destination Online"]]) {
const r = blankRow(dev, dst, tags, "Manual");
const now = new Date().toISOString();
for (const [n, col] of [[dev, "Source"], [dst, "Destination"]]) {
const t = SCHEME.test(n) ? pickTemplate(n, serverRows) : null;
r[col] = t ? t["Source Online"] : "Not Found";
r[col + " Online"] = t ? t["Source Online"] : "Not Found";
r[col + " Checked"] = now;
if (t && !serverAdv[n]) { adv[n] = synthAdv(n, t["Source Device"], serverAdv); software[n] = t["Source Device"]; }
else if (!t) software[n] = null;
else if (!t) { software[n] = null; if (!serverAdv[n]) adv[n] = unknownAdv(); }
}
out.push(r);
continue;
@@ -420,11 +430,12 @@
const t = SCHEME.test(dev) ? pickTemplate(dev, serverRows) : null;
if (!t) {
// Not in the fleet and not a valid name: what the worker reports for a typo
const r = blankRow(dev, "N/A", tags);
const r = blankRow(dev, "N/A", tags, "None");
Object.assign(r, { "USMT Status": "Not Associated", "Backup Status": "Not Associated", "Source Online": "Not Found", "Destination Online": "-",
"Source Scanstate Line": "Unreachable (Not Found)", "Source Scanstate File": "Unreachable" });
"Source Scanstate Line": "Unreachable (Not Found)", "Source Scanstate File": "Unreachable", "Source Checked": new Date().toISOString() });
out.push(r);
software[dev] = null; // SCCM has no inventory for it: an empty list, same as Get-Software.ps1
if (!serverAdv[dev]) adv[dev] = unknownAdv();
continue;
}
const tSrc = t["Source Device"], tDst = t["Destination Device"], dst = synthDest(dev);
@@ -506,7 +517,7 @@
return W;
}
add([' -> Querying SCCM for associations...', 'DarkGray']);
if (r["Source Online"] === "Cached") {
if (r["Pairing"] === "Cached" || r["Source Online"] === "Cached") {
add([' -> SCCM DB Status: Completed (Raw: 2)', 'DarkGray']);
add([' -> [CACHE HIT] Fast-tracking row and bypassing network sweeps.', 'Green']);
return W;
@@ -564,7 +575,7 @@
const before = new Set(Object.keys(parseRoster(applied).tagMap));
const { rows } = buildRows(tracked, snap.rows, snap.adv);
const { tagMap } = parseRoster(tracked);
const total = Object.keys(tagMap).length;
const total = rows.length; // source devices only, like the worker
const started = new Date();
const state = (status, cur) => { Sim.worker = { Status: status, Current: cur, Total: total }; };
@@ -582,11 +593,14 @@
}
w([['[Software Cache Synchronized]', 'Green']]);
w([]);
w([[`--- Commencing Data Sweep (${total} Devices) ---`, 'White']]);
// Devices the visitor just added are swept first, like the worker does for new devices
const fresh = rows.filter(r => !before.has(r["Source Device"]));
const ordered = fresh.concat(rows.filter(r => before.has(r["Source Device"])));
w([[`--- Commencing Data Sweep (${total} Devices${fresh.length ? `, ${fresh.length} new first` : ''}) ---`, 'White']]);
const delay = Math.max(15, Math.min(90, 9000 / Math.max(1, rows.length)));
let i = 0;
for (const r of rows) {
for (const r of ordered) {
i++;
state('Updating Devices...', i);
const src = r["Source Device"];
@@ -594,6 +608,13 @@
w([[` -> Tags: [${r.Tags}]`, 'DarkCyan']]);
for (const segs of deviceLines(r, !before.has(src), full)) w(segs);
await sleep(delay);
if (fresh.length && i === fresh.length && i < ordered.length) {
w([[` -> [PRIORITY] ${fresh.length} new device(s) swept. Publishing early...`, 'Green']]);
if (!s.tracked) await ensureOwnRoster();
s.appliedTracked = clone(tracked);
s.rev++;
Store.save();
}
}
w([[' -> Pruning and Compressing Software Cache...', 'DarkGray']]);