Add PC uptime tracking feature
- Database: Add lastboottime column to machines table - API: Accept lastBootUpTime parameter and store in lastboottime column - PowerShell: Collect LastBootUpTime from Win32_OperatingSystem - Update-PC-Minimal.ps1: Add last boot time collection - Update-ShopfloorPCs-Remote.ps1: Add last boot time collection and API posting - Display: Add Uptime column to displaypcs.asp with color-coded badges - > 90 days: red badge - > 30 days: yellow badge - > 7 days: blue badge - <= 7 days: muted text - Filter: Add "Uptime > X days" filter dropdown (7, 30, 90 days) - SQL: Production migration script for lastboottime column 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
51
api.asp
51
api.asp
@@ -115,6 +115,13 @@ Sub UpdateCompleteAsset()
|
||||
Dim hasWinRM
|
||||
hasWinRM = Trim(Request.Form("hasWinRM") & "")
|
||||
|
||||
' Last boot time (optional) - accepts both lastBootUpTime and lastBootTime
|
||||
Dim lastBootTime
|
||||
lastBootTime = Trim(Request.Form("lastBootUpTime") & "")
|
||||
If lastBootTime = "" Then
|
||||
lastBootTime = Trim(Request.Form("lastBootTime") & "")
|
||||
End If
|
||||
|
||||
' DNC/GE registry data
|
||||
dncDualPathEnabled = Request.Form("dncDualPathEnabled")
|
||||
dncPath1Name = Trim(Request.Form("dncPath1Name") & "")
|
||||
@@ -290,7 +297,14 @@ Sub UpdateCompleteAsset()
|
||||
winrmValue = 0
|
||||
End If
|
||||
|
||||
strSQL = "UPDATE machines SET serialnumber='" & safeSerial & "', modelnumberid=" & modelId & ", machinetypeid=" & machineTypeId & ", osid=" & osid & ", isvnc=" & vncValue & ", iswinrm=" & winrmValue & ", lastupdated=NOW() WHERE machineid=" & machineid
|
||||
' Build UPDATE with optional lastboottime
|
||||
Dim lastBootPart
|
||||
If lastBootTime <> "" Then
|
||||
lastBootPart = ", lastboottime='" & Replace(lastBootTime, "'", "''") & "'"
|
||||
Else
|
||||
lastBootPart = ""
|
||||
End If
|
||||
strSQL = "UPDATE machines SET serialnumber='" & safeSerial & "', modelnumberid=" & modelId & ", machinetypeid=" & machineTypeId & ", osid=" & osid & ", isvnc=" & vncValue & ", iswinrm=" & winrmValue & lastBootPart & ", lastupdated=NOW() WHERE machineid=" & machineid
|
||||
objConn.Execute strSQL
|
||||
If Err.Number <> 0 Then
|
||||
SendError debugMsg & "10-UPDATE failed: " & Err.Description
|
||||
@@ -321,7 +335,16 @@ Sub UpdateCompleteAsset()
|
||||
winrmValueInsert = 0
|
||||
End If
|
||||
|
||||
strSQL = "INSERT INTO machines (hostname, serialnumber, modelnumberid, machinetypeid, osid, machinestatusid, isvnc, iswinrm, lastupdated) VALUES ('" & safeHostname & "', '" & safeSerial & "', " & modelId & ", " & machineTypeId & ", " & osid & ", " & pcstatusid & ", " & vncValueInsert & ", " & winrmValueInsert & ", NOW())"
|
||||
' Build INSERT with optional lastboottime
|
||||
Dim lastBootColInsert, lastBootValInsert
|
||||
If lastBootTime <> "" Then
|
||||
lastBootColInsert = ", lastboottime"
|
||||
lastBootValInsert = ", '" & Replace(lastBootTime, "'", "''") & "'"
|
||||
Else
|
||||
lastBootColInsert = ""
|
||||
lastBootValInsert = ""
|
||||
End If
|
||||
strSQL = "INSERT INTO machines (hostname, serialnumber, modelnumberid, machinetypeid, osid, machinestatusid, isvnc, iswinrm, lastupdated" & lastBootColInsert & ") VALUES ('" & safeHostname & "', '" & safeSerial & "', " & modelId & ", " & machineTypeId & ", " & osid & ", " & pcstatusid & ", " & vncValueInsert & ", " & winrmValueInsert & ", NOW()" & lastBootValInsert & ")"
|
||||
objConn.Execute strSQL
|
||||
If Err.Number <> 0 Then
|
||||
SendError debugMsg & "10-INSERT failed: " & Err.Description
|
||||
@@ -922,6 +945,14 @@ Function InsertOrUpdatePC(conn, hostname, serialnumber, manufacturer, model, pcT
|
||||
sqlStatusId = "NULL"
|
||||
End If
|
||||
|
||||
' Build lastboottime part for UPDATE
|
||||
Dim sqlLastBoot
|
||||
If lastBootTime <> "" Then
|
||||
sqlLastBoot = "lastboottime = '" & Replace(lastBootTime, "'", "''") & "', "
|
||||
Else
|
||||
sqlLastBoot = ""
|
||||
End If
|
||||
|
||||
strSQL = "UPDATE machines SET " & _
|
||||
"serialnumber = '" & safeSerial & "', " & _
|
||||
"modelnumberid = " & sqlModelId & ", " & _
|
||||
@@ -930,6 +961,7 @@ Function InsertOrUpdatePC(conn, hostname, serialnumber, manufacturer, model, pcT
|
||||
"machinenumber = " & sqlMachineNum & ", " & _
|
||||
"osid = " & sqlOsId & ", " & _
|
||||
"machinestatusid = " & sqlStatusId & ", " & _
|
||||
sqlLastBoot & _
|
||||
"lastupdated = NOW() " & _
|
||||
"WHERE machineid = " & CLng(machineid) & " AND pctypeid IS NOT NULL"
|
||||
|
||||
@@ -967,7 +999,16 @@ Function InsertOrUpdatePC(conn, hostname, serialnumber, manufacturer, model, pcT
|
||||
|
||||
' Build SQL in parts to isolate error
|
||||
Dim sqlPart1, sqlPart2, sqlPart3
|
||||
sqlPart1 = "INSERT INTO machines (hostname, serialnumber, modelnumberid, machinetypeid, pctypeid, loggedinuser, machinenumber, osid, machinestatusid, isactive, lastupdated) VALUES ("
|
||||
' Add lastboottime column if provided
|
||||
Dim sqlLastBootCol, sqlLastBootVal
|
||||
If lastBootTime <> "" Then
|
||||
sqlLastBootCol = ", lastboottime"
|
||||
sqlLastBootVal = ", '" & Replace(lastBootTime, "'", "''") & "'"
|
||||
Else
|
||||
sqlLastBootCol = ""
|
||||
sqlLastBootVal = ""
|
||||
End If
|
||||
sqlPart1 = "INSERT INTO machines (hostname, serialnumber, modelnumberid, machinetypeid, pctypeid, loggedinuser, machinenumber, osid, machinestatusid, isactive, lastupdated" & sqlLastBootCol & ") VALUES ("
|
||||
sqlPart2 = "'" & safeHostname & "', '" & safeSerial & "', "
|
||||
|
||||
If modelId > 0 Then
|
||||
@@ -998,9 +1039,9 @@ Function InsertOrUpdatePC(conn, hostname, serialnumber, manufacturer, model, pcT
|
||||
End If
|
||||
|
||||
If pcstatusid > 0 Then
|
||||
sqlPart3 = sqlPart3 & CLng(pcstatusid) & ", 1, NOW())"
|
||||
sqlPart3 = sqlPart3 & CLng(pcstatusid) & ", 1, NOW()" & sqlLastBootVal & ")"
|
||||
Else
|
||||
sqlPart3 = sqlPart3 & "NULL, 1, NOW())"
|
||||
sqlPart3 = sqlPart3 & "NULL, 1, NOW()" & sqlLastBootVal & ")"
|
||||
End If
|
||||
|
||||
strSQL = sqlPart1 & sqlPart2 & sqlPart3
|
||||
|
||||
@@ -39,11 +39,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<%
|
||||
Dim currentPCStatus, recentFilter, deviceTypeFilter, pcTypeFilter, sel
|
||||
Dim currentPCStatus, recentFilter, deviceTypeFilter, pcTypeFilter, uptimeFilter, sel
|
||||
currentPCStatus = Request.QueryString("pcstatus")
|
||||
recentFilter = Request.QueryString("recent")
|
||||
deviceTypeFilter = Request.QueryString("devicetype")
|
||||
pcTypeFilter = Request.QueryString("pctype")
|
||||
uptimeFilter = Request.QueryString("uptime")
|
||||
|
||||
' Check for specialized PCs (CMM, Wax Trace, Measuring Tool) without equipment relationships
|
||||
Dim rsUnlinked, unlinkedCount
|
||||
@@ -110,7 +111,13 @@ Set rsStatus = Nothing
|
||||
<option value="">All Time</option>
|
||||
<option value="7"<% If recentFilter = "7" Then Response.Write(" selected") End If%>>Last 7 Days</option>
|
||||
</select>
|
||||
<% If currentPCStatus <> "" Or recentFilter <> "" Or deviceTypeFilter <> "" Or pcTypeFilter <> "" Or Request.QueryString("needsrelationship") <> "" Then %>
|
||||
<select id="uptimeFilter" class="btn btn-secondary btn-sm" onchange="updateFilter('uptime', this.value)">
|
||||
<option value="">All Uptimes</option>
|
||||
<option value="7"<% If uptimeFilter = "7" Then Response.Write(" selected") End If%>>Uptime > 7 Days</option>
|
||||
<option value="30"<% If uptimeFilter = "30" Then Response.Write(" selected") End If%>>Uptime > 30 Days</option>
|
||||
<option value="90"<% If uptimeFilter = "90" Then Response.Write(" selected") End If%>>Uptime > 90 Days</option>
|
||||
</select>
|
||||
<% If currentPCStatus <> "" Or recentFilter <> "" Or deviceTypeFilter <> "" Or pcTypeFilter <> "" Or uptimeFilter <> "" Or Request.QueryString("needsrelationship") <> "" Then %>
|
||||
<a href="displaypcs.asp" class="btn btn-outline-secondary btn-sm">
|
||||
<i class="zmdi zmdi-close"></i> Clear
|
||||
</a>
|
||||
@@ -129,6 +136,7 @@ Set rsStatus = Nothing
|
||||
<th scope="col">Model</th>
|
||||
<th scope="col">OS</th>
|
||||
<th scope="col">Equipment</th>
|
||||
<th scope="col">Uptime</th>
|
||||
<th scope="col">VNC</th>
|
||||
<th scope="col">WinRM</th>
|
||||
</tr>
|
||||
@@ -137,17 +145,19 @@ Set rsStatus = Nothing
|
||||
|
||||
<%
|
||||
' Build query based on filters
|
||||
Dim pcStatusFilter, recentDaysFilter, deviceTypeFilterSQL, pcTypeFilterSQL, needsRelationshipFilter, whereClause
|
||||
Dim displayName, hasVnc, vncHost, hasWinrm
|
||||
Dim pcStatusFilter, recentDaysFilter, deviceTypeFilterSQL, pcTypeFilterSQL, uptimeFilterSQL, needsRelationshipFilter, whereClause
|
||||
Dim displayName, hasVnc, vncHost, hasWinrm, uptimeDays
|
||||
pcStatusFilter = Request.QueryString("pcstatus")
|
||||
recentDaysFilter = Request.QueryString("recent")
|
||||
deviceTypeFilterSQL = Request.QueryString("devicetype")
|
||||
pcTypeFilterSQL = Request.QueryString("pctype")
|
||||
uptimeFilterSQL = Request.QueryString("uptime")
|
||||
needsRelationshipFilter = Request.QueryString("needsrelationship")
|
||||
|
||||
' Base query with LEFT JOINs to show all PCs
|
||||
strSQL = "SELECT m.machineid, m.hostname, m.serialnumber, m.machinenumber, m.machinestatusid, " & _
|
||||
"m.modelnumberid, m.osid, m.loggedinuser, m.lastupdated, m.isvnc, m.iswinrm, " & _
|
||||
"m.modelnumberid, m.osid, m.loggedinuser, m.lastupdated, m.isvnc, m.iswinrm, m.lastboottime, " & _
|
||||
"DATEDIFF(NOW(), m.lastboottime) AS uptime_days, " & _
|
||||
"vendors.vendor, models.modelnumber, operatingsystems.operatingsystem, " & _
|
||||
"c.address AS ipaddress, c.macaddress, " & _
|
||||
"machinestatus.machinestatus, " & _
|
||||
@@ -184,6 +194,11 @@ Set rsStatus = Nothing
|
||||
whereClause = whereClause & " AND m.pctypeid = " & pcTypeFilterSQL
|
||||
End If
|
||||
|
||||
' Filter by uptime (days since last boot)
|
||||
If uptimeFilterSQL <> "" And IsNumeric(uptimeFilterSQL) Then
|
||||
whereClause = whereClause & " AND m.lastboottime IS NOT NULL AND DATEDIFF(NOW(), m.lastboottime) > " & uptimeFilterSQL
|
||||
End If
|
||||
|
||||
' Filter for specialized PCs needing equipment relationships
|
||||
If needsRelationshipFilter = "1" Then
|
||||
whereClause = whereClause & " AND m.pctypeid = 7" & _
|
||||
@@ -216,6 +231,23 @@ Set rsStatus = Nothing
|
||||
Response.Write("<span class='text-muted'>-</span>")
|
||||
End If
|
||||
%></td>
|
||||
<td><%
|
||||
' Uptime column - show days since last boot
|
||||
If Not IsNull(rs("uptime_days")) And rs("uptime_days") <> "" Then
|
||||
uptimeDays = CLng(rs("uptime_days") & "")
|
||||
If uptimeDays > 90 Then
|
||||
Response.Write("<span class='badge badge-danger' title='Last boot: " & rs("lastboottime") & "'>" & uptimeDays & "d</span>")
|
||||
ElseIf uptimeDays > 30 Then
|
||||
Response.Write("<span class='badge badge-warning' title='Last boot: " & rs("lastboottime") & "'>" & uptimeDays & "d</span>")
|
||||
ElseIf uptimeDays > 7 Then
|
||||
Response.Write("<span class='badge badge-info' title='Last boot: " & rs("lastboottime") & "'>" & uptimeDays & "d</span>")
|
||||
Else
|
||||
Response.Write("<span class='text-muted' title='Last boot: " & rs("lastboottime") & "'>" & uptimeDays & "d</span>")
|
||||
End If
|
||||
Else
|
||||
Response.Write("<span class='text-muted'>-</span>")
|
||||
End If
|
||||
%></td>
|
||||
<td><%
|
||||
' VNC column with link
|
||||
hasVnc = False
|
||||
|
||||
@@ -81,11 +81,17 @@ if ($interfaces.Count -gt 0) {
|
||||
$data.networkInterfaces = ($interfaces | ConvertTo-Json -Compress)
|
||||
}
|
||||
|
||||
# Try to get OS
|
||||
# Try to get OS and last boot time
|
||||
try {
|
||||
$os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop
|
||||
$data.osVersion = $os.Caption
|
||||
"OS: $($data.osVersion)" | Tee-Object -FilePath $logFile -Append
|
||||
|
||||
# Get last boot time
|
||||
if ($os.LastBootUpTime) {
|
||||
$data.lastBootUpTime = $os.LastBootUpTime.ToString("yyyy-MM-dd HH:mm:ss")
|
||||
"Last Boot: $($data.lastBootUpTime)" | Tee-Object -FilePath $logFile -Append
|
||||
}
|
||||
} catch {
|
||||
"ERROR getting OS: $_" | Tee-Object -FilePath $logFile -Append
|
||||
}
|
||||
|
||||
@@ -327,6 +327,7 @@ function Get-RemotePCInfo {
|
||||
$result.Model = $computerSystem.Model
|
||||
$result.LoggedInUser = $computerSystem.UserName
|
||||
$result.OSVersion = $os.Caption
|
||||
$result.LastBootUpTime = if ($os.LastBootUpTime) { $os.LastBootUpTime.ToString("yyyy-MM-dd HH:mm:ss") } else { $null }
|
||||
|
||||
# Get network interfaces
|
||||
$networkAdapters = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration |
|
||||
@@ -692,6 +693,11 @@ function Send-PCDataToApi {
|
||||
osVersion = $PCData.OSVersion
|
||||
}
|
||||
|
||||
# Add last boot time if available
|
||||
if ($PCData.LastBootUpTime) {
|
||||
$postData.lastBootUpTime = $PCData.LastBootUpTime
|
||||
}
|
||||
|
||||
# Add machine number if available
|
||||
if ($PCData.MachineNo) {
|
||||
$postData.machineNo = $PCData.MachineNo
|
||||
|
||||
29
sql/add_lastboottime_column.sql
Normal file
29
sql/add_lastboottime_column.sql
Normal file
@@ -0,0 +1,29 @@
|
||||
-- Add lastboottime column to machines table for PC uptime tracking
|
||||
-- Run on production database
|
||||
-- Date: 2025-12-09
|
||||
|
||||
-- Check if column exists before adding
|
||||
SET @dbname = DATABASE();
|
||||
SET @tablename = 'machines';
|
||||
SET @columnname = 'lastboottime';
|
||||
SET @preparedStatement = (SELECT IF(
|
||||
(
|
||||
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @dbname
|
||||
AND TABLE_NAME = @tablename
|
||||
AND COLUMN_NAME = @columnname
|
||||
) > 0,
|
||||
'SELECT ''Column lastboottime already exists'';',
|
||||
'ALTER TABLE machines ADD COLUMN lastboottime DATETIME NULL DEFAULT NULL AFTER lastupdated;'
|
||||
));
|
||||
|
||||
PREPARE alterIfNotExists FROM @preparedStatement;
|
||||
EXECUTE alterIfNotExists;
|
||||
DEALLOCATE PREPARE alterIfNotExists;
|
||||
|
||||
-- Verify the column was added
|
||||
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'machines'
|
||||
AND COLUMN_NAME = 'lastboottime';
|
||||
Reference in New Issue
Block a user