From 8945fe2a0a49d97931e635327270fca2fd08cefa Mon Sep 17 00:00:00 2001 From: cproudlock Date: Thu, 29 Jan 2026 16:06:33 -0500 Subject: [PATCH] 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 --- api.asp | 69 ++++++++++++ apishopfloor.asp | 29 +++-- devicefirewall.asp | 5 +- machinemap.asp | 2 +- machinemapeditor.asp | 2 +- networkdevices.asp | 116 +++++++++++++++++++- networkmap.asp | 2 +- pcmachinerelationships.asp | 158 ++++++++++++++++++++++++++++ shopfloor-dashboard/index.html | 47 ++++++++- sql/update_network_devices_view.sql | 81 ++++++++++++++ 10 files changed, 487 insertions(+), 24 deletions(-) create mode 100644 pcmachinerelationships.asp create mode 100644 sql/update_network_devices_view.sql diff --git a/api.asp b/api.asp index be7ca55..354d9f0 100644 --- a/api.asp +++ b/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 diff --git a/apishopfloor.asp b/apishopfloor.asp index e31f3fd..99915f2 100644 --- a/apishopfloor.asp +++ b/apishopfloor.asp @@ -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) diff --git a/devicefirewall.asp b/devicefirewall.asp index f8acae8..e589ec1 100644 --- a/devicefirewall.asp +++ b/devicefirewall.asp @@ -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("") + ' Pre-select Firewall (46) as the default for this form + Dim mtSelected + If CStr(rsMachineTypes("machinetypeid")) = "46" Then mtSelected = " selected" Else mtSelected = "" + Response.Write("") rsMachineTypes.MoveNext Wend rsMachineTypes.Close diff --git a/machinemap.asp b/machinemap.asp index 92aae54..ec884b3 100644 --- a/machinemap.asp +++ b/machinemap.asp @@ -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" diff --git a/machinemapeditor.asp b/machinemapeditor.asp index fbd8176..e0a4f7c 100644 --- a/machinemapeditor.asp +++ b/machinemapeditor.asp @@ -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 diff --git a/networkdevices.asp b/networkdevices.asp index 62c17ba..a2bc0f2 100644 --- a/networkdevices.asp +++ b/networkdevices.asp @@ -40,6 +40,7 @@ Add IDF Add Server Add Switch + Add Firewall Add Camera Add Access Point Add Printer @@ -47,6 +48,107 @@ + + <% + ' 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 + %> + + <% + End If + rsModelIssues.Close + Set rsModelIssues = Nothing + %> + <% Dim filterType @@ -89,6 +191,11 @@ Switches +
@@ -102,8 +209,8 @@ If filterType = "IDF" Then Response.Write("") Response.Write("Name") Response.Write("Description") -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("") Response.Write("Name") Response.Write("Vendor") @@ -255,7 +362,7 @@ End If End If Response.Write("") - 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("") 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" diff --git a/networkmap.asp b/networkmap.asp index 4e27814..eb21c91 100644 --- a/networkmap.asp +++ b/networkmap.asp @@ -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 " &_ diff --git a/pcmachinerelationships.asp b/pcmachinerelationships.asp new file mode 100644 index 0000000..9de7713 --- /dev/null +++ b/pcmachinerelationships.asp @@ -0,0 +1,158 @@ +<%@ Language=VBScript %> + + + + + PC-Machine Relationships + + + + +
+

PC-Machine Relationships

+

PCs with relationships to shop floor machines

+ + + + + + + + + + + + + + + + +<% +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 +%> + + + + + + + +<% + rs.MoveNext +Loop +rs.Close +Set rs = Nothing +%> + +
Machine #VendorModelPC HostnamePC IP
<%= Server.HTMLEncode(rs("machine_number") & "") %><%= Server.HTMLEncode(rs("vendor") & "") %><%= Server.HTMLEncode(rs("model") & "") %><%= Server.HTMLEncode(rs("hostname") & "") %><%= Server.HTMLEncode(rs("ip") & "") %>
+ +

<%= rowCount %> records found

+
+ + + + diff --git a/shopfloor-dashboard/index.html b/shopfloor-dashboard/index.html index 9726380..79e3c6c 100644 --- a/shopfloor-dashboard/index.html +++ b/shopfloor-dashboard/index.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); diff --git a/sql/update_network_devices_view.sql b/sql/update_network_devices_view.sql new file mode 100644 index 0000000..cc0c4e1 --- /dev/null +++ b/sql/update_network_devices_view.sql @@ -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)