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
|
Dim hasWinRM
|
||||||
hasWinRM = Trim(Request.Form("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
|
' DNC/GE registry data
|
||||||
dncDualPathEnabled = Request.Form("dncDualPathEnabled")
|
dncDualPathEnabled = Request.Form("dncDualPathEnabled")
|
||||||
dncPath1Name = Trim(Request.Form("dncPath1Name") & "")
|
dncPath1Name = Trim(Request.Form("dncPath1Name") & "")
|
||||||
@@ -290,7 +297,14 @@ Sub UpdateCompleteAsset()
|
|||||||
winrmValue = 0
|
winrmValue = 0
|
||||||
End If
|
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
|
objConn.Execute strSQL
|
||||||
If Err.Number <> 0 Then
|
If Err.Number <> 0 Then
|
||||||
SendError debugMsg & "10-UPDATE failed: " & Err.Description
|
SendError debugMsg & "10-UPDATE failed: " & Err.Description
|
||||||
@@ -321,7 +335,16 @@ Sub UpdateCompleteAsset()
|
|||||||
winrmValueInsert = 0
|
winrmValueInsert = 0
|
||||||
End If
|
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
|
objConn.Execute strSQL
|
||||||
If Err.Number <> 0 Then
|
If Err.Number <> 0 Then
|
||||||
SendError debugMsg & "10-INSERT failed: " & Err.Description
|
SendError debugMsg & "10-INSERT failed: " & Err.Description
|
||||||
@@ -922,6 +945,14 @@ Function InsertOrUpdatePC(conn, hostname, serialnumber, manufacturer, model, pcT
|
|||||||
sqlStatusId = "NULL"
|
sqlStatusId = "NULL"
|
||||||
End If
|
End If
|
||||||
|
|
||||||
|
' Build lastboottime part for UPDATE
|
||||||
|
Dim sqlLastBoot
|
||||||
|
If lastBootTime <> "" Then
|
||||||
|
sqlLastBoot = "lastboottime = '" & Replace(lastBootTime, "'", "''") & "', "
|
||||||
|
Else
|
||||||
|
sqlLastBoot = ""
|
||||||
|
End If
|
||||||
|
|
||||||
strSQL = "UPDATE machines SET " & _
|
strSQL = "UPDATE machines SET " & _
|
||||||
"serialnumber = '" & safeSerial & "', " & _
|
"serialnumber = '" & safeSerial & "', " & _
|
||||||
"modelnumberid = " & sqlModelId & ", " & _
|
"modelnumberid = " & sqlModelId & ", " & _
|
||||||
@@ -930,6 +961,7 @@ Function InsertOrUpdatePC(conn, hostname, serialnumber, manufacturer, model, pcT
|
|||||||
"machinenumber = " & sqlMachineNum & ", " & _
|
"machinenumber = " & sqlMachineNum & ", " & _
|
||||||
"osid = " & sqlOsId & ", " & _
|
"osid = " & sqlOsId & ", " & _
|
||||||
"machinestatusid = " & sqlStatusId & ", " & _
|
"machinestatusid = " & sqlStatusId & ", " & _
|
||||||
|
sqlLastBoot & _
|
||||||
"lastupdated = NOW() " & _
|
"lastupdated = NOW() " & _
|
||||||
"WHERE machineid = " & CLng(machineid) & " AND pctypeid IS NOT NULL"
|
"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
|
' Build SQL in parts to isolate error
|
||||||
Dim sqlPart1, sqlPart2, sqlPart3
|
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 & "', "
|
sqlPart2 = "'" & safeHostname & "', '" & safeSerial & "', "
|
||||||
|
|
||||||
If modelId > 0 Then
|
If modelId > 0 Then
|
||||||
@@ -998,9 +1039,9 @@ Function InsertOrUpdatePC(conn, hostname, serialnumber, manufacturer, model, pcT
|
|||||||
End If
|
End If
|
||||||
|
|
||||||
If pcstatusid > 0 Then
|
If pcstatusid > 0 Then
|
||||||
sqlPart3 = sqlPart3 & CLng(pcstatusid) & ", 1, NOW())"
|
sqlPart3 = sqlPart3 & CLng(pcstatusid) & ", 1, NOW()" & sqlLastBootVal & ")"
|
||||||
Else
|
Else
|
||||||
sqlPart3 = sqlPart3 & "NULL, 1, NOW())"
|
sqlPart3 = sqlPart3 & "NULL, 1, NOW()" & sqlLastBootVal & ")"
|
||||||
End If
|
End If
|
||||||
|
|
||||||
strSQL = sqlPart1 & sqlPart2 & sqlPart3
|
strSQL = sqlPart1 & sqlPart2 & sqlPart3
|
||||||
|
|||||||
@@ -39,11 +39,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<%
|
<%
|
||||||
Dim currentPCStatus, recentFilter, deviceTypeFilter, pcTypeFilter, sel
|
Dim currentPCStatus, recentFilter, deviceTypeFilter, pcTypeFilter, uptimeFilter, sel
|
||||||
currentPCStatus = Request.QueryString("pcstatus")
|
currentPCStatus = Request.QueryString("pcstatus")
|
||||||
recentFilter = Request.QueryString("recent")
|
recentFilter = Request.QueryString("recent")
|
||||||
deviceTypeFilter = Request.QueryString("devicetype")
|
deviceTypeFilter = Request.QueryString("devicetype")
|
||||||
pcTypeFilter = Request.QueryString("pctype")
|
pcTypeFilter = Request.QueryString("pctype")
|
||||||
|
uptimeFilter = Request.QueryString("uptime")
|
||||||
|
|
||||||
' Check for specialized PCs (CMM, Wax Trace, Measuring Tool) without equipment relationships
|
' Check for specialized PCs (CMM, Wax Trace, Measuring Tool) without equipment relationships
|
||||||
Dim rsUnlinked, unlinkedCount
|
Dim rsUnlinked, unlinkedCount
|
||||||
@@ -110,7 +111,13 @@ Set rsStatus = Nothing
|
|||||||
<option value="">All Time</option>
|
<option value="">All Time</option>
|
||||||
<option value="7"<% If recentFilter = "7" Then Response.Write(" selected") End If%>>Last 7 Days</option>
|
<option value="7"<% If recentFilter = "7" Then Response.Write(" selected") End If%>>Last 7 Days</option>
|
||||||
</select>
|
</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">
|
<a href="displaypcs.asp" class="btn btn-outline-secondary btn-sm">
|
||||||
<i class="zmdi zmdi-close"></i> Clear
|
<i class="zmdi zmdi-close"></i> Clear
|
||||||
</a>
|
</a>
|
||||||
@@ -129,6 +136,7 @@ Set rsStatus = Nothing
|
|||||||
<th scope="col">Model</th>
|
<th scope="col">Model</th>
|
||||||
<th scope="col">OS</th>
|
<th scope="col">OS</th>
|
||||||
<th scope="col">Equipment</th>
|
<th scope="col">Equipment</th>
|
||||||
|
<th scope="col">Uptime</th>
|
||||||
<th scope="col">VNC</th>
|
<th scope="col">VNC</th>
|
||||||
<th scope="col">WinRM</th>
|
<th scope="col">WinRM</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -137,17 +145,19 @@ Set rsStatus = Nothing
|
|||||||
|
|
||||||
<%
|
<%
|
||||||
' Build query based on filters
|
' Build query based on filters
|
||||||
Dim pcStatusFilter, recentDaysFilter, deviceTypeFilterSQL, pcTypeFilterSQL, needsRelationshipFilter, whereClause
|
Dim pcStatusFilter, recentDaysFilter, deviceTypeFilterSQL, pcTypeFilterSQL, uptimeFilterSQL, needsRelationshipFilter, whereClause
|
||||||
Dim displayName, hasVnc, vncHost, hasWinrm
|
Dim displayName, hasVnc, vncHost, hasWinrm, uptimeDays
|
||||||
pcStatusFilter = Request.QueryString("pcstatus")
|
pcStatusFilter = Request.QueryString("pcstatus")
|
||||||
recentDaysFilter = Request.QueryString("recent")
|
recentDaysFilter = Request.QueryString("recent")
|
||||||
deviceTypeFilterSQL = Request.QueryString("devicetype")
|
deviceTypeFilterSQL = Request.QueryString("devicetype")
|
||||||
pcTypeFilterSQL = Request.QueryString("pctype")
|
pcTypeFilterSQL = Request.QueryString("pctype")
|
||||||
|
uptimeFilterSQL = Request.QueryString("uptime")
|
||||||
needsRelationshipFilter = Request.QueryString("needsrelationship")
|
needsRelationshipFilter = Request.QueryString("needsrelationship")
|
||||||
|
|
||||||
' Base query with LEFT JOINs to show all PCs
|
' Base query with LEFT JOINs to show all PCs
|
||||||
strSQL = "SELECT m.machineid, m.hostname, m.serialnumber, m.machinenumber, m.machinestatusid, " & _
|
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, " & _
|
"vendors.vendor, models.modelnumber, operatingsystems.operatingsystem, " & _
|
||||||
"c.address AS ipaddress, c.macaddress, " & _
|
"c.address AS ipaddress, c.macaddress, " & _
|
||||||
"machinestatus.machinestatus, " & _
|
"machinestatus.machinestatus, " & _
|
||||||
@@ -184,6 +194,11 @@ Set rsStatus = Nothing
|
|||||||
whereClause = whereClause & " AND m.pctypeid = " & pcTypeFilterSQL
|
whereClause = whereClause & " AND m.pctypeid = " & pcTypeFilterSQL
|
||||||
End If
|
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
|
' Filter for specialized PCs needing equipment relationships
|
||||||
If needsRelationshipFilter = "1" Then
|
If needsRelationshipFilter = "1" Then
|
||||||
whereClause = whereClause & " AND m.pctypeid = 7" & _
|
whereClause = whereClause & " AND m.pctypeid = 7" & _
|
||||||
@@ -216,6 +231,23 @@ Set rsStatus = Nothing
|
|||||||
Response.Write("<span class='text-muted'>-</span>")
|
Response.Write("<span class='text-muted'>-</span>")
|
||||||
End If
|
End If
|
||||||
%></td>
|
%></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><%
|
<td><%
|
||||||
' VNC column with link
|
' VNC column with link
|
||||||
hasVnc = False
|
hasVnc = False
|
||||||
|
|||||||
@@ -81,11 +81,17 @@ if ($interfaces.Count -gt 0) {
|
|||||||
$data.networkInterfaces = ($interfaces | ConvertTo-Json -Compress)
|
$data.networkInterfaces = ($interfaces | ConvertTo-Json -Compress)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Try to get OS
|
# Try to get OS and last boot time
|
||||||
try {
|
try {
|
||||||
$os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop
|
$os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop
|
||||||
$data.osVersion = $os.Caption
|
$data.osVersion = $os.Caption
|
||||||
"OS: $($data.osVersion)" | Tee-Object -FilePath $logFile -Append
|
"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 {
|
} catch {
|
||||||
"ERROR getting OS: $_" | Tee-Object -FilePath $logFile -Append
|
"ERROR getting OS: $_" | Tee-Object -FilePath $logFile -Append
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,6 +327,7 @@ function Get-RemotePCInfo {
|
|||||||
$result.Model = $computerSystem.Model
|
$result.Model = $computerSystem.Model
|
||||||
$result.LoggedInUser = $computerSystem.UserName
|
$result.LoggedInUser = $computerSystem.UserName
|
||||||
$result.OSVersion = $os.Caption
|
$result.OSVersion = $os.Caption
|
||||||
|
$result.LastBootUpTime = if ($os.LastBootUpTime) { $os.LastBootUpTime.ToString("yyyy-MM-dd HH:mm:ss") } else { $null }
|
||||||
|
|
||||||
# Get network interfaces
|
# Get network interfaces
|
||||||
$networkAdapters = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration |
|
$networkAdapters = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration |
|
||||||
@@ -692,6 +693,11 @@ function Send-PCDataToApi {
|
|||||||
osVersion = $PCData.OSVersion
|
osVersion = $PCData.OSVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Add last boot time if available
|
||||||
|
if ($PCData.LastBootUpTime) {
|
||||||
|
$postData.lastBootUpTime = $PCData.LastBootUpTime
|
||||||
|
}
|
||||||
|
|
||||||
# Add machine number if available
|
# Add machine number if available
|
||||||
if ($PCData.MachineNo) {
|
if ($PCData.MachineNo) {
|
||||||
$postData.machineNo = $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