<%
' Declare all variables at top level
Dim strSQL, rs, printerid, printerwindowsname, printercsfname, ipaddress, machinenumber, modelnumber, machineid, vendor
Dim printerData, zabbixConnected, pingStatus, suppliesJSON
Dim lowSuppliesFound
Dim itemStart, itemEnd, currentPos, itemBlock
Dim itemName, itemValue, itemStatus, itemState
Dim namePos, nameStart, nameEnd
Dim valuePos, valueStart, valueEnd
Dim statusPos, statusStart, statusEnd
Dim statePos, stateStart, stateEnd
Dim baseName, numericValue
Dim statusIcon, statusColor, statusText
Dim partNumber, lookupName
Dim partNumbers
Dim debugPartNumbers, debugAllItems, debugItemCount
Dim isSupplyItem, isWasteItem, showItem
Dim marketingName, displayPartNumber
Dim partKeyName, tryName, partKey, foundMatch
Dim primaryWord, supplyType, colorPos
Dim typeMatches, colorMatches
Dim urgencyScore, alertItem, alertItems(1000), alertCount, i, j, tempAlert, k, outputItem
Dim isXeroxPrinter
Dim stdPartNumber, metPartNumber, altPartNumber, stdMarketing, metMarketing, altMarketing, partNumberResult, partNumberParts
lowSuppliesFound = False
alertCount = 0
strSQL = "SELECT printers.printerid, printers.printerwindowsname, printers.printercsfname, printers.ipaddress, " &_
"machines.machinenumber, machines.machineid, models.modelnumber, machines.alias, vendors.vendor " &_
"FROM printers " &_
"INNER JOIN models ON printers.modelid = models.modelnumberid " &_
"INNER JOIN machines ON printers.machineid = machines.machineid " &_
"INNER JOIN vendors ON models.vendorid = vendors.vendorid " &_
"WHERE printers.isactive = 1 AND printers.ipaddress IS NOT NULL AND printers.ipaddress != '' " &_
"ORDER BY machines.machinenumber ASC"
set rs = objconn.Execute(strSQL)
While Not rs.EOF
printerid = rs("printerid")
printerwindowsname = rs("printerwindowsname")
printercsfname = rs("printercsfname")
ipaddress = rs("ipaddress")
modelnumber = rs("modelnumber")
machineid = rs("machineid")
vendor = rs("vendor")
' Detect if this is a Xerox printer for vendor-specific waste cartridge logic
' Xerox printers (VersaLink, AltaLink, EC series) report waste inverted:
' 100% = empty (capacity remaining), 0% = full (no capacity remaining)
isXeroxPrinter = (InStr(1, vendor, "Xerox", 1) > 0)
' Use alias if available, otherwise machinenumber
If NOT IsNull(rs("alias")) AND rs("alias") <> "" Then
machinenumber = rs("alias")
Else
machinenumber = rs("machinenumber")
End If
' Get cached Zabbix data for this printer (all supplies including maintenance)
printerData = SafeGetZabbixData(ipaddress)
If Not IsEmpty(printerData) And IsArray(printerData) Then
zabbixConnected = printerData(0)
pingStatus = printerData(1)
suppliesJSON = printerData(2)
' Parse supplies JSON to find items below 20%
If zabbixConnected = "1" And suppliesJSON <> "" And InStr(suppliesJSON, """result"":[") > 0 Then
' Check if result array is not empty
If InStr(suppliesJSON, """result"":[]") = 0 Then
' First pass: Build lookup of part numbers (type:info items)
' Use Dictionary object for more reliable storage
Set partNumbers = Server.CreateObject("Scripting.Dictionary")
debugPartNumbers = ""
debugAllItems = ""
debugItemCount = 0
currentPos = InStr(suppliesJSON, """result"":[") + 11
' Scan for part number items (containing "Part Number" in name)
Do While currentPos > 11 And currentPos < Len(suppliesJSON)
itemStart = InStr(currentPos, suppliesJSON, "{""itemid"":")
If itemStart = 0 Then Exit Do
itemEnd = InStr(itemStart, suppliesJSON, "},{")
If itemEnd = 0 Then itemEnd = InStr(itemStart, suppliesJSON, "}]")
If itemEnd = 0 Then Exit Do
itemBlock = Mid(suppliesJSON, itemStart, itemEnd - itemStart + 1)
' Extract name
namePos = InStr(itemBlock, """name"":""")
If namePos > 0 Then
nameStart = namePos + 8
nameEnd = InStr(nameStart, itemBlock, """")
itemName = Mid(itemBlock, nameStart, nameEnd - nameStart)
Else
itemName = ""
End If
' DEBUG: Track all items scanned
debugItemCount = debugItemCount + 1
If debugItemCount <= 10 Then
debugAllItems = debugAllItems & itemName & " | "
End If
' If this is a part number item, store it
' Look for various part number patterns (case-insensitive)
If InStr(1, itemName, "Part Number", 1) > 0 Or InStr(1, itemName, "Part number", 1) > 0 Or InStr(1, itemName, "OEM", 1) > 0 Or InStr(1, itemName, "SKU", 1) > 0 Then
valuePos = InStr(itemBlock, """lastvalue"":""")
If valuePos > 0 Then
valueStart = valuePos + 13
valueEnd = InStr(valueStart, itemBlock, """")
itemValue = Mid(itemBlock, valueStart, valueEnd - valueStart)
' Store in dictionary with full item name as key (e.g., "Black Toner Part Number")
If Not partNumbers.Exists(itemName) Then
partNumbers.Add itemName, itemValue
debugPartNumbers = debugPartNumbers & "[" & itemName & "=" & itemValue & "] "
End If
End If
End If
currentPos = itemEnd + 1
Loop
' Debug disabled - uncomment to show part number matching debug info
' Response.Write("
")
' Second pass: Find level items below 20%
currentPos = InStr(suppliesJSON, """result"":[") + 11
Do While currentPos > 11 And currentPos < Len(suppliesJSON)
' Find next item
itemStart = InStr(currentPos, suppliesJSON, "{""itemid"":")
If itemStart = 0 Then Exit Do
' Find end of this item
itemEnd = InStr(itemStart, suppliesJSON, "},{")
If itemEnd = 0 Then
' Last item in array
itemEnd = InStr(itemStart, suppliesJSON, "}]")
End If
If itemEnd = 0 Then Exit Do
itemBlock = Mid(suppliesJSON, itemStart, itemEnd - itemStart + 1)
' Extract item name - "name":" is 8 characters
namePos = InStr(itemBlock, """name"":""")
If namePos > 0 Then
nameStart = namePos + 8
nameEnd = InStr(nameStart, itemBlock, """")
itemName = Mid(itemBlock, nameStart, nameEnd - nameStart)
Else
itemName = "Unknown"
End If
' Extract lastvalue - "lastvalue":" is 13 characters
valuePos = InStr(itemBlock, """lastvalue"":""")
If valuePos > 0 Then
valueStart = valuePos + 13
valueEnd = InStr(valueStart, itemBlock, """")
itemValue = Mid(itemBlock, valueStart, valueEnd - valueStart)
Else
itemValue = "0"
End If
' Extract status (0 = enabled, 1 = disabled) - "status":" is 10 characters
statusPos = InStr(itemBlock, """status"":""")
If statusPos > 0 Then
statusStart = statusPos + 10
statusEnd = InStr(statusStart, itemBlock, """")
itemStatus = Mid(itemBlock, statusStart, statusEnd - statusStart)
Else
itemStatus = "0"
End If
' Extract state (0 = normal, 1 = not supported) - "state":" is 9 characters
statePos = InStr(itemBlock, """state"":""")
If statePos > 0 Then
stateStart = statePos + 9
stateEnd = InStr(stateStart, itemBlock, """")
itemState = Mid(itemBlock, stateStart, stateEnd - stateStart)
Else
itemState = "0"
End If
' Convert value to number and check if below 20%
On Error Resume Next
numericValue = CDbl(itemValue)
On Error Goto 0
' Filter: Only show actual supply level items (must have "Level" in name)
isSupplyItem = False
If InStr(1, itemName, "Level", 1) > 0 Then
' Exclude non-supply items
If InStr(1, itemName, "Part Number", 1) = 0 And _
InStr(1, itemName, "ICMP", 1) = 0 And _
InStr(1, itemName, "ping", 1) = 0 And _
InStr(1, itemName, "loss", 1) = 0 And _
InStr(1, itemName, "response", 1) = 0 And _
InStr(1, itemName, "Hostname", 1) = 0 And _
InStr(1, itemName, "Model", 1) = 0 And _
InStr(1, itemName, "Serial", 1) = 0 And _
InStr(1, itemName, "Location", 1) = 0 And _
InStr(1, itemName, "Firmware", 1) = 0 And _
InStr(1, itemName, "Current", 1) = 0 And _
InStr(1, itemName, " Max", 1) = 0 Then
isSupplyItem = True
End If
End If
' Detect if this is a waste cartridge (works backwards - high % is bad)
isWasteItem = (InStr(1, itemName, "Waste", 1) > 0)
' Check if item should be shown based on type
showItem = False
If isSupplyItem And itemStatus = "0" And itemState = "0" Then
If isWasteItem Then
' Waste cartridge logic - MODEL SPECIFIC!
' Standard (HP, etc.): 0% = empty/ok, 100% = full/bad (alert when >=95%)
' Xerox EC series (EC8036, etc.): 0% = full/bad, 100% = empty/ok (INVERTED - alert when <=5%)
If isXeroxPrinter Then
' Xerox EC series waste: alert when at or BELOW 5% (inverted - low % means full)
If numericValue <= 5 And numericValue >= 0 Then
showItem = True
End If
Else
' Standard waste: alert when at or ABOVE 95% (nearly full)
If numericValue >= 95 And numericValue <= 100 Then
showItem = True
End If
End If
Else
' Regular supplies: alert when at or below 5% (running low)
If numericValue <= 5 And numericValue >= 0 Then
showItem = True
End If
End If
End If
If showItem Then
lowSuppliesFound = True
' Determine status indicator
If isWasteItem Then
If isXeroxPrinter Then
' Xerox EC series waste: INVERTED - low % = full/bad
If numericValue <= 5 Then
statusIcon = "zmdi-alert-circle"
statusColor = "#ff0000"
statusText = "Critical - Nearly Full"
ElseIf numericValue <= 10 Then
statusIcon = "zmdi-alert-triangle"
statusColor = "#ff6600"
statusText = "Very High"
Else
statusIcon = "zmdi-info"
statusColor = "#ffaa00"
statusText = "High"
End If
Else
' Standard waste (HP, etc.): high % = full/bad
If numericValue >= 95 Then
statusIcon = "zmdi-alert-circle"
statusColor = "#ff0000"
statusText = "Critical - Nearly Full"
ElseIf numericValue >= 90 Then
statusIcon = "zmdi-alert-triangle"
statusColor = "#ff6600"
statusText = "Very High"
Else
statusIcon = "zmdi-info"
statusColor = "#ffaa00"
statusText = "High"
End If
End If
Else
' Regular supply status (low % = bad)
' 0% = Critical (red), 1-5% = Warning (orange)
If numericValue = 0 Then
statusIcon = "zmdi-alert-circle"
statusColor = "#ff0000"
statusText = "Critical"
Else
statusIcon = "zmdi-alert-triangle"
statusColor = "#ff6600"
statusText = "Warning"
End If
End If
' Look up part numbers from hardcoded list (SNMP is unreliable)
' Returns "standard|metered" or "standard|alt|metered" or just "standard"
partNumberResult = GetSupplyPartNumbers(modelnumber, itemName)
stdPartNumber = ""
metPartNumber = ""
altPartNumber = ""
If InStr(partNumberResult, "|") > 0 Then
partNumberParts = Split(partNumberResult, "|")
stdPartNumber = partNumberParts(0)
If UBound(partNumberParts) >= 2 Then
' 3 options: std|alt|met
altPartNumber = partNumberParts(1)
metPartNumber = partNumberParts(2)
Else
' 2 options: std|met
metPartNumber = partNumberParts(1)
End If
Else
stdPartNumber = partNumberResult
End If
If stdPartNumber = "" Then stdPartNumber = "-"
' Get marketing names
stdMarketing = GetMarketingName(stdPartNumber)
metMarketing = ""
altMarketing = ""
If metPartNumber <> "" Then metMarketing = GetMarketingName(metPartNumber)
If altPartNumber <> "" Then altMarketing = GetMarketingName(altPartNumber)
' Build display string showing all options
If altPartNumber <> "" Then
' 3 options (EC8036 style)
displayPartNumber = "
"
If stdMarketing <> "" Then
displayPartNumber = displayPartNumber & "Std: " & Server.HTMLEncode(stdMarketing) & " (" & Server.HTMLEncode(stdPartNumber) & ") "
Else
displayPartNumber = displayPartNumber & "Std: " & Server.HTMLEncode(stdPartNumber) & " "
End If
If metMarketing <> "" Then
displayPartNumber = displayPartNumber & "Met: " & Server.HTMLEncode(metMarketing) & " (" & Server.HTMLEncode(metPartNumber) & ")"
Else
displayPartNumber = displayPartNumber & "Met: " & Server.HTMLEncode(metPartNumber)
End If
displayPartNumber = displayPartNumber & "
"
Else
' Single option
If stdMarketing <> "" Then
displayPartNumber = "" & Server.HTMLEncode(stdMarketing) & " " & Server.HTMLEncode(stdPartNumber) & ""
Else
displayPartNumber = Server.HTMLEncode(stdPartNumber)
End If
End If
' Calculate urgency score for sorting
' For regular supplies: lower % = higher urgency (5% = 95 urgency)
' For standard waste: higher % = higher urgency (95% = 95 urgency)
' For Xerox EC series waste: INVERTED - lower % = higher urgency (5% = 95 urgency)
If isWasteItem Then
If isXeroxPrinter Then
' Xerox EC series waste: inverted - low % is bad
urgencyScore = 100 - numericValue
Else
' Standard waste: high % is bad
urgencyScore = numericValue
End If
Else
' Regular supplies: low % is bad
urgencyScore = 100 - numericValue
End If
' Store alert data for later sorting
alertItem = Array( _
urgencyScore, _
vendor, _
printerid, _
printerwindowsname, _
machineid, _
machinenumber, _
modelnumber, _
numericValue, _
statusColor, _
displayPartNumber, _
itemName _
)
alertItems(alertCount) = alertItem
alertCount = alertCount + 1
End If
' Move to next item
currentPos = itemEnd + 1
Loop
End If
End If
End If
rs.MoveNext
Wend
' Sort alerts by urgency (highest urgency first = most critical)
' Simple bubble sort with error handling
On Error Resume Next
If alertCount > 1 Then
For i = 0 To alertCount - 2
For j = 0 To alertCount - i - 2
' alertItems(j)(0) is the urgency score
If Not IsEmpty(alertItems(j)) And Not IsEmpty(alertItems(j + 1)) Then
If CDbl(alertItems(j)(0)) < CDbl(alertItems(j + 1)(0)) Then
' Swap items
tempAlert = alertItems(j)
alertItems(j) = alertItems(j + 1)
alertItems(j + 1) = tempAlert
End If
End If
Next
Next
End If
' Output sorted alerts
If alertCount > 0 Then
lowSuppliesFound = True
For k = 0 To alertCount - 1
If Not IsEmpty(alertItems(k)) And IsArray(alertItems(k)) Then
outputItem = alertItems(k)
' Array indices: 0=urgencyScore, 1=vendor, 2=printerid, 3=printerwindowsname,
' 4=machineid, 5=machinenumber, 6=modelnumber, 7=numericValue,
' 8=statusColor, 9=displayPartNumber, 10=itemName
Response.Write("