Add PC-machine relationships API and report, fix shopfloor dashboard
- Add getPCMachineRelationships API endpoint for PC-to-machine mappings - Add pcmachinerelationships.asp report page with copy table/CSV/JSON export - Fix shopfloor dashboard to immediately hide deactivated notifications - Add Firewall (machinetypeid 46) support to network device pages - Add model migration warning banner to networkdevices.asp - Create SQL script for hybrid model/machine type view Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
69
api.asp
69
api.asp
@@ -61,6 +61,8 @@ Select Case action
|
||||
GetUDCManualTiming()
|
||||
Case "getDeployableApps"
|
||||
GetDeployableApps()
|
||||
Case "getPCMachineRelationships"
|
||||
GetPCMachineRelationships()
|
||||
Case Else
|
||||
SendError "Invalid action: " & action
|
||||
End Select
|
||||
@@ -943,6 +945,73 @@ Sub GetShopfloorPCs()
|
||||
Response.Write "{""success"":true,""count"":" & pcCount & ",""data"":[" & pcList & "]}"
|
||||
End Sub
|
||||
|
||||
Sub GetPCMachineRelationships()
|
||||
' Returns PCs that have relationships to machines (equipment) with machine numbers
|
||||
' Used for identifying which PCs control which shop floor machines
|
||||
On Error Resume Next
|
||||
|
||||
Dim rsRel, strSQL, relList, relCount, relData
|
||||
|
||||
strSQL = "SELECT " & _
|
||||
"pc.machineid AS pc_id, " & _
|
||||
"pc.machinenumber AS hostname, " & _
|
||||
"c.address AS ip, " & _
|
||||
"eq.machineid AS machine_id, " & _
|
||||
"eq.machinenumber AS machine_number, " & _
|
||||
"v.vendor AS vendor, " & _
|
||||
"m.modelnumber AS model " & _
|
||||
"FROM machinerelationships mr " & _
|
||||
"JOIN machines eq ON mr.machineid = eq.machineid " & _
|
||||
"JOIN machines pc ON mr.related_machineid = pc.machineid " & _
|
||||
"LEFT JOIN communications c ON pc.machineid = c.machineid AND c.isprimary = 1 AND c.comstypeid = 1 " & _
|
||||
"LEFT JOIN models m ON eq.modelnumberid = m.modelnumberid " & _
|
||||
"LEFT JOIN vendors v ON m.vendorid = v.vendorid " & _
|
||||
"WHERE mr.isactive = 1 " & _
|
||||
"AND pc.pctypeid IS NOT NULL " & _
|
||||
"AND eq.machinenumber IS NOT NULL AND eq.machinenumber != '' " & _
|
||||
"AND eq.machinenumber REGEXP '^[0-9]{4}$' " & _
|
||||
"AND eq.machinenumber NOT IN ('0612', '0613', '0614', '0615') " & _
|
||||
"AND (v.vendor IS NULL OR v.vendor != 'WJDT') " & _
|
||||
"AND (m.modelnumber IS NULL OR m.modelnumber != 'TBD') " & _
|
||||
"ORDER BY eq.machinenumber"
|
||||
|
||||
Set rsRel = objConn.Execute(strSQL)
|
||||
|
||||
If Err.Number <> 0 Then
|
||||
SendError "Database error: " & Err.Description
|
||||
Exit Sub
|
||||
End If
|
||||
|
||||
' Build JSON array
|
||||
relList = ""
|
||||
relCount = 0
|
||||
|
||||
Do While Not rsRel.EOF
|
||||
If relList <> "" Then relList = relList & ","
|
||||
|
||||
relData = "{"
|
||||
relData = relData & """pc_id"":" & rsRel("pc_id") & ","
|
||||
relData = relData & """hostname"":""" & EscapeJSON(rsRel("hostname") & "") & ""","
|
||||
relData = relData & """ip"":""" & EscapeJSON(rsRel("ip") & "") & ""","
|
||||
relData = relData & """machine_id"":" & rsRel("machine_id") & ","
|
||||
relData = relData & """machine_number"":""" & EscapeJSON(rsRel("machine_number") & "") & ""","
|
||||
relData = relData & """vendor"":""" & EscapeJSON(rsRel("vendor") & "") & ""","
|
||||
relData = relData & """model"":""" & EscapeJSON(rsRel("model") & "") & """"
|
||||
relData = relData & "}"
|
||||
|
||||
relList = relList & relData
|
||||
relCount = relCount + 1
|
||||
|
||||
rsRel.MoveNext
|
||||
Loop
|
||||
|
||||
rsRel.Close
|
||||
Set rsRel = Nothing
|
||||
|
||||
' Send response
|
||||
Response.Write "{""success"":true,""count"":" & relCount & ",""data"":[" & relList & "]}"
|
||||
End Sub
|
||||
|
||||
Sub GetHighUptimePCs()
|
||||
' Returns list of PCs with uptime >= specified days (for reboot management)
|
||||
On Error Resume Next
|
||||
|
||||
@@ -8,6 +8,13 @@ Response.AddHeader "Cache-Control", "no-cache, no-store, must-revalidate"
|
||||
|
||||
Dim strSQL, jsonOutput, isFirstCurrent, isFirstUpcoming
|
||||
Dim businessUnitFilter
|
||||
Dim st, et, isCurrent, isResolved, isUpcoming
|
||||
Dim typeName, empSsoRaw, ssoArr, idx
|
||||
Dim singleSSO, singleName, singlePicture
|
||||
Dim empName, empPicture
|
||||
Dim upTypeName, upEmpSsoRaw, upSsoArr, upIdx
|
||||
Dim upSingleSSO, upSingleName, upSinglePicture
|
||||
Dim upEmpName, upEmpPicture
|
||||
|
||||
' Get business unit filter from query string
|
||||
businessUnitFilter = Request.QueryString("businessunit")
|
||||
@@ -16,11 +23,12 @@ strSQL = "SELECT n.notificationid, n.notification, n.starttime, n.endtime, " & _
|
||||
"n.ticketnumber, n.link, n.isactive, n.isshopfloor, n.businessunitid, " & _
|
||||
"n.employeesso, nt.typename, nt.typecolor, bu.businessunit, " & _
|
||||
"CASE " & _
|
||||
" WHEN n.starttime <= NOW() AND (n.endtime IS NULL OR n.endtime >= NOW()) THEN 1 " & _
|
||||
" WHEN n.starttime <= NOW() AND (n.endtime IS NULL OR n.endtime >= NOW()) AND n.isactive = 1 THEN 1 " & _
|
||||
" WHEN n.endtime IS NOT NULL AND n.endtime < NOW() AND DATE_ADD(n.endtime, INTERVAL 30 MINUTE) >= NOW() THEN 1 " & _
|
||||
" ELSE 0 " & _
|
||||
"END as is_current, " & _
|
||||
"CASE " & _
|
||||
" WHEN n.isactive = 0 THEN 1 " & _
|
||||
" WHEN n.endtime IS NOT NULL AND n.endtime < NOW() THEN 1 " & _
|
||||
" ELSE 0 " & _
|
||||
"END as is_resolved, " & _
|
||||
@@ -32,10 +40,9 @@ strSQL = "SELECT n.notificationid, n.notification, n.starttime, n.endtime, " & _
|
||||
"FROM notifications n " & _
|
||||
"LEFT JOIN notificationtypes nt ON n.notificationtypeid = nt.notificationtypeid " & _
|
||||
"LEFT JOIN businessunits bu ON n.businessunitid = bu.businessunitid " & _
|
||||
"WHERE n.isshopfloor = 1 AND (" & _
|
||||
" n.isactive = 1 OR " & _
|
||||
" (n.isactive = 0 AND n.endtime IS NOT NULL AND " & _
|
||||
" DATE_ADD(n.endtime, INTERVAL 30 MINUTE) >= NOW())" & _
|
||||
"WHERE n.isshopfloor = 1 AND n.isactive = 1 AND (" & _
|
||||
" n.endtime IS NULL OR " & _
|
||||
" DATE_ADD(n.endtime, INTERVAL 30 MINUTE) >= NOW()" & _
|
||||
")"
|
||||
|
||||
' Add business unit filter
|
||||
@@ -51,11 +58,10 @@ strSQL = strSQL & " ORDER BY n.notificationid DESC"
|
||||
|
||||
Set rs = objConn.Execute(strSQL)
|
||||
|
||||
jsonOutput = "{""success"":true,""timestamp"":""" & FormatDateTime(Now(), 2) & " " & FormatDateTime(Now(), 4) & """,""current"":["
|
||||
jsonOutput = "{""success"":true,""version"":""v2"",""timestamp"":""" & FormatDateTime(Now(), 2) & " " & FormatDateTime(Now(), 4) & """,""current"":["
|
||||
isFirstCurrent = True
|
||||
|
||||
Do While Not rs.EOF
|
||||
Dim st, et, isCurrent, isResolved
|
||||
st = rs("starttime")
|
||||
et = rs("endtime")
|
||||
isCurrent = rs("is_current")
|
||||
@@ -63,17 +69,14 @@ Do While Not rs.EOF
|
||||
|
||||
If isCurrent = 1 Then
|
||||
' Check if this is a Recognition with multiple employees
|
||||
Dim typeName, empSsoRaw
|
||||
typeName = rs("typename") & ""
|
||||
empSsoRaw = rs("employeesso") & ""
|
||||
|
||||
If LCase(typeName) = "recognition" And Len(empSsoRaw) > 0 And InStr(empSsoRaw, ",") > 0 Then
|
||||
' Split into individual cards for each employee
|
||||
Dim ssoArr, idx
|
||||
ssoArr = Split(empSsoRaw, ",")
|
||||
|
||||
For idx = 0 To UBound(ssoArr)
|
||||
Dim singleSSO, singleName, singlePicture
|
||||
singleSSO = Trim(ssoArr(idx))
|
||||
singleName = LookupSingleEmployeeName(singleSSO)
|
||||
singlePicture = LookupSingleEmployeePicture(singleSSO)
|
||||
@@ -128,7 +131,6 @@ Do While Not rs.EOF
|
||||
jsonOutput = jsonOutput & """typecolor"":""" & JSEscape(rs("typecolor") & "") & ""","
|
||||
jsonOutput = jsonOutput & """businessunit"":" & StrOrNull(rs("businessunit")) & ","
|
||||
' Handle employeesso - can be SSO or NAME:customname
|
||||
Dim empName, empPicture
|
||||
If Left(empSsoRaw, 5) = "NAME:" Then
|
||||
' Custom name - extract name, no picture
|
||||
empName = Mid(empSsoRaw, 6)
|
||||
@@ -156,24 +158,20 @@ jsonOutput = jsonOutput & "],""upcoming"":["
|
||||
isFirstUpcoming = True
|
||||
|
||||
Do While Not rs.EOF
|
||||
Dim isUpcoming
|
||||
st = rs("starttime")
|
||||
et = rs("endtime")
|
||||
isUpcoming = rs("is_upcoming")
|
||||
|
||||
If isUpcoming = 1 Then
|
||||
' Check if this is a Recognition with multiple employees
|
||||
Dim upTypeName, upEmpSsoRaw
|
||||
upTypeName = rs("typename") & ""
|
||||
upEmpSsoRaw = rs("employeesso") & ""
|
||||
|
||||
If LCase(upTypeName) = "recognition" And Len(upEmpSsoRaw) > 0 And InStr(upEmpSsoRaw, ",") > 0 Then
|
||||
' Split into individual cards for each employee
|
||||
Dim upSsoArr, upIdx
|
||||
upSsoArr = Split(upEmpSsoRaw, ",")
|
||||
|
||||
For upIdx = 0 To UBound(upSsoArr)
|
||||
Dim upSingleSSO, upSingleName, upSinglePicture
|
||||
upSingleSSO = Trim(upSsoArr(upIdx))
|
||||
upSingleName = LookupSingleEmployeeName(upSingleSSO)
|
||||
upSinglePicture = LookupSingleEmployeePicture(upSingleSSO)
|
||||
@@ -216,7 +214,6 @@ Do While Not rs.EOF
|
||||
jsonOutput = jsonOutput & """typecolor"":""" & JSEscape(rs("typecolor") & "") & ""","
|
||||
jsonOutput = jsonOutput & """businessunit"":" & StrOrNull(rs("businessunit")) & ","
|
||||
' Handle employeesso - can be SSO or NAME:customname
|
||||
Dim upEmpName, upEmpPicture
|
||||
If Left(upEmpSsoRaw, 5) = "NAME:" Then
|
||||
' Custom name - extract name, no picture
|
||||
upEmpName = Mid(upEmpSsoRaw, 6)
|
||||
|
||||
@@ -240,7 +240,10 @@
|
||||
strSQL2 = "SELECT machinetypeid, machinetype FROM machinetypes WHERE isactive = 1 ORDER BY machinetype ASC"
|
||||
Set rsMachineTypes = objConn.Execute(strSQL2)
|
||||
While Not rsMachineTypes.EOF
|
||||
Response.Write("<option value='" & rsMachineTypes("machinetypeid") & "'>" & Server.HTMLEncode(rsMachineTypes("machinetype")) & "</option>")
|
||||
' Pre-select Firewall (46) as the default for this form
|
||||
Dim mtSelected
|
||||
If CStr(rsMachineTypes("machinetypeid")) = "46" Then mtSelected = " selected" Else mtSelected = ""
|
||||
Response.Write("<option value='" & rsMachineTypes("machinetypeid") & "'" & mtSelected & ">" & Server.HTMLEncode(rsMachineTypes("machinetype")) & "</option>")
|
||||
rsMachineTypes.MoveNext
|
||||
Wend
|
||||
rsMachineTypes.Close
|
||||
|
||||
@@ -297,7 +297,7 @@ strSQL = "SELECT m.machineid, m.machinenumber, m.alias, m.serialnumber, " &_
|
||||
"AND m.isactive = 1 " &_
|
||||
"AND m.mapleft IS NOT NULL " &_
|
||||
"AND m.maptop IS NOT NULL " &_
|
||||
"AND mt.machinetypeid NOT IN (1, 16, 17, 18, 19, 20) " &_
|
||||
"AND mt.machinetypeid NOT IN (1, 16, 17, 18, 19, 20, 46) " &_
|
||||
"AND mt.machinetypeid < 33 " &_
|
||||
"ORDER BY mt.machinetype, m.machinenumber ASC"
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ Set rsM = objConn.Execute("SELECT m.machineid, m.machinenumber, m.alias, m.maple
|
||||
"LEFT JOIN machinetypes mt ON mo.machinetypeid = mt.machinetypeid " &_
|
||||
"WHERE m.pctypeid IS NULL " &_
|
||||
"AND m.isactive = 1 " &_
|
||||
"AND mt.machinetypeid NOT IN (1, 16, 17, 18, 19, 20) " &_
|
||||
"AND mt.machinetypeid NOT IN (1, 16, 17, 18, 19, 20, 46) " &_
|
||||
"AND mt.machinetypeid < 33 " &_
|
||||
"ORDER BY m.machinenumber")
|
||||
Do While Not rsM.EOF
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
<a class="dropdown-item" href="deviceidf.asp?id=0"><i class="zmdi zmdi-city-alt"></i> Add IDF</a>
|
||||
<a class="dropdown-item" href="deviceserver.asp?id=0"><i class="zmdi zmdi-storage"></i> Add Server</a>
|
||||
<a class="dropdown-item" href="deviceswitch.asp?id=0"><i class="zmdi zmdi-device-hub"></i> Add Switch</a>
|
||||
<a class="dropdown-item" href="devicefirewall.asp?id=0"><i class="zmdi zmdi-shield-security"></i> Add Firewall</a>
|
||||
<a class="dropdown-item" href="devicecamera.asp?id=0"><i class="zmdi zmdi-videocam"></i> Add Camera</a>
|
||||
<a class="dropdown-item" href="deviceaccesspoint.asp?id=0"><i class="zmdi zmdi-wifi"></i> Add Access Point</a>
|
||||
<a class="dropdown-item" href="addprinter.asp"><i class="zmdi zmdi-print"></i> Add Printer</a>
|
||||
@@ -47,6 +48,107 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Model Migration Warning - Devices needing vendor/model updates -->
|
||||
<%
|
||||
' Check for devices that need model fixes
|
||||
Dim rsModelIssues, modelIssueCount
|
||||
Dim strModelIssueSQL
|
||||
strModelIssueSQL = "SELECT " & _
|
||||
"ma.machineid, " & _
|
||||
"COALESCE(ma.alias, ma.machinenumber) as device_name, " & _
|
||||
"mt_machine.machinetype as current_type, " & _
|
||||
"ma.machinetypeid as machine_typeid, " & _
|
||||
"mo.modelnumber, " & _
|
||||
"CASE " & _
|
||||
" WHEN ma.modelnumberid IS NULL THEN 'No model assigned' " & _
|
||||
" WHEN mo.machinetypeid IS NULL THEN 'Model has no type set' " & _
|
||||
" WHEN mo.machinetypeid NOT IN (16,17,18,19,20,46) THEN 'Model has incorrect type' " & _
|
||||
" ELSE 'OK' " & _
|
||||
"END as issue " & _
|
||||
"FROM machines ma " & _
|
||||
"JOIN machinetypes mt_machine ON ma.machinetypeid = mt_machine.machinetypeid " & _
|
||||
"LEFT JOIN models mo ON ma.modelnumberid = mo.modelnumberid " & _
|
||||
"WHERE ma.machinetypeid IN (16,17,18,19,20,46) " & _
|
||||
"AND ma.isactive = 1 " & _
|
||||
"AND (ma.modelnumberid IS NULL OR mo.machinetypeid IS NULL OR mo.machinetypeid NOT IN (16,17,18,19,20,46)) " & _
|
||||
"ORDER BY mt_machine.machinetype, ma.alias"
|
||||
Set rsModelIssues = objConn.Execute(strModelIssueSQL)
|
||||
|
||||
' Count issues
|
||||
modelIssueCount = 0
|
||||
If Not rsModelIssues.EOF Then
|
||||
rsModelIssues.MoveFirst
|
||||
Do While Not rsModelIssues.EOF
|
||||
modelIssueCount = modelIssueCount + 1
|
||||
rsModelIssues.MoveNext
|
||||
Loop
|
||||
rsModelIssues.MoveFirst
|
||||
End If
|
||||
|
||||
If modelIssueCount > 0 Then
|
||||
%>
|
||||
<div class="alert alert-warning alert-dismissible fade show" role="alert" style="margin-bottom:20px;">
|
||||
<h5 class="alert-heading"><i class="zmdi zmdi-alert-triangle"></i> Model Migration Required</h5>
|
||||
<p><strong><%=modelIssueCount%> device(s)</strong> need vendor/model updates to complete the migration from machine-level type assignment to model-level type assignment.</p>
|
||||
<hr>
|
||||
<p class="mb-0">
|
||||
<a class="btn btn-sm btn-warning" data-toggle="collapse" href="#modelIssuesList" role="button">
|
||||
<i class="zmdi zmdi-eye"></i> Show Devices Needing Updates
|
||||
</a>
|
||||
</p>
|
||||
<div class="collapse mt-3" id="modelIssuesList">
|
||||
<table class="table table-sm table-bordered" style="background:#fff; color:#333;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Device</th>
|
||||
<th>Type</th>
|
||||
<th>Current Model</th>
|
||||
<th>Issue</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%
|
||||
Dim editUrl
|
||||
Do While Not rsModelIssues.EOF
|
||||
Select Case rsModelIssues("current_type")
|
||||
Case "IDF"
|
||||
editUrl = "deviceidf.asp?id=" & rsModelIssues("machineid")
|
||||
Case "Server"
|
||||
editUrl = "deviceserver.asp?id=" & rsModelIssues("machineid")
|
||||
Case "Switch"
|
||||
editUrl = "deviceswitch.asp?id=" & rsModelIssues("machineid")
|
||||
Case "Firewall"
|
||||
editUrl = "devicefirewall.asp?id=" & rsModelIssues("machineid")
|
||||
Case "Camera"
|
||||
editUrl = "devicecamera.asp?id=" & rsModelIssues("machineid")
|
||||
Case "Access Point"
|
||||
editUrl = "deviceaccesspoint.asp?id=" & rsModelIssues("machineid")
|
||||
Case Else
|
||||
editUrl = "displaydevice.asp?id=" & rsModelIssues("machineid")
|
||||
End Select
|
||||
%>
|
||||
<tr>
|
||||
<td><%=Server.HTMLEncode(rsModelIssues("device_name") & "")%></td>
|
||||
<td><%=Server.HTMLEncode(rsModelIssues("current_type") & "")%></td>
|
||||
<td><%If IsNull(rsModelIssues("modelnumber")) Or rsModelIssues("modelnumber") = "" Then Response.Write("<em>None</em>") Else Response.Write(Server.HTMLEncode(rsModelIssues("modelnumber") & ""))%></td>
|
||||
<td><span class="badge badge-warning"><%=Server.HTMLEncode(rsModelIssues("issue") & "")%></span></td>
|
||||
<td><a href="<%=editUrl%>" class="btn btn-xs btn-primary"><i class="zmdi zmdi-edit"></i> Edit</a></td>
|
||||
</tr>
|
||||
<%
|
||||
rsModelIssues.MoveNext
|
||||
Loop
|
||||
%>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<%
|
||||
End If
|
||||
rsModelIssues.Close
|
||||
Set rsModelIssues = Nothing
|
||||
%>
|
||||
|
||||
<!-- Filter Tabs -->
|
||||
<%
|
||||
Dim filterType
|
||||
@@ -89,6 +191,11 @@
|
||||
<i class="zmdi zmdi-device-hub"></i> Switches
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <%If filterType="Firewall" Then Response.Write("active")%>" href="networkdevices.asp?filter=Firewall">
|
||||
<i class="zmdi zmdi-shield-security"></i> Firewalls
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="table-responsive">
|
||||
@@ -102,8 +209,8 @@ If filterType = "IDF" Then
|
||||
Response.Write("<th scope='col'><i class='zmdi zmdi-pin'></i></th>")
|
||||
Response.Write("<th scope='col'>Name</th>")
|
||||
Response.Write("<th scope='col'>Description</th>")
|
||||
ElseIf filterType = "Server" Or filterType = "Switch" Then
|
||||
' Server/Switch columns: Location, Name, Vendor, Model, Serial, IP Address, FQDN, Description
|
||||
ElseIf filterType = "Server" Or filterType = "Switch" Or filterType = "Firewall" Then
|
||||
' Server/Switch/Firewall columns: Location, Name, Vendor, Model, Serial, IP Address, FQDN, Description
|
||||
Response.Write("<th scope='col'><i class='zmdi zmdi-pin'></i></th>")
|
||||
Response.Write("<th scope='col'>Name</th>")
|
||||
Response.Write("<th scope='col'>Vendor</th>")
|
||||
@@ -255,7 +362,7 @@ End If
|
||||
End If
|
||||
Response.Write("</td>")
|
||||
|
||||
ElseIf filterType = "Server" Or filterType = "Switch" Then
|
||||
ElseIf filterType = "Server" Or filterType = "Switch" Or filterType = "Firewall" Then
|
||||
' Vendor, Model, Serial, IP Address, FQDN, Description, Actions
|
||||
Response.Write("<td>")
|
||||
If Not IsNull(rs("vendor")) Then
|
||||
@@ -475,6 +582,9 @@ End If
|
||||
Case "Switch":
|
||||
noDataMessage = "No switches found."
|
||||
colspanCount = "8"
|
||||
Case "Firewall":
|
||||
noDataMessage = "No firewalls found."
|
||||
colspanCount = "8"
|
||||
Case "Camera":
|
||||
noDataMessage = "No cameras found."
|
||||
colspanCount = "8"
|
||||
|
||||
@@ -246,7 +246,7 @@ strSQL = "SELECT printers.printerid AS id, machines.machinenumber AS name, machi
|
||||
"LEFT JOIN machinetypes mt ON m.machinetypeid = mt.machinetypeid " &_
|
||||
"LEFT JOIN vendors v ON mo.vendorid = v.vendorid " &_
|
||||
"LEFT JOIN communications c ON m.machineid = c.machineid AND c.isprimary = 1 AND c.comstypeid = 1 " &_
|
||||
"WHERE m.machinetypeid IN (16, 17, 18, 19, 20) " &_
|
||||
"WHERE m.machinetypeid IN (16, 17, 18, 19, 20, 46) " &_
|
||||
"AND m.isactive = 1 " &_
|
||||
"AND m.mapleft IS NOT NULL " &_
|
||||
"AND m.maptop IS NOT NULL " &_
|
||||
|
||||
158
pcmachinerelationships.asp
Normal file
158
pcmachinerelationships.asp
Normal file
@@ -0,0 +1,158 @@
|
||||
<%@ Language=VBScript %>
|
||||
<!--#include file="includes/sql.asp"-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>PC-Machine Relationships</title>
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.6.0/css/bootstrap.min.css">
|
||||
<style>
|
||||
body { padding: 20px; }
|
||||
table { font-size: 14px; }
|
||||
.copy-btn { margin-bottom: 15px; }
|
||||
pre { background: #f8f9fa; padding: 15px; border-radius: 4px; max-height: 400px; overflow: auto; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<h2>PC-Machine Relationships</h2>
|
||||
<p class="text-muted">PCs with relationships to shop floor machines</p>
|
||||
|
||||
<button class="btn btn-primary copy-btn" onclick="copyTable()">Copy Table to Clipboard</button>
|
||||
<button class="btn btn-secondary copy-btn" onclick="copyCSV()">Copy as CSV</button>
|
||||
<button class="btn btn-info copy-btn" onclick="copyJSON()">Copy as JSON</button>
|
||||
|
||||
<table class="table table-striped table-bordered table-sm" id="dataTable">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Machine #</th>
|
||||
<th>Vendor</th>
|
||||
<th>Model</th>
|
||||
<th>PC Hostname</th>
|
||||
<th>PC IP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%
|
||||
Dim strSQL, rs
|
||||
|
||||
strSQL = "SELECT " & _
|
||||
"pc.machineid AS pc_id, " & _
|
||||
"pc.machinenumber AS hostname, " & _
|
||||
"c.address AS ip, " & _
|
||||
"eq.machineid AS machine_id, " & _
|
||||
"eq.machinenumber AS machine_number, " & _
|
||||
"v.vendor AS vendor, " & _
|
||||
"m.modelnumber AS model " & _
|
||||
"FROM machinerelationships mr " & _
|
||||
"JOIN machines eq ON mr.machineid = eq.machineid " & _
|
||||
"JOIN machines pc ON mr.related_machineid = pc.machineid " & _
|
||||
"LEFT JOIN communications c ON pc.machineid = c.machineid AND c.isprimary = 1 AND c.comstypeid = 1 " & _
|
||||
"LEFT JOIN models m ON eq.modelnumberid = m.modelnumberid " & _
|
||||
"LEFT JOIN vendors v ON m.vendorid = v.vendorid " & _
|
||||
"WHERE mr.isactive = 1 " & _
|
||||
"AND pc.pctypeid IS NOT NULL " & _
|
||||
"AND eq.machinenumber IS NOT NULL AND eq.machinenumber != '' " & _
|
||||
"AND eq.machinenumber REGEXP '^[0-9]{4}$' " & _
|
||||
"AND eq.machinenumber NOT IN ('0612', '0613', '0614', '0615') " & _
|
||||
"AND (v.vendor IS NULL OR v.vendor != 'WJDT') " & _
|
||||
"AND (m.modelnumber IS NULL OR m.modelnumber != 'TBD') " & _
|
||||
"ORDER BY eq.machinenumber"
|
||||
|
||||
Set rs = objConn.Execute(strSQL)
|
||||
|
||||
Dim rowCount
|
||||
rowCount = 0
|
||||
|
||||
Do While Not rs.EOF
|
||||
rowCount = rowCount + 1
|
||||
%>
|
||||
<tr>
|
||||
<td><%= Server.HTMLEncode(rs("machine_number") & "") %></td>
|
||||
<td><%= Server.HTMLEncode(rs("vendor") & "") %></td>
|
||||
<td><%= Server.HTMLEncode(rs("model") & "") %></td>
|
||||
<td><%= Server.HTMLEncode(rs("hostname") & "") %></td>
|
||||
<td><%= Server.HTMLEncode(rs("ip") & "") %></td>
|
||||
</tr>
|
||||
<%
|
||||
rs.MoveNext
|
||||
Loop
|
||||
rs.Close
|
||||
Set rs = Nothing
|
||||
%>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p class="text-muted"><%= rowCount %> records found</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function copyTable() {
|
||||
var table = document.getElementById('dataTable');
|
||||
var text = '';
|
||||
|
||||
// Get headers
|
||||
var headers = table.querySelectorAll('thead th');
|
||||
headers.forEach(function(th, i) {
|
||||
text += th.innerText + (i < headers.length - 1 ? '\t' : '\n');
|
||||
});
|
||||
|
||||
// Get rows
|
||||
var rows = table.querySelectorAll('tbody tr');
|
||||
rows.forEach(function(row) {
|
||||
var cells = row.querySelectorAll('td');
|
||||
cells.forEach(function(td, i) {
|
||||
text += td.innerText + (i < cells.length - 1 ? '\t' : '\n');
|
||||
});
|
||||
});
|
||||
|
||||
navigator.clipboard.writeText(text).then(function() {
|
||||
alert('Table copied to clipboard!');
|
||||
});
|
||||
}
|
||||
|
||||
function copyCSV() {
|
||||
var table = document.getElementById('dataTable');
|
||||
var csv = '';
|
||||
|
||||
// Get headers
|
||||
var headers = table.querySelectorAll('thead th');
|
||||
headers.forEach(function(th, i) {
|
||||
csv += '"' + th.innerText + '"' + (i < headers.length - 1 ? ',' : '\n');
|
||||
});
|
||||
|
||||
// Get rows
|
||||
var rows = table.querySelectorAll('tbody tr');
|
||||
rows.forEach(function(row) {
|
||||
var cells = row.querySelectorAll('td');
|
||||
cells.forEach(function(td, i) {
|
||||
csv += '"' + td.innerText + '"' + (i < cells.length - 1 ? ',' : '\n');
|
||||
});
|
||||
});
|
||||
|
||||
navigator.clipboard.writeText(csv).then(function() {
|
||||
alert('CSV copied to clipboard!');
|
||||
});
|
||||
}
|
||||
|
||||
function copyJSON() {
|
||||
var table = document.getElementById('dataTable');
|
||||
var data = [];
|
||||
|
||||
var rows = table.querySelectorAll('tbody tr');
|
||||
rows.forEach(function(row) {
|
||||
var cells = row.querySelectorAll('td');
|
||||
data.push({
|
||||
"Name": cells[0].innerText,
|
||||
"IpAddress": cells[4].innerText,
|
||||
"Group": null
|
||||
});
|
||||
});
|
||||
|
||||
var json = JSON.stringify(data, null, 2);
|
||||
navigator.clipboard.writeText(json).then(function() {
|
||||
alert('JSON copied to clipboard!');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1186,15 +1186,40 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Fallback timeout for pendingDataRender
|
||||
let pendingDataTimeout = null;
|
||||
|
||||
// Render events on the page (with smart delay during transitions)
|
||||
function renderEvents(data) {
|
||||
// If any carousel is mid-transition, delay the render
|
||||
if (isTransitioning || isNonIncidentTransitioning || isRecognitionTransitioning) {
|
||||
console.log('Carousel: Transition in progress, delaying render by 800ms');
|
||||
console.log('Carousel: Transition in progress, delaying render');
|
||||
pendingDataRender = data;
|
||||
|
||||
// Fallback: if pendingDataRender isn't processed within 1 second, force render
|
||||
if (pendingDataTimeout) clearTimeout(pendingDataTimeout);
|
||||
pendingDataTimeout = setTimeout(() => {
|
||||
if (pendingDataRender) {
|
||||
console.log('Carousel: Fallback timeout - forcing render of pending data');
|
||||
// Reset all transition flags in case they're stuck
|
||||
isTransitioning = false;
|
||||
isNonIncidentTransitioning = false;
|
||||
isRecognitionTransitioning = false;
|
||||
const dataToRender = pendingDataRender;
|
||||
pendingDataRender = null;
|
||||
renderEvents(dataToRender);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear fallback timeout since we're rendering now
|
||||
if (pendingDataTimeout) {
|
||||
clearTimeout(pendingDataTimeout);
|
||||
pendingDataTimeout = null;
|
||||
}
|
||||
|
||||
const container = document.getElementById('eventsContainer');
|
||||
let html = '';
|
||||
|
||||
@@ -1539,6 +1564,14 @@
|
||||
currentItem.classList.remove('exit-up');
|
||||
currentItem.classList.add('enter-down');
|
||||
isNonIncidentTransitioning = false;
|
||||
|
||||
// If there's pending data to render, render it now
|
||||
if (pendingDataRender) {
|
||||
console.log('Non-Incident Carousel: Transition complete, rendering pending data');
|
||||
const dataToRender = pendingDataRender;
|
||||
pendingDataRender = null;
|
||||
renderEvents(dataToRender);
|
||||
}
|
||||
}, 800);
|
||||
|
||||
currentNonIncidentIndex = nextIndex;
|
||||
@@ -1681,6 +1714,14 @@
|
||||
currentItem.classList.remove('exit-up');
|
||||
currentItem.classList.add('enter-down');
|
||||
isRecognitionTransitioning = false;
|
||||
|
||||
// If there's pending data to render, render it now
|
||||
if (pendingDataRender) {
|
||||
console.log('Recognition Carousel: Transition complete, rendering pending data');
|
||||
const dataToRender = pendingDataRender;
|
||||
pendingDataRender = null;
|
||||
renderEvents(dataToRender);
|
||||
}
|
||||
}, 800);
|
||||
|
||||
currentRecognitionIndex = nextIndex;
|
||||
@@ -1853,6 +1894,10 @@
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
// Debug: log recognition count
|
||||
const recCount = data.current ? data.current.filter(e => e.typecolor === 'recognition').length : 0;
|
||||
console.log(`Fetch: Got ${recCount} recognitions, ${data.current ? data.current.length : 0} total current events`);
|
||||
|
||||
renderEvents(data);
|
||||
updateLastUpdateTime();
|
||||
setConnectionStatus(true);
|
||||
|
||||
81
sql/update_network_devices_view.sql
Normal file
81
sql/update_network_devices_view.sql
Normal file
@@ -0,0 +1,81 @@
|
||||
-- ============================================================================
|
||||
-- FILE: update_network_devices_view.sql
|
||||
-- PURPOSE: Update vw_network_devices to use model's machinetypeid (with fallback)
|
||||
-- DATE: 2026-01-29
|
||||
--
|
||||
-- DESCRIPTION:
|
||||
-- This update changes vw_network_devices to prefer model.machinetypeid over
|
||||
-- machines.machinetypeid (deprecating the latter). Uses a hybrid approach:
|
||||
-- - If model has a valid network device machinetypeid (16,17,18,19,20,46), use it
|
||||
-- - Otherwise fall back to machines.machinetypeid for backward compatibility
|
||||
--
|
||||
-- Also adds Firewall (machinetypeid=46) to the list of network device types.
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE VIEW vw_network_devices AS
|
||||
-- Printers (from separate printers table)
|
||||
SELECT
|
||||
'Printer' AS device_type,
|
||||
p.printerid AS device_id,
|
||||
p.printerwindowsname AS device_name,
|
||||
p.modelid AS modelid,
|
||||
m.modelnumber AS modelnumber,
|
||||
v.vendor AS vendor,
|
||||
p.serialnumber AS serialnumber,
|
||||
p.ipaddress AS ipaddress,
|
||||
NULL AS description,
|
||||
p.maptop AS maptop,
|
||||
p.mapleft AS mapleft,
|
||||
p.isactive AS isactive,
|
||||
NULL AS idfid,
|
||||
NULL AS idfname,
|
||||
NULL AS macaddress,
|
||||
p.fqdn AS fqdn
|
||||
FROM printers p
|
||||
LEFT JOIN models m ON p.modelid = m.modelnumberid
|
||||
LEFT JOIN vendors v ON m.vendorid = v.vendorid
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Network devices from machines table
|
||||
-- HYBRID: Use model's machinetypeid if valid network device type,
|
||||
-- otherwise fall back to machine's machinetypeid
|
||||
SELECT
|
||||
mt.machinetype AS device_type,
|
||||
ma.machineid AS device_id,
|
||||
COALESCE(ma.alias, ma.machinenumber) AS device_name,
|
||||
ma.modelnumberid AS modelid,
|
||||
mo.modelnumber AS modelnumber,
|
||||
ve.vendor AS vendor,
|
||||
ma.serialnumber AS serialnumber,
|
||||
c.address AS ipaddress,
|
||||
ma.machinenotes AS description,
|
||||
ma.maptop AS maptop,
|
||||
ma.mapleft AS mapleft,
|
||||
ma.isactive AS isactive,
|
||||
NULL AS idfid,
|
||||
NULL AS idfname,
|
||||
c.macaddress AS macaddress,
|
||||
ma.fqdn AS fqdn
|
||||
FROM machines ma
|
||||
LEFT JOIN models mo ON ma.modelnumberid = mo.modelnumberid
|
||||
JOIN machinetypes mt ON mt.machinetypeid = COALESCE(
|
||||
-- Prefer model's machinetypeid if it's a valid network device type
|
||||
CASE WHEN mo.machinetypeid IN (16,17,18,19,20,46) THEN mo.machinetypeid ELSE NULL END,
|
||||
-- Fall back to machine's machinetypeid
|
||||
ma.machinetypeid
|
||||
)
|
||||
LEFT JOIN vendors ve ON mo.vendorid = ve.vendorid
|
||||
LEFT JOIN communications c ON ma.machineid = c.machineid AND c.isprimary = 1 AND c.comstypeid = 1
|
||||
WHERE COALESCE(
|
||||
CASE WHEN mo.machinetypeid IN (16,17,18,19,20,46) THEN mo.machinetypeid ELSE NULL END,
|
||||
ma.machinetypeid
|
||||
) IN (16, 17, 18, 19, 20, 46);
|
||||
|
||||
-- Network device type IDs:
|
||||
-- 16 = Access Point
|
||||
-- 17 = IDF
|
||||
-- 18 = Camera
|
||||
-- 19 = Switch
|
||||
-- 20 = Server
|
||||
-- 46 = Firewall (newly added)
|
||||
Reference in New Issue
Block a user