Fix dualpath propagation, getShopfloorPCs filtering, USB management, and printer features

- Fix dualpath PC propagation direction (Equipment->PC) in api.asp and db_helpers.asp
- Fix early exit in CreatePCMachineRelationship preventing propagation
- Fix getShopfloorPCs to filter machinetypeid IN (33,34,35) instead of >= 33
- Fix getShopfloorPCs to show equipment numbers via GROUP_CONCAT subquery
- Add detailed PropagateDP logging for dualpath debugging
- Default "Show on Shopfloor Dashboard" checkbox to checked in addnotification.asp
- Add USB label batch printing, single USB labels, and USB history pages
- Add printer supplies tracking and toner report enhancements
- Add uptime map visualization page
- Add dashboard/lobby display SQL migration
- Update CLAUDE.md with IIS 401 workaround documentation
- Update TODO.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-02-03 10:44:55 -05:00
parent 8945fe2a0a
commit e382a3246e
27 changed files with 1926 additions and 255 deletions

View File

@@ -154,6 +154,31 @@ PCs are in the machines table, identified by:
- `pctypeid IS NOT NULL` - `pctypeid IS NOT NULL`
- `machinetypeid IN (33, 34, 35)` - `machinetypeid IN (33, 34, 35)`
### IIS 401 Error on New ASP Files
**Problem:** When creating new ASP files from scratch, they may return HTTP 200 on the first request but HTTP 401 Unauthorized on all subsequent requests.
**Symptoms:**
- First curl/browser request to new .asp file returns 200
- All following requests return 401 Unauthorized
- Existing files (like printerqrbatch.asp) work fine with consistent 200 responses
- Adding `<%@ Language=VBScript %>` directive alone does NOT fix it
**Solution:** Instead of creating new ASP files from scratch, **copy an existing working ASP file** to the new filename, then modify its contents:
```bash
# DON'T do this (creates file from scratch - will get 401 errors):
# Write tool to create newfile.asp
# DO this instead (copy existing working file):
cp printerqrbatch.asp newfile.asp
# Then use Edit tool to replace the content
```
**Why this works:** The exact root cause is unclear, but appears related to how IIS handles file metadata/permissions for newly created files vs. copied files. Copying preserves whatever attributes IIS needs to serve the file correctly.
**Verified fix:** Files created by copying printerqrbatch.asp consistently return 200 on multiple sequential requests.
## API Endpoints ## API Endpoints
**Base URL:** `http://192.168.122.151:8080/api.asp` **Base URL:** `http://192.168.122.151:8080/api.asp`

View File

@@ -1,15 +1,12 @@
# ShopDB - Future TODO List # ShopDB - Future TODO List
**Created:** 2025-11-25 **Created:** 2025-11-25
**Last Updated:** 2025-11-25 **Last Updated:** 2026-01-16
--- ---
## High Priority ## High Priority
### Outstanding Bugs
- [ ] Fix displaysubnet.asp - Runtime error (subscript out of range)
### Uncommitted Changes ### Uncommitted Changes
- [ ] Review and commit pending changes: - [ ] Review and commit pending changes:
- api.asp - api.asp
@@ -68,6 +65,9 @@
## Completed (Reference) ## Completed (Reference)
### December 2025
- [x] Fix displaysubnet.asp runtime error - Phase 2 schema update (Dec 29)
### November 2025 ### November 2025
- [x] Phase 1: Schema changes (Nov 6) - [x] Phase 1: Schema changes (Nov 6)
- [x] Phase 2: PC migration (Nov 10) - [x] Phase 2: PC migration (Nov 10)

View File

@@ -311,7 +311,7 @@
<div class="form-group"> <div class="form-group">
<div class="custom-control custom-checkbox"> <div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="isshopfloor" name="isshopfloor" value="1"> <input type="checkbox" class="custom-control-input" id="isshopfloor" name="isshopfloor" value="1" checked>
<label class="custom-control-label" for="isshopfloor">Show on Shopfloor Dashboard</label> <label class="custom-control-label" for="isshopfloor">Show on Shopfloor Dashboard</label>
</div> </div>
<small class="form-text text-muted">Check this to display on the shopfloor TV dashboard (72-hour window)</small> <small class="form-text text-muted">Check this to display on the shopfloor TV dashboard (72-hour window)</small>

View File

@@ -3,6 +3,8 @@
<head> <head>
<!--#include file="./includes/header.asp"--> <!--#include file="./includes/header.asp"-->
<!--#include file="./includes/sql.asp"--> <!--#include file="./includes/sql.asp"-->
<!-- JsBarcode for barcode generation -->
<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js"></script>
</head> </head>
<% <%
@@ -60,15 +62,22 @@ End If
<form id="scanForm" method="post" action="./saveusbdirect.asp"> <form id="scanForm" method="post" action="./saveusbdirect.asp">
<div class="form-group"> <div class="form-group">
<label for="serialnumber">Serial Number</label> <label for="serialnumber">Serial Number</label>
<input <div class="input-group">
type="text" <input
class="form-control form-control-lg" type="text"
id="serialnumber" class="form-control form-control-lg"
name="serialnumber" id="serialnumber"
placeholder="Scan barcode or type serial number..." name="serialnumber"
autocomplete="off" placeholder="Scan barcode or type serial number..."
autofocus autocomplete="off"
style="font-size: 24px; text-align: center; padding: 20px; font-family: monospace; letter-spacing: 2px;"> autofocus
style="font-size: 24px; text-align: center; padding: 20px; font-family: monospace; letter-spacing: 2px;">
<div class="input-group-append">
<button type="button" class="btn btn-info btn-lg" id="generateBtn" title="Generate random 8-digit serial">
<i class="zmdi zmdi-refresh"></i> Generate
</button>
</div>
</div>
</div> </div>
<div class="form-group"> <div class="form-group">
@@ -106,6 +115,25 @@ Set rsBU = Nothing
</button> </button>
</div> </div>
</form> </form>
<!-- Barcode Preview Section -->
<div id="barcodeSection" style="display:none; margin-top:30px; border-top:1px solid #444; padding-top:20px;">
<h6><i class="zmdi zmdi-label"></i> Barcode Label Preview</h6>
<p class="text-muted small">Print this on a Uline sticker label. Click the barcode to print.</p>
<div id="barcodePreview" style="background:#fff; padding:10px; display:inline-block; border-radius:4px; cursor:pointer;" onclick="printBarcode()" title="Click to print">
<svg id="barcodeSvg"></svg>
</div>
<div style="margin-top:10px;">
<button type="button" class="btn btn-sm btn-outline-light" onclick="printBarcode()">
<i class="zmdi zmdi-print"></i> Quick Print
</button>
<a id="labelPageLink" href="#" target="_blank" class="btn btn-sm btn-outline-info" style="display:none;">
<i class="zmdi zmdi-label"></i> Print Label Page
</a>
</div>
</div>
</div> </div>
<div id="successArea" style="display:none;"> <div id="successArea" style="display:none;">
@@ -191,6 +219,23 @@ $(document).ready(function() {
$('#serialnumber').val('').focus(); $('#serialnumber').val('').focus();
} }
// Generate random 8-digit serial number
$('#generateBtn').on('click', function() {
var serial = generateSerial();
$('#serialnumber').val(serial);
updateBarcode(serial);
});
// Update barcode when serial number changes
$('#serialnumber').on('input', function() {
var serial = $(this).val().trim();
if (serial.length >= 3) {
updateBarcode(serial);
} else {
$('#barcodeSection').hide();
}
});
// Validate form before submission // Validate form before submission
$('#scanForm').on('submit', function(e) { $('#scanForm').on('submit', function(e) {
var serial = $('#serialnumber').val().trim(); var serial = $('#serialnumber').val().trim();
@@ -202,6 +247,76 @@ $(document).ready(function() {
} }
}); });
}); });
// Generate random 8-digit serial number
function generateSerial() {
var min = 10000000; // 8 digits minimum
var max = 99999999; // 8 digits maximum
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// Update barcode preview
function updateBarcode(serial) {
if (serial && serial.length >= 3) {
try {
JsBarcode("#barcodeSvg", serial, {
format: "CODE128",
width: 1.5,
height: 30,
displayValue: true,
fontSize: 12,
margin: 5,
textMargin: 2
});
$('#barcodeSection').show();
$('#labelPageLink').attr('href', './usbsingle.asp?serial=' + encodeURIComponent(serial)).show();
} catch (e) {
console.error('Barcode error:', e);
$('#barcodeSection').hide();
$('#labelPageLink').hide();
}
} else {
$('#barcodeSection').hide();
$('#labelPageLink').hide();
}
}
// Print barcode label
function printBarcode() {
var serial = $('#serialnumber').val().trim();
if (!serial) {
alert('Please enter a serial number first');
return;
}
// Create print window with just the barcode
var printWindow = window.open('', '_blank', 'width=300,height=200');
printWindow.document.write('<html><head><title>USB Label - ' + serial + '</title>');
printWindow.document.write('<style>');
printWindow.document.write('body { margin: 0; padding: 5px; font-family: Arial, sans-serif; }');
printWindow.document.write('.label { text-align: center; }');
printWindow.document.write('@media print { @page { size: 1in 0.5in; margin: 0; } }');
printWindow.document.write('</style>');
printWindow.document.write('<scr' + 'ipt src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js"></scr' + 'ipt>');
printWindow.document.write('</head><body>');
printWindow.document.write('<div class="label">');
printWindow.document.write('<svg id="printBarcode"></svg>');
printWindow.document.write('</div>');
printWindow.document.write('<scr' + 'ipt>');
printWindow.document.write('JsBarcode("#printBarcode", "' + serial + '", {');
printWindow.document.write(' format: "CODE128",');
printWindow.document.write(' width: 1.2,');
printWindow.document.write(' height: 25,');
printWindow.document.write(' displayValue: true,');
printWindow.document.write(' fontSize: 10,');
printWindow.document.write(' margin: 2,');
printWindow.document.write(' textMargin: 1');
printWindow.document.write('});');
printWindow.document.write('setTimeout(function() { window.print(); window.close(); }, 300);');
printWindow.document.write('</scr' + 'ipt>');
printWindow.document.write('</body></html>');
printWindow.document.close();
}
</script> </script>
</body> </body>

77
api.asp
View File

@@ -853,8 +853,9 @@ Sub GetShopfloorPCs()
' Build WHERE clause with optional filters ' Build WHERE clause with optional filters
' Dashboard (11) and Lobby Display (12) can be on any subnet, others require 10.134.* ' Dashboard (11) and Lobby Display (12) can be on any subnet, others require 10.134.*
' Only include actual PCs (machinetypeid 33-35), not USB devices (44), Part Markers (45), Firewalls (46), etc.
whereClause = "WHERE m.isactive = 1 " & _ whereClause = "WHERE m.isactive = 1 " & _
"AND m.machinetypeid >= 33 " "AND m.machinetypeid IN (33, 34, 35) "
' Add pctypeid filter if provided ' Add pctypeid filter if provided
If filterPcTypeId <> "" And IsNumeric(filterPcTypeId) Then If filterPcTypeId <> "" And IsNumeric(filterPcTypeId) Then
@@ -881,7 +882,11 @@ Sub GetShopfloorPCs()
"m.loggedinuser, m.lastupdated, m.pctypeid, m.businessunitid, " & _ "m.loggedinuser, m.lastupdated, m.pctypeid, m.businessunitid, " & _
"c.address AS ipaddress, " & _ "c.address AS ipaddress, " & _
"COALESCE(pt.typename, 'Uncategorized') AS pctype, " & _ "COALESCE(pt.typename, 'Uncategorized') AS pctype, " & _
"COALESCE(bu.businessunit, 'TBD') AS businessunit " & _ "COALESCE(bu.businessunit, 'TBD') AS businessunit, " & _
"(SELECT GROUP_CONCAT(eq2.machinenumber ORDER BY eq2.machinenumber SEPARATOR ', ') " & _
"FROM machinerelationships mr2 " & _
"JOIN machines eq2 ON mr2.machineid = eq2.machineid AND eq2.pctypeid IS NULL " & _
"WHERE mr2.related_machineid = m.machineid AND mr2.relationshiptypeid = 3 AND mr2.isactive = 1) AS equipment_number " & _
"FROM machines m " & _ "FROM machines m " & _
"LEFT JOIN communications c ON m.machineid = c.machineid AND c.isprimary = 1 AND c.comstypeid = 1 " & _ "LEFT JOIN communications c ON m.machineid = c.machineid AND c.isprimary = 1 AND c.comstypeid = 1 " & _
"LEFT JOIN pctype pt ON m.pctypeid = pt.pctypeid " & _ "LEFT JOIN pctype pt ON m.pctypeid = pt.pctypeid " & _
@@ -907,7 +912,7 @@ Sub GetShopfloorPCs()
pcData = "{" pcData = "{"
pcData = pcData & """machineid"":" & rsPC("machineid") & "," pcData = pcData & """machineid"":" & rsPC("machineid") & ","
pcData = pcData & """hostname"":""" & EscapeJSON(rsPC("hostname") & "") & """," pcData = pcData & """hostname"":""" & EscapeJSON(rsPC("hostname") & "") & ""","
pcData = pcData & """machinenumber"":""" & EscapeJSON(rsPC("machinenumber") & "") & """," pcData = pcData & """machinenumber"":""" & EscapeJSON(rsPC("equipment_number") & "") & ""","
pcData = pcData & """serialnumber"":""" & EscapeJSON(rsPC("serialnumber") & "") & """," pcData = pcData & """serialnumber"":""" & EscapeJSON(rsPC("serialnumber") & "") & ""","
pcData = pcData & """ipaddress"":""" & EscapeJSON(rsPC("ipaddress") & "") & """," pcData = pcData & """ipaddress"":""" & EscapeJSON(rsPC("ipaddress") & "") & ""","
pcData = pcData & """loggedinuser"":""" & EscapeJSON(rsPC("loggedinuser") & "") & """," pcData = pcData & """loggedinuser"":""" & EscapeJSON(rsPC("loggedinuser") & "") & ""","
@@ -1794,10 +1799,18 @@ Function CreatePCMachineRelationship(pcMachineid, machineNumber)
Set rsResult = objConn.Execute(strSQL) Set rsResult = objConn.Execute(strSQL)
If Not rsResult.EOF Then If Not rsResult.EOF Then
' Relationship already exists ' Relationship already exists - still propagate to dualpath partners
LogToFile "CreatePCMachineRelationship: Relationship already exists (relationshipid=" & rsResult("relationshipid") & ")" LogToFile "CreatePCMachineRelationship: Relationship already exists (relationshipid=" & rsResult("relationshipid") & ")"
rsResult.Close rsResult.Close
Set rsResult = Nothing Set rsResult = Nothing
' Propagate controller to any dualpath machines that may be missing it
Dim dualpathCountExisting
dualpathCountExisting = PropagateControllerToDualpathMachinesAPI(CLng(equipmentMachineid), CLng(pcMachineid))
If dualpathCountExisting > 0 Then
LogToFile "Propagated controller to " & dualpathCountExisting & " dualpath machine(s) from existing relationship"
End If
CreatePCMachineRelationship = True CreatePCMachineRelationship = True
Exit Function Exit Function
End If End If
@@ -1845,49 +1858,85 @@ End Function
Function PropagateControllerToDualpathMachinesAPI(equipmentMachineid, pcMachineid) Function PropagateControllerToDualpathMachinesAPI(equipmentMachineid, pcMachineid)
On Error Resume Next On Error Resume Next
LogToFile "PropagateDP: Starting for equipment=" & equipmentMachineid & " pc=" & pcMachineid
Dim rsDP, rsDPCheck, controlsTypeID, dualpathMachineId, cnt Dim rsDP, rsDPCheck, controlsTypeID, dualpathMachineId, cnt
cnt = 0 cnt = 0
' Get Controls relationship type ID ' Get Controls relationship type ID
Set rsDP = objConn.Execute("SELECT relationshiptypeid FROM relationshiptypes WHERE relationshiptype = 'Controls'") Set rsDP = objConn.Execute("SELECT relationshiptypeid FROM relationshiptypes WHERE relationshiptype = 'Controls'")
If Err.Number <> 0 Then
LogToFile "PropagateDP: ERROR getting Controls type: " & Err.Description
Err.Clear
PropagateControllerToDualpathMachinesAPI = 0
Exit Function
End If
If rsDP.EOF Then If rsDP.EOF Then
LogToFile "PropagateDP: Controls relationship type not found in relationshiptypes table"
PropagateControllerToDualpathMachinesAPI = 0 PropagateControllerToDualpathMachinesAPI = 0
rsDP.Close rsDP.Close
Set rsDP = Nothing Set rsDP = Nothing
Exit Function Exit Function
End If End If
controlsTypeID = CLng(rsDP("relationshiptypeid")) controlsTypeID = CLng(rsDP("relationshiptypeid"))
LogToFile "PropagateDP: Controls type ID = " & controlsTypeID
rsDP.Close rsDP.Close
Set rsDP = Nothing Set rsDP = Nothing
' Find all machines with dualpath relationship to this equipment ' Find all machines with dualpath relationship to this equipment
Set rsDP = objConn.Execute("SELECT related_machineid FROM machinerelationships mr " & _ Dim dpSQL
dpSQL = "SELECT related_machineid FROM machinerelationships mr " & _
"JOIN relationshiptypes rt ON mr.relationshiptypeid = rt.relationshiptypeid " & _ "JOIN relationshiptypes rt ON mr.relationshiptypeid = rt.relationshiptypeid " & _
"WHERE mr.machineid = " & CLng(equipmentMachineid) & " " & _ "WHERE mr.machineid = " & CLng(equipmentMachineid) & " " & _
"AND rt.relationshiptype = 'Dualpath' AND mr.isactive = 1") "AND rt.relationshiptype = 'Dualpath' AND mr.isactive = 1"
LogToFile "PropagateDP: Finding dualpath partners: " & dpSQL
Set rsDP = objConn.Execute(dpSQL)
If Err.Number <> 0 Then
LogToFile "PropagateDP: ERROR finding dualpath partners: " & Err.Description
Err.Clear
PropagateControllerToDualpathMachinesAPI = 0
Exit Function
End If
If rsDP.EOF Then
LogToFile "PropagateDP: No dualpath partners found for equipment " & equipmentMachineid
End If
Do While Not rsDP.EOF Do While Not rsDP.EOF
dualpathMachineId = CLng(rsDP("related_machineid")) dualpathMachineId = CLng(rsDP("related_machineid"))
LogToFile "PropagateDP: Found dualpath partner machineid=" & dualpathMachineId
' Check if this dualpath machine already has a Controls relationship with this PC ' Check if this dualpath machine already has a Controls relationship with this PC
Set rsDPCheck = objConn.Execute("SELECT relationshipid FROM machinerelationships " & _ ' Pattern: Equipment -> Controls -> PC (machineid=equipment, related_machineid=PC)
"WHERE machineid = " & CLng(pcMachineid) & " " & _ Dim checkSQL
"AND related_machineid = " & dualpathMachineId & " " & _ checkSQL = "SELECT relationshipid FROM machinerelationships " & _
"AND relationshiptypeid = " & controlsTypeID & " AND isactive = 1") "WHERE machineid = " & dualpathMachineId & " " & _
"AND related_machineid = " & CLng(pcMachineid) & " " & _
"AND relationshiptypeid = " & controlsTypeID & " AND isactive = 1"
LogToFile "PropagateDP: Checking existing: " & checkSQL
Set rsDPCheck = objConn.Execute(checkSQL)
If rsDPCheck.EOF Then If rsDPCheck.EOF Then
' Create Controls relationship: PC -> Dualpath Machine LogToFile "PropagateDP: No existing relationship, creating new one"
' Create Controls relationship: Dualpath Equipment -> PC (matches existing data pattern)
Dim cmdDPAPI Dim cmdDPAPI
Set cmdDPAPI = Server.CreateObject("ADODB.Command") Set cmdDPAPI = Server.CreateObject("ADODB.Command")
cmdDPAPI.ActiveConnection = objConn cmdDPAPI.ActiveConnection = objConn
cmdDPAPI.CommandText = "INSERT INTO machinerelationships (machineid, related_machineid, relationshiptypeid, isactive) VALUES (?, ?, ?, 1)" cmdDPAPI.CommandText = "INSERT INTO machinerelationships (machineid, related_machineid, relationshiptypeid, isactive) VALUES (?, ?, ?, 1)"
cmdDPAPI.Parameters.Append cmdDPAPI.CreateParameter("@pcid", 3, 1, , CLng(pcMachineid))
cmdDPAPI.Parameters.Append cmdDPAPI.CreateParameter("@equipid", 3, 1, , dualpathMachineId) cmdDPAPI.Parameters.Append cmdDPAPI.CreateParameter("@equipid", 3, 1, , dualpathMachineId)
cmdDPAPI.Parameters.Append cmdDPAPI.CreateParameter("@pcid", 3, 1, , CLng(pcMachineid))
cmdDPAPI.Parameters.Append cmdDPAPI.CreateParameter("@reltypeid", 3, 1, , controlsTypeID) cmdDPAPI.Parameters.Append cmdDPAPI.CreateParameter("@reltypeid", 3, 1, , controlsTypeID)
cmdDPAPI.Execute cmdDPAPI.Execute
If Err.Number <> 0 Then
LogToFile "PropagateDP: ERROR inserting relationship: " & Err.Description
Err.Clear
Else
cnt = cnt + 1
LogToFile "PropagateDP: SUCCESS created Controls relationship: Equipment " & dualpathMachineId & " -> PC " & pcMachineid
End If
Set cmdDPAPI = Nothing Set cmdDPAPI = Nothing
cnt = cnt + 1 Else
LogToFile "Created dualpath Controls relationship: Equipment " & dualpathMachineId & " controlled by PC " & pcMachineid LogToFile "PropagateDP: Relationship already exists (id=" & rsDPCheck("relationshipid") & ")"
End If End If
rsDPCheck.Close rsDPCheck.Close
Set rsDPCheck = Nothing Set rsDPCheck = Nothing

View File

@@ -68,6 +68,20 @@
"LEFT JOIN vendors v ON m.vendorid = v.vendorid " & _ "LEFT JOIN vendors v ON m.vendorid = v.vendorid " & _
"LEFT JOIN communications c ON mac.machineid = c.machineid AND c.isprimary = 1 AND c.comstypeid = 1 " & _ "LEFT JOIN communications c ON mac.machineid = c.machineid AND c.isprimary = 1 AND c.comstypeid = 1 " & _
"WHERE mac.machineid = " & CLng(deviceId) & " AND mac.machinetypeid = 19" "WHERE mac.machineid = " & CLng(deviceId) & " AND mac.machinetypeid = 19"
Case "firewall"
machineTypeId = 46
editUrl = "devicefirewall.asp?id=" & deviceId
listUrl = "networkdevices.asp?filter=Firewall"
strSQL = "SELECT mac.machineid, mac.alias AS firewallname, mac.machinenotes AS description, " & _
"mac.maptop, mac.mapleft, mac.isactive, mac.serialnumber, mac.fqdn, mac.logicmonitorurl, " & _
"v.vendor, m.modelnumber, m.image, c.address AS ipaddress, c.macaddress, " & _
"NULL AS idfname, 'Firewall' AS devicetype, " & _
"mac.alias AS devicename " & _
"FROM machines mac " & _
"LEFT JOIN models m ON mac.modelnumberid = m.modelnumberid " & _
"LEFT JOIN vendors v ON m.vendorid = v.vendorid " & _
"LEFT JOIN communications c ON mac.machineid = c.machineid AND c.isprimary = 1 AND c.comstypeid = 1 " & _
"WHERE mac.machineid = " & CLng(deviceId) & " AND mac.machinetypeid = 46"
Case "camera" Case "camera"
machineTypeId = 18 machineTypeId = 18
editUrl = "devicecamera.asp?id=" & deviceId editUrl = "devicecamera.asp?id=" & deviceId
@@ -121,6 +135,8 @@
deviceName = rs("servername") deviceName = rs("servername")
Case "switch" Case "switch"
deviceName = rs("switchname") deviceName = rs("switchname")
Case "firewall"
deviceName = rs("firewallname")
Case "camera" Case "camera"
deviceName = rs("cameraname") deviceName = rs("cameraname")
Case "accesspoint", "access point" Case "accesspoint", "access point"
@@ -206,7 +222,7 @@
<% <%
Dim showLogicMonitor Dim showLogicMonitor
showLogicMonitor = False showLogicMonitor = False
If LCase(deviceType) = "server" Or LCase(deviceType) = "switch" Or LCase(deviceType) = "accesspoint" Or LCase(deviceType) = "access point" Then If LCase(deviceType) = "server" Or LCase(deviceType) = "switch" Or LCase(deviceType) = "firewall" Or LCase(deviceType) = "accesspoint" Or LCase(deviceType) = "access point" Then
If Not IsNull(rs("logicmonitorurl")) Then If Not IsNull(rs("logicmonitorurl")) Then
If rs("logicmonitorurl") & "" <> "" Then showLogicMonitor = True If rs("logicmonitorurl") & "" <> "" Then showLogicMonitor = True
End If End If

View File

@@ -62,7 +62,7 @@
<div class="clearfix"></div> <div class="clearfix"></div>
<div class="content-wrapper"> <div class="content-wrapper">
<div class="row"> <div class="row">
<div class="col-xl-auto"> <div class="col-12">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:15px;"> <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:15px;">

View File

@@ -407,8 +407,8 @@ End If
<div class="tab-pane" id="relationships"> <div class="tab-pane" id="relationships">
<h5 class="mb-3">Machine Relationships</h5> <h5 class="mb-3">Machine Relationships</h5>
<!-- Controlling PCs --> <!-- Connected PCs -->
<h6 class="mt-3 mb-2"><i class="zmdi zmdi-desktop-mac"></i> Controlled By PC</h6> <h6 class="mt-3 mb-2"><i class="zmdi zmdi-desktop-mac"></i> Connected PCs</h6>
<div class="table-responsive mb-4"> <div class="table-responsive mb-4">
<table class="table table-hover table-striped"> <table class="table table-hover table-striped">
<thead> <thead>
@@ -421,36 +421,54 @@ End If
</thead> </thead>
<tbody> <tbody>
<% <%
' Query PCs that control this machine (directly or via dualpath) ' Query ALL PCs related to this machine via machinerelationships
' Check both directions - the PC is identified by pctypeid IS NOT NULL ' Check both directions - the PC is identified by pctypeid IS NOT NULL
' Use GROUP_CONCAT to combine multiple IPs into one row per PC ' Use GROUP_CONCAT to combine multiple IPs into one row per PC
strSQL2 = "SELECT m.machineid, m.machinenumber, m.hostname, " & _ strSQL2 = "SELECT m.machineid, m.machinenumber, m.hostname, rt.relationshiptype, " & _
"GROUP_CONCAT(DISTINCT c.address ORDER BY c.address SEPARATOR ', ') as address, 'Controls' as relationshiptype " & _ "GROUP_CONCAT(DISTINCT c.address ORDER BY c.address SEPARATOR ', ') as address " & _
"FROM machinerelationships mr " & _ "FROM machinerelationships mr " & _
"JOIN relationshiptypes rt ON mr.relationshiptypeid = rt.relationshiptypeid " & _
"JOIN machines m ON (mr.machineid = m.machineid OR mr.related_machineid = m.machineid) " & _ "JOIN machines m ON (mr.machineid = m.machineid OR mr.related_machineid = m.machineid) " & _
"LEFT JOIN communications c ON m.machineid = c.machineid AND c.comstypeid IN (1, 3) AND c.isactive = 1 " & _ "LEFT JOIN communications c ON m.machineid = c.machineid AND c.comstypeid IN (1, 3) AND c.isactive = 1 " & _
"WHERE (mr.machineid = ? OR mr.related_machineid = ?) AND mr.relationshiptypeid = 3 " & _ "WHERE (mr.machineid = ? OR mr.related_machineid = ?) " & _
" AND m.pctypeid IS NOT NULL AND m.machineid <> ? AND mr.isactive = 1 " & _ " AND m.pctypeid IS NOT NULL AND m.machineid <> ? AND mr.isactive = 1 " & _
"GROUP BY m.machineid, m.machinenumber, m.hostname" "GROUP BY m.machineid, m.machinenumber, m.hostname, rt.relationshiptype " & _
"ORDER BY rt.relationshiptype, m.hostname"
Set rs2 = ExecuteParameterizedQuery(objConn, strSQL2, Array(machineid, machineid, machineid)) Set rs2 = ExecuteParameterizedQuery(objConn, strSQL2, Array(machineid, machineid, machineid))
Dim pcHostname, pcIP, pcMachineID Dim pcHostname, pcIP, pcMachineID, pcLocation, pcRelType
If rs2.EOF Then If rs2.EOF Then
Response.Write("<tr><td colspan='4' class='text-muted text-center'>No controlling PC assigned</td></tr>") Response.Write("<tr><td colspan='4' class='text-muted text-center'>No connected PCs</td></tr>")
Else Else
Do While Not rs2.EOF Do While Not rs2.EOF
pcHostname = rs2("hostname") & "" pcHostname = rs2("hostname") & ""
pcIP = rs2("address") & "" pcIP = rs2("address") & ""
pcMachineID = rs2("machineid") pcMachineID = rs2("machineid")
pcLocation = rs2("machinenumber") & ""
pcRelType = rs2("relationshiptype") & ""
If pcHostname = "" Then pcHostname = rs2("machinenumber") & "" If pcHostname = "" Then pcHostname = pcLocation
If pcIP = "" Then pcIP = "<span class='text-muted'>N/A</span>" If pcIP = "" Then pcIP = "<span class='text-muted'>N/A</span>"
If pcLocation = "" Then pcLocation = "N/A"
' Badge color based on relationship type
Dim pcRelBadge
Select Case LCase(pcRelType)
Case "controls"
pcRelBadge = "badge-primary"
Case "dualpath"
pcRelBadge = "badge-warning"
Case "connected to"
pcRelBadge = "badge-success"
Case Else
pcRelBadge = "badge-info"
End Select
Response.Write("<tr>") Response.Write("<tr>")
Response.Write("<td><a href='./displaypc.asp?machineid=" & pcMachineID & "'>" & Server.HTMLEncode(pcHostname) & "</a></td>") Response.Write("<td><a href='./displaypc.asp?machineid=" & pcMachineID & "'>" & Server.HTMLEncode(pcHostname) & "</a></td>")
Response.Write("<td>" & pcIP & "</td>") Response.Write("<td>" & pcIP & "</td>")
Response.Write("<td class='text-center'><a href='#' class='location-link text-info' data-machineid='" & pcMachineID & "' data-name='" & Server.HTMLEncode(pcHostname) & "'><i class='zmdi zmdi-pin' style='font-size:1.2rem;'></i></a></td>") Response.Write("<td>" & Server.HTMLEncode(pcLocation) & " <a href='#' class='location-link text-info' data-machineid='" & pcMachineID & "' data-name='" & Server.HTMLEncode(pcHostname) & "'><i class='zmdi zmdi-pin'></i></a></td>")
Response.Write("<td><span class='badge badge-primary'>" & Server.HTMLEncode(rs2("relationshiptype") & "") & "</span></td>") Response.Write("<td><span class='badge " & pcRelBadge & "'>" & Server.HTMLEncode(pcRelType) & "</span></td>")
Response.Write("</tr>") Response.Write("</tr>")
rs2.MoveNext rs2.MoveNext
Loop Loop

View File

@@ -87,14 +87,14 @@ Dim theme, strSQL, rs, objConn
<% <%
' Build WHERE clause with optional BU filter ' Build WHERE clause with optional BU filter
' NOTE: Exclude LocationOnly (1), network devices (16-20), and PC types (33+) ' NOTE: Exclude LocationOnly (1), network devices (16-20, 46), and PC types (33)
Dim whereClause Dim whereClause
whereClause = "models.machinetypeid = machinetypes.machinetypeid AND " &_ whereClause = "models.machinetypeid = machinetypes.machinetypeid AND " &_
"machines.modelnumberid = models.modelnumberid AND " &_ "machines.modelnumberid = models.modelnumberid AND " &_
"models.vendorid = vendors.vendorid AND " &_ "models.vendorid = vendors.vendorid AND " &_
"machines.businessunitid = businessunits.businessunitID AND " &_ "machines.businessunitid = businessunits.businessunitID AND " &_
"machines.isactive = 1 AND islocationonly=0 AND " &_ "machines.isactive = 1 AND islocationonly=0 AND " &_
"models.machinetypeid NOT IN (1, 16, 17, 18, 19, 20) AND models.machinetypeid < 33" "models.machinetypeid NOT IN (1, 16, 17, 18, 19, 20, 33, 46)"
' Add BU filter if specified ' Add BU filter if specified
If filterBU <> "" And IsNumeric(filterBU) Then If filterBU <> "" And IsNumeric(filterBU) Then

View File

@@ -524,8 +524,8 @@ End If
<% <%
End If End If
%> %>
<!-- Machines Controlled by This PC --> <!-- Connected Equipment -->
<h6 class="mt-3 mb-2"><i class="zmdi zmdi-settings"></i> Machines Controlled by This PC</h6> <h6 class="mt-3 mb-2"><i class="zmdi zmdi-settings"></i> Connected Equipment</h6>
<div class="table-responsive mb-4"> <div class="table-responsive mb-4">
<table class="table table-hover table-striped"> <table class="table table-hover table-striped">
<thead> <thead>
@@ -533,42 +533,59 @@ End If
<th>Machine Number</th> <th>Machine Number</th>
<th>Type</th> <th>Type</th>
<th>Model</th> <th>Model</th>
<th>Location</th>
<th>Relationship</th> <th>Relationship</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<% <%
' Query machines that THIS PC controls ' Query ALL equipment related to this PC via machinerelationships
' Check both directions - the equipment is identified by pctypeid IS NULL ' Check both directions - the equipment is identified by pctypeid IS NULL
strSQL2 = "SELECT m.machineid, m.machinenumber, mt.machinetype, mo.modelnumber, 'Controls' as relationshiptype " & _ strSQL2 = "SELECT m.machineid, m.machinenumber, mt.machinetype, mo.modelnumber, rt.relationshiptype " & _
"FROM machinerelationships mr " & _ "FROM machinerelationships mr " & _
"JOIN relationshiptypes rt ON mr.relationshiptypeid = rt.relationshiptypeid " & _
"JOIN machines m ON (mr.machineid = m.machineid OR mr.related_machineid = m.machineid) " & _ "JOIN machines m ON (mr.machineid = m.machineid OR mr.related_machineid = m.machineid) " & _
"LEFT JOIN models mo ON m.modelnumberid = mo.modelnumberid " & _ "LEFT JOIN models mo ON m.modelnumberid = mo.modelnumberid " & _
"LEFT JOIN machinetypes mt ON mo.machinetypeid = mt.machinetypeid " & _ "LEFT JOIN machinetypes mt ON mo.machinetypeid = mt.machinetypeid " & _
"WHERE (mr.machineid = ? OR mr.related_machineid = ?) AND mr.relationshiptypeid = 3 " & _ "WHERE (mr.machineid = ? OR mr.related_machineid = ?) " & _
" AND m.pctypeid IS NULL AND m.machineid <> ? AND mr.isactive = 1 " & _ " AND m.pctypeid IS NULL AND m.machineid <> ? AND mr.isactive = 1 " & _
"ORDER BY machinenumber" "ORDER BY rt.relationshiptype, m.machinenumber"
Set rs2 = ExecuteParameterizedQuery(objConn, strSQL2, Array(machineid, machineid, machineid)) Set rs2 = ExecuteParameterizedQuery(objConn, strSQL2, Array(machineid, machineid, machineid))
If rs2.EOF Then If rs2.EOF Then
Response.Write("<tr><td colspan='4' class='text-muted text-center'>This PC does not control any machines</td></tr>") Response.Write("<tr><td colspan='5' class='text-muted text-center'>No connected equipment</td></tr>")
Else Else
Do While Not rs2.EOF Do While Not rs2.EOF
Dim ctrlMachineNum, ctrlType, ctrlModel, ctrlMachineID Dim ctrlMachineNum, ctrlType, ctrlModel, ctrlMachineID, ctrlRelType
ctrlMachineNum = rs2("machinenumber") & "" ctrlMachineNum = rs2("machinenumber") & ""
ctrlType = rs2("machinetype") & "" ctrlType = rs2("machinetype") & ""
ctrlModel = rs2("modelnumber") & "" ctrlModel = rs2("modelnumber") & ""
ctrlMachineID = rs2("machineid") ctrlMachineID = rs2("machineid")
ctrlRelType = rs2("relationshiptype") & ""
If ctrlMachineNum = "" Then ctrlMachineNum = "<span class='text-muted'>N/A</span>" If ctrlMachineNum = "" Then ctrlMachineNum = "N/A"
If ctrlType = "" Then ctrlType = "<span class='text-muted'>N/A</span>" If ctrlType = "" Then ctrlType = "<span class='text-muted'>N/A</span>"
If ctrlModel = "" Then ctrlModel = "<span class='text-muted'>N/A</span>" If ctrlModel = "" Then ctrlModel = "<span class='text-muted'>N/A</span>"
' Badge color based on relationship type
Dim ctrlRelBadge
Select Case LCase(ctrlRelType)
Case "controls"
ctrlRelBadge = "badge-primary"
Case "dualpath"
ctrlRelBadge = "badge-warning"
Case "connected to"
ctrlRelBadge = "badge-success"
Case Else
ctrlRelBadge = "badge-info"
End Select
Response.Write("<tr>") Response.Write("<tr>")
Response.Write("<td><a href='./displaymachine.asp?machineid=" & ctrlMachineID & "'>" & Server.HTMLEncode(ctrlMachineNum) & "</a></td>") Response.Write("<td><a href='./displaymachine.asp?machineid=" & ctrlMachineID & "'>" & Server.HTMLEncode(ctrlMachineNum) & "</a></td>")
Response.Write("<td>" & ctrlType & "</td>") Response.Write("<td>" & ctrlType & "</td>")
Response.Write("<td>" & ctrlModel & "</td>") Response.Write("<td>" & ctrlModel & "</td>")
Response.Write("<td><span class='badge badge-success'>" & Server.HTMLEncode(rs2("relationshiptype") & "") & "</span></td>") Response.Write("<td>" & Server.HTMLEncode(ctrlMachineNum) & " <a href='#' class='location-link text-info' data-machineid='" & ctrlMachineID & "' data-name='" & Server.HTMLEncode(ctrlMachineNum) & "'><i class='zmdi zmdi-pin'></i></a></td>")
Response.Write("<td><span class='badge " & ctrlRelBadge & "'>" & Server.HTMLEncode(ctrlRelType) & "</span></td>")
Response.Write("</tr>") Response.Write("</tr>")
rs2.MoveNext rs2.MoveNext
Loop Loop

View File

@@ -182,7 +182,15 @@
End If End If
%> %>
</p> </p>
<p class="mb-2"><%=Server.HTMLEncode(fqdnVal)%></p> <p class="mb-2">
<%
If fqdnVal <> "N/A" And fqdnVal <> "" Then
Response.Write("<a href='http://" & Server.HTMLEncode(fqdnVal) & "' title='Click to Access Printer Admin Page' target='_blank'>" & Server.HTMLEncode(fqdnVal) & "</a>")
Else
Response.Write("N/A")
End If
%>
</p>
<p class="mb-2"><%=Server.HTMLEncode(pinVal)%></p> <p class="mb-2"><%=Server.HTMLEncode(pinVal)%></p>
<p class="mb-2"> <p class="mb-2">
<% <%

View File

@@ -43,9 +43,9 @@
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col"></th>
<th scope="col"><i class="zmdi zmdi-pin"></i></th> <th scope="col"><i class="zmdi zmdi-pin"></i></th>
<th scope="col"><i class="zmdi zmdi-attachment-alt"></i></th> <th scope="col"><i class="zmdi zmdi-download"></i></th>
<th scope="col">ID</th>
<th scope="col">Machine</th> <th scope="col">Machine</th>
<th scope="col">Vendor</th> <th scope="col">Vendor</th>
<th scope="col">Model</th> <th scope="col">Model</th>
@@ -92,23 +92,23 @@
Response.write("<tr>") Response.write("<tr>")
' Location column - just map icon ' Picture column (far left)
Response.Write("<td><a href='./displayprinter.asp?printerid=" & printer & "' title='Click to Display Printer Details'><img src='./images/printers/" & image & "' width='30px' height='30px'></a></td>")
' Location column - map icon
Response.write("<td>") Response.write("<td>")
Response.write("<span class='location-link' data-machineid='" & machineid & "' style='cursor:pointer;'>") Response.write("<span class='location-link' data-machineid='" & machineid & "' style='cursor:pointer;'>")
Response.write("<i class='zmdi zmdi-pin'></i>") Response.write("<i class='zmdi zmdi-pin'></i>")
Response.write("</span>") Response.write("</span>")
Response.write("</td>") Response.write("</td>")
' Drivers column ' Download column
If installpath <> "" Then If installpath <> "" Then
Response.write("<td><a href='./" & installpath & "'><i class='zmdi zmdi-download' title='Click to Download Specific Installer' style='color:#fff;'></i></a></td>") Response.write("<td><a href='./" & installpath & "'><i class='zmdi zmdi-download' title='Click to Download Specific Installer' style='color:#fff;'></i></a></td>")
Else Else
Response.write("<td><a href='./installprinter.asp?printerid=" & printer & "'><i class='zmdi zmdi-download' title='Click to Download Universal Driver Installer' style='color:#fff;'></i></a></td>") Response.write("<td><a href='./installprinter.asp?printerid=" & printer & "'><i class='zmdi zmdi-download' title='Click to Download Universal Driver Installer' style='color:#fff;'></i></a></td>")
End If End If
' ID column
Response.Write("<td><a href='./displayprinter.asp?printerid=" & printer & "' title='Click to Display Printer Details'><img src='./images/printers/" & image & "' width='30px' height='30px'></a></td>")
' Machine column - link to machine (or printer if location only) ' Machine column - link to machine (or printer if location only)
' Check if location only (1 = location only) ' Check if location only (1 = location only)
isLocOnly = False isLocOnly = False

View File

@@ -75,6 +75,9 @@
<a href="./addusb.asp" class="btn btn-success btn-sm"> <a href="./addusb.asp" class="btn btn-success btn-sm">
<i class="zmdi zmdi-plus-circle"></i> Add USB <i class="zmdi zmdi-plus-circle"></i> Add USB
</a> </a>
<a href="./usblabelbatch.asp" class="btn btn-info btn-sm" target="_blank">
<i class="zmdi zmdi-label"></i> Batch Print Labels
</a>
</div> </div>
</div> </div>
@@ -176,6 +179,9 @@
<a href="./usbhistory.asp?machineid=<%=machineId%>" class="btn btn-sm btn-secondary" title="View history"> <a href="./usbhistory.asp?machineid=<%=machineId%>" class="btn btn-sm btn-secondary" title="View history">
<i class="zmdi zmdi-time"></i> <i class="zmdi zmdi-time"></i>
</a> </a>
<a href="./usbsingle.asp?machineid=<%=machineId%>" class="btn btn-sm btn-info" title="Print barcode label" target="_blank">
<i class="zmdi zmdi-label"></i>
</a>
</td> </td>
</tr> </tr>
<% <%

View File

@@ -302,19 +302,20 @@ Function PropagateControllerToDualpathMachines(conn, equipmentMachineid, pcMachi
dualpathMachineId = CLng(rsDP("related_machineid")) dualpathMachineId = CLng(rsDP("related_machineid"))
' Check if this dualpath machine already has a Controls relationship with this PC ' Check if this dualpath machine already has a Controls relationship with this PC
' Pattern: Equipment -> Controls -> PC (machineid=equipment, related_machineid=PC)
Set rsDPCheck = conn.Execute("SELECT relationshipid FROM machinerelationships " & _ Set rsDPCheck = conn.Execute("SELECT relationshipid FROM machinerelationships " & _
"WHERE machineid = " & CLng(pcMachineid) & " " & _ "WHERE machineid = " & dualpathMachineId & " " & _
"AND related_machineid = " & dualpathMachineId & " " & _ "AND related_machineid = " & CLng(pcMachineid) & " " & _
"AND relationshiptypeid = " & controlsTypeID & " AND isactive = 1") "AND relationshiptypeid = " & controlsTypeID & " AND isactive = 1")
If rsDPCheck.EOF Then If rsDPCheck.EOF Then
' Create Controls relationship: PC -> Dualpath Machine ' Create Controls relationship: Dualpath Equipment -> PC (matches existing data pattern)
Dim cmdDP Dim cmdDP
Set cmdDP = Server.CreateObject("ADODB.Command") Set cmdDP = Server.CreateObject("ADODB.Command")
cmdDP.ActiveConnection = conn cmdDP.ActiveConnection = conn
cmdDP.CommandText = "INSERT INTO machinerelationships (machineid, related_machineid, relationshiptypeid, isactive) VALUES (?, ?, ?, 1)" cmdDP.CommandText = "INSERT INTO machinerelationships (machineid, related_machineid, relationshiptypeid, isactive) VALUES (?, ?, ?, 1)"
cmdDP.Parameters.Append cmdDP.CreateParameter("@pcid", 3, 1, , CLng(pcMachineid))
cmdDP.Parameters.Append cmdDP.CreateParameter("@equipid", 3, 1, , dualpathMachineId) cmdDP.Parameters.Append cmdDP.CreateParameter("@equipid", 3, 1, , dualpathMachineId)
cmdDP.Parameters.Append cmdDP.CreateParameter("@pcid", 3, 1, , CLng(pcMachineid))
cmdDP.Parameters.Append cmdDP.CreateParameter("@reltypeid", 3, 1, , controlsTypeID) cmdDP.Parameters.Append cmdDP.CreateParameter("@reltypeid", 3, 1, , controlsTypeID)
cmdDP.Execute cmdDP.Execute
Set cmdDP = Nothing Set cmdDP = Nothing
@@ -360,28 +361,29 @@ Function PropagateControllerFromDualpathMachine(conn, machineId1, machineId2)
Set rsCtrl = Nothing Set rsCtrl = Nothing
' Check if machine1 has a controller, copy to machine2 if machine2 doesn't have one ' Check if machine1 has a controller, copy to machine2 if machine2 doesn't have one
Set rsCtrl = conn.Execute("SELECT machineid FROM machinerelationships " & _ ' Pattern: Equipment -> Controls -> PC (machineid=equipment, related_machineid=PC)
"WHERE related_machineid = " & CLng(machineId1) & " " & _ Set rsCtrl = conn.Execute("SELECT related_machineid FROM machinerelationships " & _
"WHERE machineid = " & CLng(machineId1) & " " & _
"AND relationshiptypeid = " & controlsTypeID & " AND isactive = 1") "AND relationshiptypeid = " & controlsTypeID & " AND isactive = 1")
If Not rsCtrl.EOF Then If Not rsCtrl.EOF Then
pcMachineid = CLng(rsCtrl("machineid")) pcMachineid = CLng(rsCtrl("related_machineid"))
rsCtrl.Close rsCtrl.Close
Set rsCtrl = Nothing Set rsCtrl = Nothing
' Check if machine2 already has this controller ' Check if machine2 already has this controller
Set rsCheck = conn.Execute("SELECT relationshipid FROM machinerelationships " & _ Set rsCheck = conn.Execute("SELECT relationshipid FROM machinerelationships " & _
"WHERE machineid = " & pcMachineid & " AND related_machineid = " & CLng(machineId2) & " " & _ "WHERE machineid = " & CLng(machineId2) & " AND related_machineid = " & pcMachineid & " " & _
"AND relationshiptypeid = " & controlsTypeID & " AND isactive = 1") "AND relationshiptypeid = " & controlsTypeID & " AND isactive = 1")
If rsCheck.EOF Then If rsCheck.EOF Then
' Create: PC -> Controls -> machine2 ' Create: Equipment -> Controls -> PC
Dim cmdCtrl Dim cmdCtrl
Set cmdCtrl = Server.CreateObject("ADODB.Command") Set cmdCtrl = Server.CreateObject("ADODB.Command")
cmdCtrl.ActiveConnection = conn cmdCtrl.ActiveConnection = conn
cmdCtrl.CommandText = "INSERT INTO machinerelationships (machineid, related_machineid, relationshiptypeid, isactive) VALUES (?, ?, ?, 1)" cmdCtrl.CommandText = "INSERT INTO machinerelationships (machineid, related_machineid, relationshiptypeid, isactive) VALUES (?, ?, ?, 1)"
cmdCtrl.Parameters.Append cmdCtrl.CreateParameter("@pcid", 3, 1, , pcMachineid)
cmdCtrl.Parameters.Append cmdCtrl.CreateParameter("@equipid", 3, 1, , CLng(machineId2)) cmdCtrl.Parameters.Append cmdCtrl.CreateParameter("@equipid", 3, 1, , CLng(machineId2))
cmdCtrl.Parameters.Append cmdCtrl.CreateParameter("@pcid", 3, 1, , pcMachineid)
cmdCtrl.Parameters.Append cmdCtrl.CreateParameter("@reltypeid", 3, 1, , controlsTypeID) cmdCtrl.Parameters.Append cmdCtrl.CreateParameter("@reltypeid", 3, 1, , controlsTypeID)
cmdCtrl.Execute cmdCtrl.Execute
Set cmdCtrl = Nothing Set cmdCtrl = Nothing
@@ -395,28 +397,28 @@ Function PropagateControllerFromDualpathMachine(conn, machineId1, machineId2)
End If End If
' Now check if machine2 has a controller, copy to machine1 if machine1 doesn't have one ' Now check if machine2 has a controller, copy to machine1 if machine1 doesn't have one
Set rsCtrl = conn.Execute("SELECT machineid FROM machinerelationships " & _ Set rsCtrl = conn.Execute("SELECT related_machineid FROM machinerelationships " & _
"WHERE related_machineid = " & CLng(machineId2) & " " & _ "WHERE machineid = " & CLng(machineId2) & " " & _
"AND relationshiptypeid = " & controlsTypeID & " AND isactive = 1") "AND relationshiptypeid = " & controlsTypeID & " AND isactive = 1")
If Not rsCtrl.EOF Then If Not rsCtrl.EOF Then
pcMachineid = CLng(rsCtrl("machineid")) pcMachineid = CLng(rsCtrl("related_machineid"))
rsCtrl.Close rsCtrl.Close
Set rsCtrl = Nothing Set rsCtrl = Nothing
' Check if machine1 already has this controller ' Check if machine1 already has this controller
Set rsCheck = conn.Execute("SELECT relationshipid FROM machinerelationships " & _ Set rsCheck = conn.Execute("SELECT relationshipid FROM machinerelationships " & _
"WHERE machineid = " & pcMachineid & " AND related_machineid = " & CLng(machineId1) & " " & _ "WHERE machineid = " & CLng(machineId1) & " AND related_machineid = " & pcMachineid & " " & _
"AND relationshiptypeid = " & controlsTypeID & " AND isactive = 1") "AND relationshiptypeid = " & controlsTypeID & " AND isactive = 1")
If rsCheck.EOF Then If rsCheck.EOF Then
' Create: PC -> Controls -> machine1 ' Create: Equipment -> Controls -> PC
Dim cmdCtrl2 Dim cmdCtrl2
Set cmdCtrl2 = Server.CreateObject("ADODB.Command") Set cmdCtrl2 = Server.CreateObject("ADODB.Command")
cmdCtrl2.ActiveConnection = conn cmdCtrl2.ActiveConnection = conn
cmdCtrl2.CommandText = "INSERT INTO machinerelationships (machineid, related_machineid, relationshiptypeid, isactive) VALUES (?, ?, ?, 1)" cmdCtrl2.CommandText = "INSERT INTO machinerelationships (machineid, related_machineid, relationshiptypeid, isactive) VALUES (?, ?, ?, 1)"
cmdCtrl2.Parameters.Append cmdCtrl2.CreateParameter("@pcid", 3, 1, , pcMachineid)
cmdCtrl2.Parameters.Append cmdCtrl2.CreateParameter("@equipid", 3, 1, , CLng(machineId1)) cmdCtrl2.Parameters.Append cmdCtrl2.CreateParameter("@equipid", 3, 1, , CLng(machineId1))
cmdCtrl2.Parameters.Append cmdCtrl2.CreateParameter("@pcid", 3, 1, , pcMachineid)
cmdCtrl2.Parameters.Append cmdCtrl2.CreateParameter("@reltypeid", 3, 1, , controlsTypeID) cmdCtrl2.Parameters.Append cmdCtrl2.CreateParameter("@reltypeid", 3, 1, , controlsTypeID)
cmdCtrl2.Execute cmdCtrl2.Execute
Set cmdCtrl2 = Nothing Set cmdCtrl2 = Nothing

View File

@@ -0,0 +1,181 @@
<%
' Returns both standard and metered part numbers as pipe-delimited string
' Format: "standard|metered" or just "standard" if no metered option
Function GetSupplyPartNumbers(printerModel, supplyName)
On Error Resume Next
Dim pn, sn, isDrum, isWaste, stdPN, metPN
pn = UCase(printerModel)
sn = UCase(supplyName)
isDrum = (InStr(sn, "DRUM") > 0 Or InStr(sn, "IMAGING") > 0)
isWaste = (InStr(sn, "WASTE") > 0)
stdPN = ""
metPN = ""
' VersaLink C415
If InStr(pn, "C415") > 0 Then
If isWaste Then
stdPN = "008R13335"
ElseIf isDrum Then
stdPN = "013R00701"
ElseIf InStr(sn, "BLACK") > 0 Then
stdPN = "006R04677"
metPN = "006R04681"
ElseIf InStr(sn, "CYAN") > 0 Then
stdPN = "006R04678"
metPN = "006R04682"
ElseIf InStr(sn, "MAGENTA") > 0 Then
stdPN = "006R04679"
metPN = "006R04683"
ElseIf InStr(sn, "YELLOW") > 0 Then
stdPN = "006R04680"
metPN = "006R04684"
End If
' VersaLink C405
ElseIf InStr(pn, "C405") > 0 Then
If isWaste Then
stdPN = "108R01124"
ElseIf isDrum Then
stdPN = "101R00555"
ElseIf InStr(sn, "BLACK") > 0 Then
stdPN = "106R03500"
ElseIf InStr(sn, "CYAN") > 0 Then
stdPN = "106R03501"
ElseIf InStr(sn, "MAGENTA") > 0 Then
stdPN = "106R03502"
ElseIf InStr(sn, "YELLOW") > 0 Then
stdPN = "106R03503"
End If
' VersaLink C7125/C7100
ElseIf InStr(pn, "C7125") > 0 Or InStr(pn, "C7100") > 0 Then
If isWaste Then
stdPN = "115R00129"
ElseIf isDrum Then
stdPN = "013R00688"
ElseIf InStr(sn, "BLACK") > 0 Then
stdPN = "006R01824"
metPN = "006R01820"
ElseIf InStr(sn, "CYAN") > 0 Then
stdPN = "006R01825"
metPN = "006R01821"
ElseIf InStr(sn, "MAGENTA") > 0 Then
stdPN = "006R01826"
metPN = "006R01822"
ElseIf InStr(sn, "YELLOW") > 0 Then
stdPN = "006R01827"
metPN = "006R01823"
End If
' VersaLink B7125
ElseIf InStr(pn, "B7125") > 0 Then
If isWaste Then
stdPN = "115R00129"
ElseIf isDrum Then
stdPN = "013R00687"
ElseIf InStr(sn, "BLACK") > 0 Or InStr(sn, "TONER") > 0 Then
stdPN = "006R01818"
metPN = "006R01819"
End If
' VersaLink B405
ElseIf InStr(pn, "B405") > 0 Then
If isWaste Then
stdPN = "108R01124"
ElseIf isDrum Then
stdPN = "101R00554"
ElseIf InStr(sn, "BLACK") > 0 Or InStr(sn, "TONER") > 0 Then
stdPN = "106R03580"
End If
' AltaLink C8135
ElseIf InStr(pn, "C8135") > 0 Then
If isWaste Then
stdPN = "008R08101"
ElseIf isDrum Then
stdPN = "013R00681"
ElseIf InStr(sn, "BLACK") > 0 Then
stdPN = "006R01746"
ElseIf InStr(sn, "CYAN") > 0 Then
stdPN = "006R01747"
ElseIf InStr(sn, "MAGENTA") > 0 Then
stdPN = "006R01748"
ElseIf InStr(sn, "YELLOW") > 0 Then
stdPN = "006R01749"
End If
' Xerox EC8036/AltaLink C8036 (compatible with WC7800 and AltaLink C80xx toner)
ElseIf InStr(pn, "EC8036") > 0 Or InStr(pn, "C8036") > 0 Then
Dim altPN
altPN = ""
If isWaste Then
stdPN = "008R13061"
ElseIf isDrum Then
stdPN = "013R00677"
ElseIf InStr(sn, "BLACK") > 0 Then
stdPN = "006R01509"
altPN = "006R01697"
metPN = "006R01701"
ElseIf InStr(sn, "CYAN") > 0 Then
stdPN = "006R01512"
altPN = "006R01698"
metPN = "006R01702"
ElseIf InStr(sn, "MAGENTA") > 0 Then
stdPN = "006R01511"
altPN = "006R01699"
metPN = "006R01703"
ElseIf InStr(sn, "YELLOW") > 0 Then
stdPN = "006R01510"
altPN = "006R01700"
metPN = "006R01704"
End If
' Return all 3 options for EC8036
If altPN <> "" Then
GetSupplyPartNumbers = stdPN & "|" & altPN & "|" & metPN
Exit Function
End If
' HP LaserJet Pro M454dw / M454dn / MFP M479fdw / M479fdn (414A/414X series)
ElseIf InStr(pn, "M454") > 0 Or InStr(pn, "M479") > 0 Then
If InStr(sn, "BLACK") > 0 Then
stdPN = "W2020A"
metPN = "W2020X"
ElseIf InStr(sn, "CYAN") > 0 Then
stdPN = "W2021A"
metPN = "W2021X"
ElseIf InStr(sn, "MAGENTA") > 0 Then
stdPN = "W2023A"
metPN = "W2023X"
ElseIf InStr(sn, "YELLOW") > 0 Then
stdPN = "W2022A"
metPN = "W2022X"
End If
' HP LaserJet Pro M251nw / M252dw / MFP M277dw (201A/201X series)
ElseIf InStr(pn, "M251") > 0 Or InStr(pn, "M252") > 0 Or InStr(pn, "M277") > 0 Then
If InStr(sn, "BLACK") > 0 Then
stdPN = "CF400A"
metPN = "CF400X"
ElseIf InStr(sn, "CYAN") > 0 Then
stdPN = "CF401A"
metPN = "CF401X"
ElseIf InStr(sn, "MAGENTA") > 0 Then
stdPN = "CF403A"
metPN = "CF403X"
ElseIf InStr(sn, "YELLOW") > 0 Then
stdPN = "CF402A"
metPN = "CF402X"
End If
End If
If metPN <> "" Then
GetSupplyPartNumbers = stdPN & "|" & metPN
Else
GetSupplyPartNumbers = stdPN
End If
End Function
Function IsWasteSupply(supplyName)
IsWasteSupply = (InStr(1, UCase(supplyName), "WASTE") > 0)
End Function
%>

View File

@@ -1,10 +1,35 @@
<!--#include file="config.asp"--> <!--#include file="config.asp"-->
<% <%
' Employee database connection - uses centralized config ' Employee database connection - uses centralized config
Dim objConn Dim objConn, empConnError
empConnError = ""
Session.Timeout = APP_SESSION_TIMEOUT Session.Timeout = APP_SESSION_TIMEOUT
On Error Resume Next
Set objConn = Server.CreateObject("ADODB.Connection") Set objConn = Server.CreateObject("ADODB.Connection")
objConn.ConnectionString = GetEmployeeConnectionString() objConn.ConnectionString = GetEmployeeConnectionString()
objConn.Open objConn.Open
If Err.Number <> 0 Then
empConnError = "Employee DB Connection Error: " & Err.Number & " - " & Err.Description & " (Source: " & Err.Source & ")"
Err.Clear
End If
On Error Goto 0
Set rs = Server.CreateObject("ADODB.Recordset") Set rs = Server.CreateObject("ADODB.Recordset")
' If connection failed, display error and stop
If empConnError <> "" Then
Dim connType
If USE_EMP_DSN Then
connType = "DSN-based (wjf_employees)"
Else
connType = "Direct ODBC"
End If
Response.Write "<!DOCTYPE html><html><head><title>Database Error</title></head><body>"
Response.Write "<h2>Database Connection Error</h2>"
Response.Write "<p style='color:red;'>" & Server.HTMLEncode(empConnError) & "</p>"
Response.Write "<p>Connection String Type: " & connType & "</p>"
Response.Write "</body></html>"
Response.End
End If
%> %>

View File

@@ -1,54 +1,37 @@
<% <%
' Cached Zabbix API wrapper for ALL supply levels (toner, ink, drums, maintenance kits, etc.) ' Cached Zabbix API wrapper for ALL supply levels (toner, ink, drums, maintenance kits, etc.)
' Simplified caching - no background refresh, minimal locking
%> %>
<!--#include file="./zabbix_all_supplies.asp"--> <!--#include file="./zabbix_all_supplies.asp"-->
<% <%
' Cached function for all supply levels - returns data immediately, refreshes in background if stale ' Cached function for all supply levels - simple 5-minute cache
Function GetAllPrinterSuppliesCached(hostIP) Function GetAllPrinterSuppliesCached(hostIP)
Dim cacheKey, cacheAge, forceRefresh On Error Resume Next
Dim cacheKey, cacheTime, cacheAge, cachedData, forceRefresh
cacheKey = "zabbix_all_supplies_" & hostIP cacheKey = "zabbix_all_supplies_" & hostIP
' Check if manual refresh was requested ' Check if manual refresh was requested
forceRefresh = (Request.QueryString("refresh") = "1" And Request.QueryString("ip") = hostIP) forceRefresh = (Request.QueryString("refresh") = "1" And Request.QueryString("ip") = hostIP)
If forceRefresh Then ' Check if valid cache exists (without locking)
' Clear cache for manual refresh If Not forceRefresh Then
Application.Lock cachedData = Application(cacheKey)
Application(cacheKey) = Empty cacheTime = Application(cacheKey & "_time")
Application(cacheKey & "_time") = Empty
Application(cacheKey & "_refreshing") = "false"
Application.Unlock
End If
' Check if cache exists If Not IsEmpty(cachedData) And Not IsEmpty(cacheTime) Then
If Not IsEmpty(Application(cacheKey)) And Not forceRefresh Then cacheAge = DateDiff("n", cacheTime, Now())
cacheAge = DateDiff("n", Application(cacheKey & "_time"), Now()) If cacheAge < 5 Then
' Cache is fresh, return it
' If cache is stale (>5 min) AND not already refreshing, trigger background update GetAllPrinterSuppliesCached = cachedData
If cacheAge >= 5 And Application(cacheKey & "_refreshing") <> "true" Then Exit Function
' Mark as refreshing End If
Application.Lock
Application(cacheKey & "_refreshing") = "true"
Application.Unlock
' Trigger async background refresh (non-blocking)
On Error Resume Next
Dim http
Set http = Server.CreateObject("MSXML2.ServerXMLHTTP.6.0")
http.Open "GET", "http://localhost/refresh_all_supplies_cache.asp?ip=" & Server.URLEncode(hostIP), True
http.Send
Set http = Nothing
On Error Goto 0
End If End If
' Return cached data immediately
GetAllPrinterSuppliesCached = Application(cacheKey)
Exit Function
End If End If
' No cache exists - fetch initial data ' Cache miss or stale - fetch fresh data
Dim freshData, zabbixConnected, pingStatus, suppliesJSON Dim zabbixConnected, pingStatus, suppliesJSON
zabbixConnected = ZabbixLogin() zabbixConnected = ZabbixLogin()
@@ -66,13 +49,13 @@ Function GetAllPrinterSuppliesCached(hostIP)
resultData(1) = pingStatus resultData(1) = pingStatus
resultData(2) = suppliesJSON resultData(2) = suppliesJSON
' Cache the result ' Cache the result (brief lock)
Application.Lock Application.Lock
Application(cacheKey) = resultData Application(cacheKey) = resultData
Application(cacheKey & "_time") = Now() Application(cacheKey & "_time") = Now()
Application(cacheKey & "_refreshing") = "false"
Application.Unlock Application.Unlock
On Error Goto 0
GetAllPrinterSuppliesCached = resultData GetAllPrinterSuppliesCached = resultData
End Function End Function

View File

@@ -282,3 +282,4 @@ function escapeHtml(text) {
</script> </script>
</body> </body>
</html> </html>

View File

@@ -1,3 +1,4 @@
<%@ Language=VBScript %>
<% <%
'============================================================================= '=============================================================================
' FILE: savecheckinusb.asp ' FILE: savecheckinusb.asp

View File

@@ -1,3 +1,4 @@
<%@ Language=VBScript %>
<% <%
'============================================================================= '=============================================================================
' FILE: savecheckoutusb.asp ' FILE: savecheckoutusb.asp

View File

@@ -1,3 +1,4 @@
<%@ Language=VBScript %>
<% <%
'============================================================================= '=============================================================================
' FILE: saveusbdirect.asp ' FILE: saveusbdirect.asp

View File

@@ -0,0 +1,22 @@
-- ============================================================
-- Add Dashboard and Lobby Display PC Types and Applications
-- Run against production database
-- Date: 2026-01-14
-- ============================================================
-- Add new PC types
INSERT INTO pctype (typename, description, functionalaccountid, isactive, displayorder) VALUES
('Dashboard', 'Shopfloor dashboard display PC', 3, '1', 10),
('Lobby Display', 'Lobby/TV dashboard display PC', 3, '1', 11);
-- Add new applications
INSERT INTO applications (appname, appdescription, supportteamid, isinstallable, isactive, ishidden) VALUES
('GE Aerospace Dashboard', 'Shopfloor dashboard kiosk display application', 1, b'1', b'1', b'0'),
('GE Aerospace Lobby Display', 'Lobby/TV dashboard kiosk display application', 1, b'1', b'1', b'0');
-- Verify insertions
SELECT 'PC Types Added:' AS '';
SELECT pctypeid, typename, description FROM pctype WHERE typename IN ('Dashboard', 'Lobby Display');
SELECT 'Applications Added:' AS '';
SELECT appid, appname, appdescription FROM applications WHERE appname LIKE 'GE Aerospace Dashboard' OR appname LIKE 'GE Aerospace Lobby Display';

View File

@@ -3,6 +3,7 @@
<head> <head>
<!--#include file="./includes/header.asp"--> <!--#include file="./includes/header.asp"-->
<!--#include file="./includes/sql.asp"--> <!--#include file="./includes/sql.asp"-->
<!--#include file="./includes/printer_supplies.asp"-->
<!--#include file="./includes/zabbix_all_supplies_cached.asp"--> <!--#include file="./includes/zabbix_all_supplies_cached.asp"-->
</head> </head>
@@ -206,13 +207,70 @@
' Xerox VersaLink B7025/B7030/B7035 Monochrome (WorkCentre 7800 series) ' Xerox VersaLink B7025/B7030/B7035 Monochrome (WorkCentre 7800 series)
ElseIf oem = "006R01756" Then ElseIf oem = "006R01756" Then
marketing = "Xerox Black Toner" marketing = "Xerox Black Toner"
' Xerox VersaLink C415
ElseIf oem = "006R04677" Then
marketing = "C415 Black Toner"
ElseIf oem = "006R04678" Then
marketing = "C415 Cyan Toner"
ElseIf oem = "006R04679" Then
marketing = "C415 Magenta Toner"
ElseIf oem = "006R04680" Then
marketing = "C415 Yellow Toner"
ElseIf oem = "006R04681" Then
marketing = "C415 Black Toner (Metered)"
ElseIf oem = "006R04682" Then
marketing = "C415 Cyan Toner (Metered)"
ElseIf oem = "006R04683" Then
marketing = "C415 Magenta Toner (Metered)"
ElseIf oem = "006R04684" Then
marketing = "C415 Yellow Toner (Metered)"
ElseIf oem = "008R13335" Then
marketing = "C415 Waste Cartridge"
ElseIf oem = "013R00701" Then
marketing = "C415 Drum/Imaging Unit"
' Xerox VersaLink C405
ElseIf oem = "106R03500" Then
marketing = "C405 Black Toner"
ElseIf oem = "106R03501" Then
marketing = "C405 Cyan Toner"
ElseIf oem = "106R03502" Then
marketing = "C405 Magenta Toner"
ElseIf oem = "106R03503" Then
marketing = "C405 Yellow Toner"
ElseIf oem = "108R01124" Then
marketing = "C405 Waste Container"
ElseIf oem = "101R00555" Then
marketing = "C405 Drum Cartridge"
' Xerox VersaLink C7125/C7100
ElseIf oem = "006R01824" Then
marketing = "C7125 Black Toner"
ElseIf oem = "006R01825" Then
marketing = "C7125 Cyan Toner"
ElseIf oem = "006R01826" Then
marketing = "C7125 Magenta Toner"
ElseIf oem = "006R01827" Then
marketing = "C7125 Yellow Toner"
ElseIf oem = "006R01820" Then
marketing = "C7125 Black Toner (Metered)"
ElseIf oem = "006R01821" Then
marketing = "C7125 Cyan Toner (Metered)"
ElseIf oem = "006R01822" Then
marketing = "C7125 Magenta Toner (Metered)"
ElseIf oem = "006R01823" Then
marketing = "C7125 Yellow Toner (Metered)"
ElseIf oem = "115R00129" Then
marketing = "VersaLink Waste Container"
ElseIf oem = "013R00688" Then
marketing = "C7125 Drum Cartridge"
' Xerox VersaLink B7125/B7130/B7135 Monochrome ' Xerox VersaLink B7125/B7130/B7135 Monochrome
ElseIf oem = "006R01817" Then ElseIf oem = "006R01817" Then
marketing = "Xerox Black Toner (Standard)" marketing = "B7125 Black Toner (Standard)"
ElseIf oem = "006R01818" Then ElseIf oem = "006R01818" Then
marketing = "Xerox Black Toner (High Capacity)" marketing = "B7125 Black Toner (High Capacity)"
ElseIf oem = "006R01819" Then ElseIf oem = "006R01819" Then
marketing = "Xerox Black Toner (DMO)" marketing = "B7125 Black Toner (Metered)"
ElseIf oem = "013R00687" Then
marketing = "B7125 Drum Cartridge"
' Xerox VersaLink C7000 series ' Xerox VersaLink C7000 series
ElseIf oem = "106R03536" Then ElseIf oem = "106R03536" Then
marketing = "Xerox Black Toner" marketing = "Xerox Black Toner"
@@ -252,15 +310,36 @@
' Xerox Phaser 4600/4620 series ' Xerox Phaser 4600/4620 series
ElseIf oem = "006R01817" Then ElseIf oem = "006R01817" Then
marketing = "Xerox Black Toner" marketing = "Xerox Black Toner"
' Xerox (legacy - keeping for compatibility) ' Xerox AltaLink EC8036/C8036 - WC7800 series compatible
ElseIf oem = "006R01509" Then
marketing = "WC7800 Black"
ElseIf oem = "006R01512" Then
marketing = "WC7800 Cyan"
ElseIf oem = "006R01511" Then
marketing = "WC7800 Magenta"
ElseIf oem = "006R01510" Then
marketing = "WC7800 Yellow"
' Xerox AltaLink EC8036/C8036 - AltaLink C80xx series
ElseIf oem = "006R01697" Then ElseIf oem = "006R01697" Then
marketing = "Xerox Black Toner" marketing = "AltaLink Black"
ElseIf oem = "006R01698" Then ElseIf oem = "006R01698" Then
marketing = "Xerox Cyan Toner" marketing = "AltaLink Cyan"
ElseIf oem = "006R01699" Then ElseIf oem = "006R01699" Then
marketing = "Xerox Yellow Toner" marketing = "AltaLink Magenta"
ElseIf oem = "006R01700" Then ElseIf oem = "006R01700" Then
marketing = "Xerox Magenta Toner" marketing = "AltaLink Yellow"
ElseIf oem = "006R01701" Then
marketing = "AltaLink Black (Met)"
ElseIf oem = "006R01702" Then
marketing = "AltaLink Cyan (Met)"
ElseIf oem = "006R01703" Then
marketing = "AltaLink Magenta (Met)"
ElseIf oem = "006R01704" Then
marketing = "AltaLink Yellow (Met)"
ElseIf oem = "008R13061" Then
marketing = "EC8036 Waste Container"
ElseIf oem = "013R00677" Then
marketing = "EC8036 Drum Cartridge"
Else Else
marketing = "" ' No mapping found - will display OEM number only marketing = "" ' No mapping found - will display OEM number only
@@ -290,7 +369,7 @@
<div> <div>
<h5 class="card-title"><i class='zmdi zmdi-collection-image text-yellow'></i>&nbsp;&nbsp;Supplies Alert Report</h5> <h5 class="card-title"><i class='zmdi zmdi-collection-image text-yellow'></i>&nbsp;&nbsp;Supplies Alert Report</h5>
<p class="text-muted" style="font-size:13px; margin-top:5px; margin-bottom:0;"> <p class="text-muted" style="font-size:13px; margin-top:5px; margin-bottom:0;">
Monitors: Toner/Ink &le;5%, Drums &le;5%, Maintenance Kits &le;5%, Waste &ge;95% (Xerox EC series: &le;5% inverted) Monitors: Toner/Ink &le;5%, Drums &le;5%, Maintenance Kits &le;5%, Waste &ge;95% (Xerox: &le;5% inverted)
</p> </p>
</div> </div>
<div style="display:flex; gap:10px; align-items:center;"> <div style="display:flex; gap:10px; align-items:center;">
@@ -340,6 +419,7 @@
Dim typeMatches, colorMatches Dim typeMatches, colorMatches
Dim urgencyScore, alertItem, alertItems(1000), alertCount, i, j, tempAlert, k, outputItem Dim urgencyScore, alertItem, alertItems(1000), alertCount, i, j, tempAlert, k, outputItem
Dim isXeroxPrinter Dim isXeroxPrinter
Dim stdPartNumber, metPartNumber, altPartNumber, stdMarketing, metMarketing, altMarketing, partNumberResult, partNumberParts
lowSuppliesFound = False lowSuppliesFound = False
alertCount = 0 alertCount = 0
@@ -364,9 +444,10 @@
machineid = rs("machineid") machineid = rs("machineid")
vendor = rs("vendor") vendor = rs("vendor")
' Detect if this is a Xerox EC series printer (EC8036, etc.) for vendor-specific logic ' Detect if this is a Xerox printer for vendor-specific waste cartridge logic
' These enterprise models report waste cartridges inverted from standard behavior ' Xerox printers (VersaLink, AltaLink, EC series) report waste inverted:
isXeroxPrinter = (InStr(1, vendor, "Xerox", 1) > 0 And InStr(1, modelnumber, "EC", 1) > 0) ' 100% = empty (capacity remaining), 0% = full (no capacity remaining)
isXeroxPrinter = (InStr(1, vendor, "Xerox", 1) > 0)
' Use alias if available, otherwise machinenumber ' Use alias if available, otherwise machinenumber
If NOT IsNull(rs("alias")) AND rs("alias") <> "" Then If NOT IsNull(rs("alias")) AND rs("alias") <> "" Then
@@ -617,137 +698,66 @@
End If End If
End If End If
' Look up part number for this item ' Look up part numbers from hardcoded list (SNMP is unreliable)
partNumber = "-" ' Returns "standard|metered" or "standard|alt|metered" or just "standard"
If partNumbers.Count > 0 Then partNumberResult = GetSupplyPartNumbers(modelnumber, itemName)
' Extract base name for lookup - remove " Level" suffix stdPartNumber = ""
lookupName = Replace(itemName, " Level", "") metPartNumber = ""
lookupName = Trim(lookupName) altPartNumber = ""
' Comprehensive matching strategy for all template versions If InStr(partNumberResult, "|") > 0 Then
foundMatch = False partNumberParts = Split(partNumberResult, "|")
stdPartNumber = partNumberParts(0)
' Strategy 1: EXACT match - NEW template format (preferred) If UBound(partNumberParts) >= 2 Then
' "Black Toner Level" → "Black Toner Part Number" ' 3 options: std|alt|met
' "Cyan Ink Level" → "Cyan Ink Part Number" altPartNumber = partNumberParts(1)
' "Black Drum Level" → "Black Drum Part Number" metPartNumber = partNumberParts(2)
partKeyName = lookupName & " Part Number" Else
If partNumbers.Exists(partKeyName) Then ' 2 options: std|met
partNumber = partNumbers(partKeyName) metPartNumber = partNumberParts(1)
foundMatch = True
End If
' Strategy 2: Add " Cartridge" - OLD Xerox template format
' "Black Drum Level" → "Black Drum Cartridge Part Number"
' "Black Toner Level" → "Black Toner Cartridge Part Number"
If Not foundMatch Then
tryName = lookupName & " Cartridge Part Number"
If partNumbers.Exists(tryName) Then
partNumber = partNumbers(tryName)
foundMatch = True
End If
End If
' Strategy 3: Replace supply type with "Cartridge" - OLD HP template format
' "Black Toner Level" → "Black Cartridge Part Number"
' "Cyan Ink Level" → "Cyan Cartridge Part Number"
If Not foundMatch Then
' Replace common supply types with "Cartridge"
If InStr(1, lookupName, "Toner", 1) > 0 Then
tryName = Replace(lookupName, "Toner", "Cartridge", 1, -1, 1) & " Part Number"
ElseIf InStr(1, lookupName, "Ink", 1) > 0 Then
tryName = Replace(lookupName, "Ink", "Cartridge", 1, -1, 1) & " Part Number"
ElseIf InStr(1, lookupName, "Drum", 1) > 0 Then
tryName = Replace(lookupName, "Drum", "Cartridge", 1, -1, 1) & " Part Number"
Else
tryName = ""
End If
If tryName <> "" And partNumbers.Exists(tryName) Then
partNumber = partNumbers(tryName)
foundMatch = True
End If
End If
' Strategy 4: Check for "Standard MIB" suffix variation
' "Maintenance Kit Level" → "Maintenance Kit Part Number (Standard MIB)"
If Not foundMatch Then
tryName = lookupName & " Part Number (Standard MIB)"
If partNumbers.Exists(tryName) Then
partNumber = partNumbers(tryName)
foundMatch = True
End If
End If
' Strategy 5: Intelligent fuzzy match by type and color
If Not foundMatch Then
' Extract primary identifier (first significant word)
primaryWord = ""
supplyType = ""
' Determine supply type
If InStr(1, lookupName, "Toner", 1) > 0 Then
supplyType = "Toner"
ElseIf InStr(1, lookupName, "Ink", 1) > 0 Then
supplyType = "Ink"
ElseIf InStr(1, lookupName, "Drum", 1) > 0 Then
supplyType = "Drum"
ElseIf InStr(1, lookupName, "Waste", 1) > 0 Then
supplyType = "Waste"
ElseIf InStr(1, lookupName, "Fuser", 1) > 0 Then
supplyType = "Fuser"
ElseIf InStr(1, lookupName, "Maintenance", 1) > 0 Then
supplyType = "Maintenance"
End If
' Extract color/identifier (first word before supply type)
If supplyType <> "" Then
colorPos = InStr(1, lookupName, supplyType, 1)
If colorPos > 1 Then
primaryWord = Trim(Left(lookupName, colorPos - 1))
End If
End If
' Search all keys for matching type and color
For Each partKey In partNumbers.Keys
If InStr(1, partKey, "Part Number", 1) > 0 Then
' Must match supply type
typeMatches = False
If supplyType <> "" Then
typeMatches = (InStr(1, partKey, supplyType, 1) > 0) Or (InStr(1, partKey, "Cartridge", 1) > 0)
Else
' For items without obvious type, just look for any match
typeMatches = True
End If
' Must match color/identifier if present
colorMatches = True
If primaryWord <> "" Then
colorMatches = (InStr(1, partKey, primaryWord, 1) > 0)
End If
If typeMatches And colorMatches Then
partNumber = partNumbers(partKey)
foundMatch = True
Exit For
End If
End If
Next
End If End If
Else
stdPartNumber = partNumberResult
End If End If
' Replace generic Xerox part numbers with actual model-specific part numbers If stdPartNumber = "" Then stdPartNumber = "-"
partNumber = GetActualPartNumber(partNumber, modelnumber, itemName)
' Get marketing name for this part number ' Get marketing names
marketingName = GetMarketingName(partNumber) stdMarketing = GetMarketingName(stdPartNumber)
metMarketing = ""
altMarketing = ""
If metPartNumber <> "" Then metMarketing = GetMarketingName(metPartNumber)
If altPartNumber <> "" Then altMarketing = GetMarketingName(altPartNumber)
If marketingName <> "" Then ' Build display string showing all options
' Show marketing name prominently with OEM number in smaller text If altPartNumber <> "" Then
displayPartNumber = "<strong>" & Server.HTMLEncode(marketingName) & "</strong><br><small style='color:#999;'>" & Server.HTMLEncode(partNumber) & "</small>" ' 3 options (EC8036 style)
displayPartNumber = "<div style='line-height:1.3; font-size:12px;'>"
displayPartNumber = displayPartNumber & "<strong>WC:</strong> " & Server.HTMLEncode(stdPartNumber) & "<br>"
displayPartNumber = displayPartNumber & "<strong>Alt:</strong> " & Server.HTMLEncode(altPartNumber) & "<br>"
displayPartNumber = displayPartNumber & "<strong>Met:</strong> " & Server.HTMLEncode(metPartNumber)
displayPartNumber = displayPartNumber & "</div>"
ElseIf metPartNumber <> "" Then
' 2 options
displayPartNumber = "<div style='line-height:1.4;'>"
If stdMarketing <> "" Then
displayPartNumber = displayPartNumber & "<strong>Std:</strong> " & Server.HTMLEncode(stdMarketing) & " <small style='color:#999;'>(" & Server.HTMLEncode(stdPartNumber) & ")</small><br>"
Else
displayPartNumber = displayPartNumber & "<strong>Std:</strong> " & Server.HTMLEncode(stdPartNumber) & "<br>"
End If
If metMarketing <> "" Then
displayPartNumber = displayPartNumber & "<strong>Met:</strong> " & Server.HTMLEncode(metMarketing) & " <small style='color:#999;'>(" & Server.HTMLEncode(metPartNumber) & ")</small>"
Else
displayPartNumber = displayPartNumber & "<strong>Met:</strong> " & Server.HTMLEncode(metPartNumber)
End If
displayPartNumber = displayPartNumber & "</div>"
Else Else
' No mapping found, just show OEM number ' Single option
displayPartNumber = Server.HTMLEncode(partNumber) If stdMarketing <> "" Then
displayPartNumber = "<strong>" & Server.HTMLEncode(stdMarketing) & "</strong><br><small style='color:#999;'>" & Server.HTMLEncode(stdPartNumber) & "</small>"
Else
displayPartNumber = Server.HTMLEncode(stdPartNumber)
End If
End If End If
' Calculate urgency score for sorting ' Calculate urgency score for sorting
@@ -851,7 +861,7 @@
</div> </div>
<div class="card-footer"> <div class="card-footer">
<small class="text-muted"> <small class="text-muted">
<i class="zmdi zmdi-info-outline"></i> This report shows printers with low supplies (&le;5%) or waste cartridges near full (&ge;95%, Xerox EC series inverted &le;5%). <i class="zmdi zmdi-info-outline"></i> This report shows printers with low supplies (&le;5%) or waste cartridges near full (&ge;95%, Xerox inverted &le;5%).
Data refreshed from Zabbix every 5 minutes. Data refreshed from Zabbix every 5 minutes.
</small> </small>
</div> </div>
@@ -919,6 +929,77 @@
}); });
</script> </script>
<!-- Print styles for readable printouts -->
<style>
@media print {
/* Reset dark theme for printing */
body, .bg-theme, .content-wrapper, .card, .card-body {
background: white !important;
color: black !important;
}
/* Make all text black and readable - override inline styles */
h1, h2, h3, h4, h5, h6, p, span, td, th, a, strong, small, div {
color: black !important;
}
/* Force small text (part numbers) to be dark and readable */
small, small[style], td small {
color: #333 !important;
font-size: 11px !important;
}
/* Location link styling for print */
.location-link, .location-link i {
color: black !important;
}
/* Table styling for print */
.table {
border-collapse: collapse !important;
}
.table th, .table td {
border: 1px solid #333 !important;
padding: 8px !important;
color: black !important;
background: white !important;
}
.table thead th {
background: #eee !important;
font-weight: bold !important;
}
.table-striped tbody tr:nth-of-type(odd) {
background: #f5f5f5 !important;
}
/* Keep status colors visible but darker for print */
strong[style*="color:#ff0000"] { color: #cc0000 !important; }
strong[style*="color:#ff6600"] { color: #cc5500 !important; }
/* Hide non-essential elements */
#sidebar-wrapper, .topbar-nav, .back-to-top, .footer,
#pageloader-overlay, #vendorFilter, #refreshBtn,
.location-popup, .location-popup-overlay {
display: none !important;
}
/* Expand content to full width */
.content-wrapper {
margin-left: 0 !important;
padding: 0 !important;
}
.card {
border: none !important;
box-shadow: none !important;
}
/* Part number styling */
.card-body small {
color: #666 !important;
}
}
</style>
<!-- Location map popup modal --> <!-- Location map popup modal -->
<style> <style>
/* Theme-specific styling for location links */ /* Theme-specific styling for location links */

595
uptimemap.asp Normal file
View File

@@ -0,0 +1,595 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!--#include file="./includes/header.asp"-->
<!--#include file="./includes/sql.asp"-->
<link rel="stylesheet" href="./leaflet/leaflet.css">
<script src="./leaflet/leaflet.js"></script>
</head>
<%
theme = Request.Cookies("theme")
IF theme = "" THEN
theme="bg-theme1"
END IF
' Get minimum uptime filter from query string (default 10 days)
Dim minUptime
minUptime = Request.QueryString("minuptime")
If minUptime = "" Or Not IsNumeric(minUptime) Then
minUptime = 10
Else
minUptime = CInt(minUptime)
End If
%>
<body class="bg-theme <%Response.Write(theme)%>">
<!-- start loader -->
<div id="pageloader-overlay" class="visible incoming"><div class="loader-wrapper-outer"><div class="loader-wrapper-inner" ><div class="loader"></div></div></div></div>
<!-- end loader -->
<!-- Start wrapper-->
<div id="wrapper">
<!--#include file="./includes/leftsidebar.asp"-->
<!--Start topbar header-->
<!--#include file="./includes/topbarheader.asp"-->
<!--End topbar header-->
<div class="clearfix"></div>
<div class="content-wrapper">
<div class="row">
<div class="col-lg-9">
<div class="card">
<div class="card-body" style="padding:0;">
<div style="padding:15px; border-bottom:1px solid #444;">
<h5 class="card-title" style="margin:0; display:inline-block;">
<i class='zmdi zmdi-time-interval'></i>&nbsp;&nbsp;High Uptime PCs with Machine Relationships
</h5>
<div style="float:right;">
<input type="text" id="pcSearch" class="form-control form-control-sm" placeholder="Search hostname, machine..." style="display:inline-block; width:180px; margin-right:10px;">
<label style="margin-right:5px; display:inline-block; color:#aaa;">Min Uptime:</label>
<select id="uptimeFilter" class="form-control form-control-sm" style="display:inline-block; width:100px; margin-right:10px;">
<option value="10" <%If minUptime = 10 Then Response.Write("selected")%>>10+ days</option>
<option value="20" <%If minUptime = 20 Then Response.Write("selected")%>>20+ days</option>
<option value="30" <%If minUptime = 30 Then Response.Write("selected")%>>30+ days</option>
<option value="60" <%If minUptime = 60 Then Response.Write("selected")%>>60+ days</option>
<option value="90" <%If minUptime = 90 Then Response.Write("selected")%>>90+ days</option>
</select>
<label style="margin-right:5px; display:inline-block; color:#aaa;">BU:</label>
<select id="businessUnitFilter" class="form-control form-control-sm" style="display:inline-block; width:120px;">
<option value="all">All</option>
<%
' Get business units for dropdown
Dim rsBU, strBUSQL
strBUSQL = "SELECT businessunitid, businessunit FROM businessunits WHERE isactive = 1 ORDER BY businessunit"
Set rsBU = objConn.Execute(strBUSQL)
Do While Not rsBU.EOF
Response.Write("<option value='" & rsBU("businessunitid") & "'>" & Server.HTMLEncode(rsBU("businessunit")) & "</option>")
rsBU.MoveNext
Loop
rsBU.Close
Set rsBU = Nothing
%>
</select>
</div>
</div>
<div id="map"></div>
</div>
</div>
</div>
<div class="col-lg-3">
<div class="card">
<div class="card-header" style="background: linear-gradient(45deg, #ff6b6b, #ee5a24); color: white;">
<i class="zmdi zmdi-time-interval"></i> Uptime Legend
</div>
<div class="card-body">
<p style="font-size:12px; color:#aaa; margin-bottom:15px;">
PCs with uptime > <%=minUptime%> days that need to be rebooted.
</p>
<div style="margin-bottom:20px;">
<div style="margin:8px 0; display:flex; align-items:center;">
<span style="display:inline-block; width:14px; height:14px; background:#F44336; border-radius:50%; margin-right:10px; border:2px solid #fff; box-shadow:0 1px 3px rgba(0,0,0,0.5);"></span>
<span style="font-size:13px; color:#fff;">Needs Reboot (<%=minUptime%>+ days)</span>
</div>
</div>
<div id="statsPanel" style="margin-top:20px; padding:15px; background:#2a2a2a; border-radius:4px;">
<strong style="color:#4fc3f7;">Statistics:</strong>
<div id="pcCount" style="margin-top:10px; font-size:14px; color:#fff;">Loading...</div>
</div>
<div style="margin-top:20px; padding:15px; background:#2a2a2a; border-radius:4px; font-size:12px;">
<strong style="color:#4fc3f7;">Tips:</strong>
<ul style="margin:8px 0; padding-left:20px; color:#aaa;">
<li style="margin:5px 0;">Hover over markers for PC details</li>
<li style="margin:5px 0;">Markers placed at related machine location</li>
<li style="margin:5px 0;">Click "View PC" for full information</li>
<li style="margin:5px 0;">Red markers need attention!</li>
</ul>
</div>
</div>
</div>
</div>
</div><!--End Row-->
<!-- PCs without machine relationships (can't be mapped) -->
<div class="row" style="margin-top:20px;">
<div class="col-12">
<div class="card">
<div class="card-header" style="background: linear-gradient(45deg, #6c757d, #495057); color: white; cursor:pointer;" onclick="toggleUnmappedTable()">
<i class="zmdi zmdi-alert-circle"></i> PCs That Cannot Be Mapped (No Relationship or No Location)
<span id="unmappedToggle" style="float:right;"><i class="zmdi zmdi-chevron-down"></i></span>
</div>
<div id="unmappedTable" class="card-body" style="display:none; padding:0;">
<p style="padding:15px 15px 0 15px; font-size:12px; color:#aaa;">
These PCs have high uptime but cannot be placed on the map because they either have no machine relationship,
or their related machine has no map coordinates. <span style="color:#FFC107;">*</span> = has relationship but machine needs location.
</p>
<div class="table-responsive">
<table class="table table-sm table-hover" style="margin:0;">
<thead>
<tr>
<th>Hostname</th>
<th>Uptime (Days)</th>
<th>Last Boot</th>
<th>Business Unit</th>
<th>Related Machine</th>
<th>Serial Number</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<%
' Query PCs with high uptime that CANNOT be mapped:
' 1. No machine relationship at all, OR
' 2. Has relationship but equipment has no map coordinates
Dim rsUnmapped, strUnmappedSQL
Dim unmappedDisplayCount
unmappedDisplayCount = 0
strUnmappedSQL = "SELECT " &_
"pc.machineid, " &_
"pc.hostname, " &_
"pc.serialnumber, " &_
"pc.lastboottime, " &_
"DATEDIFF(NOW(), pc.lastboottime) as uptime_days, " &_
"bu.businessunit, " &_
"eq.machinenumber as related_machine " &_
"FROM machines pc " &_
"LEFT JOIN machinerelationships mr ON pc.machineid = mr.related_machineid AND mr.isactive = 1 " &_
"LEFT JOIN machines eq ON mr.machineid = eq.machineid " &_
"LEFT JOIN businessunits bu ON pc.businessunitid = bu.businessunitid " &_
"WHERE pc.pctypeid IS NOT NULL " &_
"AND pc.isactive = 1 " &_
"AND pc.lastboottime IS NOT NULL " &_
"AND DATEDIFF(NOW(), pc.lastboottime) >= " & minUptime & " " &_
"AND (mr.relationshipid IS NULL OR eq.mapleft IS NULL OR eq.maptop IS NULL) " &_
"ORDER BY uptime_days DESC"
Set rsUnmapped = objConn.Execute(strUnmappedSQL)
Do While Not rsUnmapped.EOF
unmappedDisplayCount = unmappedDisplayCount + 1
Dim umHostname, umUptime, umLastBoot, umBU, umSerial, umId, umColor
umId = rsUnmapped("machineid")
umHostname = rsUnmapped("hostname") & ""
If IsNull(rsUnmapped("uptime_days")) Then
umUptime = 0
Else
umUptime = CLng(rsUnmapped("uptime_days"))
End If
umLastBoot = rsUnmapped("lastboottime")
umSerial = rsUnmapped("serialnumber") & ""
If IsNull(rsUnmapped("businessunit")) Then
umBU = "-"
Else
umBU = rsUnmapped("businessunit") & ""
End If
Dim umRelatedMachine
If IsNull(rsUnmapped("related_machine")) Then
umRelatedMachine = ""
Else
umRelatedMachine = rsUnmapped("related_machine") & ""
End If
' Color based on uptime
If umUptime >= 90 Then
umColor = "#F44336"
ElseIf umUptime >= 60 Then
umColor = "#FF9800"
ElseIf umUptime >= 30 Then
umColor = "#FFC107"
Else
umColor = "#4CAF50"
End If
%>
<tr>
<td><a href="./displaypc.asp?machineid=<%=umId%>" target="_blank"><%=Server.HTMLEncode(umHostname)%></a></td>
<td><span style="color:<%=umColor%>; font-weight:bold;"><%=umUptime%></span></td>
<td><%=umLastBoot%></td>
<td><%=Server.HTMLEncode(umBU)%></td>
<td><%If umRelatedMachine <> "" Then%><span style="color:#FFC107;" title="Has relationship but machine has no map location"><%=Server.HTMLEncode(umRelatedMachine)%> *</span><%Else%><span style="color:#aaa;">None</span><%End If%></td>
<td><%=Server.HTMLEncode(umSerial)%></td>
<td><a href="./displaypc.asp?machineid=<%=umId%>" target="_blank" class="btn btn-sm btn-outline-info">View</a></td>
</tr>
<%
rsUnmapped.MoveNext
Loop
rsUnmapped.Close
Set rsUnmapped = Nothing
%>
</tbody>
</table>
</div>
<div style="padding:10px 15px; background:#2a2a2a; font-size:12px; color:#aaa;">
Total: <strong style="color:#fff;"><%=unmappedDisplayCount%></strong> PCs without machine relationships
</div>
</div>
</div>
</div>
</div>
</div><!--End content-wrapper-->
<!--Start Back To Top Button-->
<a href="javaScript:void();" class="back-to-top"><i class="fa fa-angle-double-up"></i> </a>
<!--End Back To Top Button-->
<!--Start footer-->
<footer class="footer">
</div>
</footer>
<!--End footer-->
</div><!--End wrapper-->
<!-- Bootstrap core JavaScript-->
<script src="assets/js/jquery.min.js"></script>
<script src="assets/js/popper.min.js"></script>
<script src="assets/js/bootstrap.min.js"></script>
<script src="assets/plugins/simplebar/js/simplebar.js"></script>
<script src="assets/js/sidebar-menu.js"></script>
<script src="assets/js/app-script.js"></script>
<style>
#map {
width: 100%;
height: calc(100vh - 250px);
min-height: 600px;
background-color: #1a1a1a;
}
.leaflet-control-zoom a {
background-color: #2a2a2a !important;
color: #fff !important;
border-color: #444 !important;
}
.leaflet-control-zoom a:hover {
background-color: #3a3a3a !important;
}
.leaflet-bar {
border: 1px solid #444 !important;
}
.leaflet-popup-content-wrapper {
background: #1f1f1f !important;
color: #fff !important;
box-shadow: 0 3px 14px rgba(0,0,0,0.6) !important;
border-radius: 4px !important;
padding: 0 !important;
}
.leaflet-popup-content {
margin: 0 !important;
}
.leaflet-popup-tip-container {
display: none !important;
}
.leaflet-popup-close-button {
color: #fff !important;
font-size: 24px !important;
padding: 4px 8px 0 0 !important;
}
.leaflet-control-attribution {
display: none !important;
}
.uptime-pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.2); opacity: 0.8; }
}
</style>
<script>
// Get current theme
var bodyClass = document.body.className;
var themeMatch = bodyClass.match(/bg-theme(\d+)/);
var theme = themeMatch ? 'bg-theme' + themeMatch[1] : 'bg-theme1';
var themeConfig = {
'bg-theme1': { bg: '#2a2a2a', filter: 'brightness(0.7) contrast(1.1)', gradient: 'linear-gradient(45deg, #ff6b6b, #ee5a24)' },
'bg-theme7': { bg: '#0c675e', filter: 'brightness(0.8) contrast(1.1) hue-rotate(-10deg)', gradient: 'linear-gradient(45deg, #ff6b6b, #ee5a24)' },
'bg-theme11': { bg: '#1565C0', filter: 'brightness(0.85) contrast(1.05) hue-rotate(-5deg)', gradient: 'linear-gradient(45deg, #ff6b6b, #ee5a24)' }
};
var config = themeConfig[theme] || { bg: '#1a1a1a', filter: 'brightness(0.7) contrast(1.1)', gradient: 'linear-gradient(45deg, #ff6b6b, #ee5a24)' };
document.getElementById('map').style.backgroundColor = config.bg;
var map = L.map('map', {
crs: L.CRS.Simple,
minZoom: -3
});
var bounds = [[0,0], [2550,3300]];
var lightThemes = ['bg-theme11', 'bg-theme13'];
var mapImage = lightThemes.includes(theme) ? './images/sitemap2025-light.png' : './images/sitemap2025-dark.png';
var image = L.imageOverlay(mapImage, bounds);
image.on('load', function() {
var imgElement = this.getElement();
if (imgElement) {
imgElement.style.filter = config.filter;
}
});
image.addTo(map);
var center = [1275, 1650];
map.setView(center, -2.3);
// Store PC markers
var pcMarkers = [];
// Uptime color function - single color for all (reboot needed)
function getUptimeColor(days) {
return '#F44336'; // Red - all PCs need reboot
}
function getUptimeLabel(days) {
return 'Needs Reboot';
}
<%
' First, get count of PCs without relationships (for stats display)
Dim rsUnmappedCount, unmappedCount
unmappedCount = 0
Set rsUnmappedCount = objConn.Execute("SELECT COUNT(*) as cnt FROM machines pc " &_
"LEFT JOIN machinerelationships mr ON pc.machineid = mr.related_machineid AND mr.isactive = 1 " &_
"LEFT JOIN machines eq ON mr.machineid = eq.machineid " &_
"WHERE pc.pctypeid IS NOT NULL AND pc.isactive = 1 " &_
"AND pc.lastboottime IS NOT NULL " &_
"AND DATEDIFF(NOW(), pc.lastboottime) >= " & minUptime & " " &_
"AND (mr.relationshipid IS NULL OR eq.mapleft IS NULL OR eq.maptop IS NULL)")
If Not rsUnmappedCount.EOF Then
unmappedCount = CLng(rsUnmappedCount("cnt"))
End If
rsUnmappedCount.Close
Set rsUnmappedCount = Nothing
' Query PCs with machine relationships and high uptime
' Use the RELATED MACHINE's map coordinates since PCs don't have their own
Dim strSQL, rs
Dim pcId, pcHostname, pcUptime, lastBoot
Dim eqId, eqNumber, eqMapLeft, eqMapTop, eqType
Dim businessunitid, businessunit
strSQL = "SELECT " &_
"pc.machineid as pc_id, " &_
"pc.hostname as pc_hostname, " &_
"pc.lastboottime, " &_
"DATEDIFF(NOW(), pc.lastboottime) as uptime_days, " &_
"eq.machineid as eq_id, " &_
"eq.machinenumber as eq_number, " &_
"eq.mapleft as eq_mapleft, " &_
"eq.maptop as eq_maptop, " &_
"mt.machinetype as eq_type, " &_
"pc.businessunitid, " &_
"bu.businessunit " &_
"FROM machines pc " &_
"INNER JOIN machinerelationships mr ON pc.machineid = mr.related_machineid AND mr.isactive = 1 " &_
"INNER JOIN machines eq ON mr.machineid = eq.machineid " &_
"LEFT JOIN models mo ON eq.modelnumberid = mo.modelnumberid " &_
"LEFT JOIN machinetypes mt ON mo.machinetypeid = mt.machinetypeid " &_
"LEFT JOIN businessunits bu ON pc.businessunitid = bu.businessunitid " &_
"WHERE pc.pctypeid IS NOT NULL " &_
"AND pc.isactive = 1 " &_
"AND pc.lastboottime IS NOT NULL " &_
"AND DATEDIFF(NOW(), pc.lastboottime) >= " & minUptime & " " &_
"AND eq.mapleft IS NOT NULL " &_
"AND eq.maptop IS NOT NULL " &_
"ORDER BY uptime_days DESC"
Set rs = objConn.Execute(strSQL)
Dim pcCount
pcCount = 0
Do While Not rs.EOF
pcCount = pcCount + 1
pcId = rs("pc_id")
pcHostname = rs("pc_hostname") & ""
pcUptime = rs("uptime_days")
lastBoot = rs("lastboottime")
eqId = rs("eq_id")
eqNumber = rs("eq_number") & ""
eqMapLeft = rs("eq_mapleft")
eqMapTop = 2550 - rs("eq_maptop") ' Flip Y coordinate
If Not IsNull(rs("eq_type")) Then
eqType = rs("eq_type")
Else
eqType = "Unknown"
End If
If Not IsNull(rs("businessunitid")) Then
businessunitid = rs("businessunitid")
Else
businessunitid = 0
End If
If Not IsNull(rs("businessunit")) Then
businessunit = rs("businessunit")
Else
businessunit = "N/A"
End If
%>
(function() {
var pcId = '<%=pcId%>';
var pcHostname = '<%=Server.HTMLEncode(pcHostname)%>';
var uptimeDays = <%=pcUptime%>;
var lastBoot = '<%=lastBoot%>';
var eqId = '<%=eqId%>';
var eqNumber = '<%=Server.HTMLEncode(eqNumber)%>';
var eqType = '<%=Server.HTMLEncode(eqType)%>';
var businessUnitId = '<%=businessunitid%>';
var businessUnit = '<%=Server.HTMLEncode(businessunit)%>';
var color = getUptimeColor(uptimeDays);
var uptimeLabel = getUptimeLabel(uptimeDays);
// Create custom marker icon
var icon = L.divIcon({
html: '<div style="background:' + color + '; width:14px; height:14px; border-radius:50%; border:2px solid #fff; box-shadow:0 2px 5px rgba(0,0,0,0.5);"></div>',
iconSize: [14, 14],
iconAnchor: [7, 7],
popupAnchor: [0, -5],
className: 'custom-marker'
});
var marker = L.marker([<%=eqMapTop%>, <%=eqMapLeft%>], {
title: pcHostname + ' (' + uptimeDays + ' days)',
icon: icon,
pcId: pcId,
uptimeDays: uptimeDays
}).addTo(map);
// Store marker with searchable data
pcMarkers.push({
marker: marker,
pcId: pcId,
uptimeDays: uptimeDays,
businessUnitId: businessUnitId,
searchData: {
hostname: pcHostname.toLowerCase(),
machine: eqNumber.toLowerCase(),
bu: businessUnit.toLowerCase()
}
});
// Popup on hover
var popupTimeout;
marker.on('mouseover', function() {
clearTimeout(popupTimeout);
this.openPopup();
});
marker.on('mouseout', function(e) {
popupTimeout = setTimeout(function() {
marker.closePopup();
}, 800);
});
var pcUrl = './displaypc.asp?machineid=' + pcId;
var eqUrl = './displaymachine.asp?machineid=' + eqId;
var popupContent = '<div style="background:#1f1f1f; color:#fff; min-width:280px; border-radius:4px; overflow:hidden;">' +
'<div style="background:' + config.gradient + '; padding:10px 15px; border-bottom:1px solid #444;">' +
'<h6 style="margin:0; color:#fff; font-size:14px;"><i class="zmdi zmdi-desktop-mac"></i> ' + pcHostname + '</h6>' +
'</div>' +
'<div style="padding:10px 15px; font-size:12px;">' +
'<div style="margin:8px 0; padding:10px; background:' + color + '22; border-left:3px solid ' + color + '; border-radius:0 4px 4px 0;">' +
'<strong style="color:' + color + '; font-size:16px;">' + uptimeDays + ' days uptime</strong>' +
'<div style="color:#aaa; font-size:11px; margin-top:3px;">Status: ' + uptimeLabel + '</div>' +
'</div>' +
'<div style="margin:5px 0;"><strong style="color:#aaa;">Last Boot:</strong> <span style="color:#fff;">' + lastBoot + '</span></div>' +
'<div style="margin:5px 0;"><strong style="color:#aaa;">Related Machine:</strong> <a href="' + eqUrl + '" target="_blank" style="color:#4fc3f7;">' + eqNumber + '</a> (' + eqType + ')</div>' +
(businessUnit !== 'N/A' ? '<div style="margin:5px 0;"><strong style="color:#aaa;">Business Unit:</strong> <span style="color:#fff;">' + businessUnit + '</span></div>' : '') +
'</div>' +
'<div style="padding:10px 15px; border-top:1px solid #444; text-align:center;">' +
'<a href="' + pcUrl + '" style="display:inline-block; background:' + config.gradient + '; color:#fff; padding:8px 18px; border-radius:4px; text-decoration:none; font-size:13px; font-weight:500;" target="_blank"><i class="zmdi zmdi-desktop-mac"></i> View PC</a>' +
'</div>' +
'</div>';
marker.bindPopup(popupContent);
})();
<%
rs.MoveNext
Loop
rs.Close
Set rs = Nothing
objConn.Close
%>
// Update stats
document.getElementById('pcCount').innerHTML =
'<div style="margin:5px 0;"><i class="zmdi zmdi-pin"></i> <strong><%=pcCount%></strong> PCs on map</div>' +
'<div style="margin:5px 0;"><i class="zmdi zmdi-alert-circle" style="color:#6c757d;"></i> <strong><%=unmappedCount%></strong> PCs without location</div>' +
'<div style="margin:5px 0; font-size:12px; color:#aaa;">Total: <%=pcCount + unmappedCount%> high-uptime PCs</div>';
// Toggle unmapped PCs table
function toggleUnmappedTable() {
var table = document.getElementById('unmappedTable');
var toggle = document.getElementById('unmappedToggle');
if (table.style.display === 'none') {
table.style.display = 'block';
toggle.innerHTML = '<i class="zmdi zmdi-chevron-up"></i>';
} else {
table.style.display = 'none';
toggle.innerHTML = '<i class="zmdi zmdi-chevron-down"></i>';
}
}
// Filter functionality
function applyFilters() {
var selectedBU = document.getElementById('businessUnitFilter').value;
var searchTerm = document.getElementById('pcSearch').value.toLowerCase().trim();
var minUptimeVal = parseInt(document.getElementById('uptimeFilter').value);
var visibleCount = 0;
pcMarkers.forEach(function(item) {
var buMatch = (selectedBU === 'all' || item.businessUnitId == selectedBU);
var uptimeMatch = item.uptimeDays >= minUptimeVal;
var searchMatch = true;
if (searchTerm !== '') {
searchMatch = item.searchData.hostname.indexOf(searchTerm) > -1 ||
item.searchData.machine.indexOf(searchTerm) > -1 ||
item.searchData.bu.indexOf(searchTerm) > -1;
}
if (buMatch && uptimeMatch && searchMatch) {
item.marker.setOpacity(1);
visibleCount++;
} else {
item.marker.setOpacity(0.15);
}
});
// Update visible count
document.getElementById('pcCount').innerHTML =
'<div style="margin:5px 0;"><i class="zmdi zmdi-desktop-mac"></i> <strong>' + visibleCount + '</strong> PCs visible</div>' +
'<div style="margin:5px 0; font-size:12px; color:#aaa;">Filtered from <%=pcCount%> total</div>';
}
// Uptime filter changes page
document.getElementById('uptimeFilter').addEventListener('change', function() {
window.location.href = '?minuptime=' + this.value;
});
// Listen to filter changes
document.getElementById('businessUnitFilter').addEventListener('change', applyFilters);
// Listen to search input with debouncing
var searchTimeout;
document.getElementById('pcSearch').addEventListener('input', function() {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(applyFilters, 300);
});
</script>
</body>
</html>

View File

@@ -130,8 +130,8 @@
checkinTime = Month(rs("checkin_time")) & "/" & Day(rs("checkin_time")) & "/" & Year(rs("checkin_time")) & " " & FormatDateTime(rs("checkin_time"), 3) checkinTime = Month(rs("checkin_time")) & "/" & Day(rs("checkin_time")) & "/" & Year(rs("checkin_time")) & " " & FormatDateTime(rs("checkin_time"), 3)
statusClass = "" statusClass = ""
Else Else
checkinTime = "<span class='badge badge-warning'>Still Out</span>" checkinTime = "<span class='badge badge-dark'>Still Out</span>"
statusClass = "table-warning" statusClass = ""
End If End If
' Format duration ' Format duration

329
usblabelbatch.asp Normal file
View File

@@ -0,0 +1,329 @@
<%@ Language=VBScript %>
<%
Option Explicit
Dim objConn, rs
%>
<!DOCTYPE html>
<html>
<head>
<!--#include file="./includes/sql.asp"-->
<title>Batch Print USB Barcode Labels</title>
<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js"></script>
<style>
/* ULINE S-20135: 8.5x11 sheet, 3x3 labels, 2 cols x 3 rows */
/* Each 3x3 label holds 3x4 grid of 1x0.75 mini-labels (12 per cell) */
@page { size: letter; margin: 0; }
body { font-family: Arial, sans-serif; background: #f0f0f0; margin: 0; padding: 20px; }
.no-print { margin-bottom: 20px; }
.controls { background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
.controls h3 { margin-top: 0; }
.print-btn { padding: 10px 30px; font-size: 16px; cursor: pointer; background: #667eea; color: white; border: none; border-radius: 5px; margin-right: 10px; }
.print-btn:disabled { background: #ccc; cursor: not-allowed; }
.clear-btn { padding: 10px 20px; font-size: 14px; cursor: pointer; background: #dc3545; color: white; border: none; border-radius: 5px; margin-right: 10px; }
.select-all-btn { padding: 10px 20px; font-size: 14px; cursor: pointer; background: #28a745; color: white; border: none; border-radius: 5px; }
.back-btn { padding: 10px 20px; font-size: 14px; cursor: pointer; background: #6c757d; color: white; border: none; border-radius: 5px; text-decoration: none; margin-left: 10px; }
.usb-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; max-height: 300px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; background: #fafafa; }
.usb-item { display: flex; align-items: center; padding: 8px; background: white; border: 1px solid #ddd; border-radius: 4px; cursor: pointer; }
.usb-item:hover { background: #f0f0f0; }
.usb-item.selected { background: #e7f1ff; border-color: #667eea; }
.usb-item input { margin-right: 10px; }
.usb-item label { cursor: pointer; flex: 1; }
.usb-item .alias { font-size: 11px; color: #666; }
.selected-count { font-weight: bold; margin: 10px 0; }
.selected-count .count { color: #667eea; }
.selected-count .pages { color: #28a745; }
.sheets-container { display: flex; flex-direction: column; gap: 20px; }
.print-sheet { width: 8.5in; height: 11in; background: white; margin: 0 auto; position: relative; border: 1px solid #ccc; page-break-after: always; }
.print-sheet:last-child { page-break-after: auto; }
.sheet-label { position: absolute; top: -25px; left: 0; font-size: 12px; color: #666; }
.label-cell {
width: 3in;
height: 3in;
position: absolute;
box-sizing: border-box;
border: 1px dashed #ccc;
overflow: hidden;
}
.label-cell.has-content { border: 1px solid #667eea; }
.label-cell.empty { background: #fafafa; }
/* Label cell positions */
.cell-1 { top: 0.875in; left: 1.1875in; }
.cell-2 { top: 0.875in; left: 4.3125in; }
.cell-3 { top: 4in; left: 1.1875in; }
.cell-4 { top: 4in; left: 4.3125in; }
.cell-5 { top: 7.125in; left: 1.1875in; }
.cell-6 { top: 7.125in; left: 4.3125in; }
.mini-grid { display: grid; grid-template-columns: repeat(3, 1in); grid-template-rows: repeat(4, 0.75in); width: 3in; height: 3in; }
.mini-label { width: 1in; height: 0.75in; display: flex; flex-direction: column; align-items: center; justify-content: center; box-sizing: border-box; padding: 0.02in; border: 1px dotted #ddd; overflow: hidden; }
.mini-label.filled { border: 1px solid #999; }
.mini-label.empty { background: #f8f8f8; border: 1px dotted #eee; }
.barcode-container { text-align: center; line-height: 0; }
.barcode-container svg { max-width: 0.9in; height: 24px; }
.serial-text { font-size: 6pt; font-weight: bold; font-family: monospace; text-align: center; margin-top: 1px; letter-spacing: 0.3px; }
.empty-cell-text { color: #ccc; font-size: 12px; display: flex; align-items: center; justify-content: center; height: 100%; }
@media print {
body { padding: 0; margin: 0; background: white; }
.no-print { display: none; }
.sheets-container { gap: 0; }
.print-sheet { border: none; margin: 0; width: 8.5in; height: 11in; overflow: hidden; }
.sheet-label { display: none; }
.label-cell { border: none !important; }
.label-cell.empty { visibility: hidden; }
.mini-label { border: 1px dotted #ccc !important; }
.mini-label.empty { visibility: hidden; }
}
</style>
</head>
<body>
<%
Dim strSQL
strSQL = "SELECT m.machineid, m.serialnumber, m.alias FROM machines m WHERE m.machinetypeid = 44 AND m.isactive = 1 ORDER BY m.serialnumber ASC"
Set rs = objConn.Execute(strSQL)
%>
<div class="no-print">
<div class="controls">
<h3>Batch Print USB Barcode Labels</h3>
<p>Select USB devices to print (72 labels per page - 6 ULINE labels x 12 mini-labels each, cut after printing):</p>
<div class="usb-grid">
<%
Dim displayName
Do While Not rs.EOF
displayName = rs("serialnumber") & ""
%>
<div class="usb-item" onclick="toggleUSB(this, <%=rs("machineid")%>, '<%=Server.HTMLEncode(Replace(rs("serialnumber") & "", "'", "\'"))%>', '<%=Server.HTMLEncode(Replace(rs("alias") & "", "'", "\'"))%>')">
<input type="checkbox" id="usb-<%=rs("machineid")%>">
<label>
<strong><code><%=Server.HTMLEncode(displayName)%></code></strong>
<div class="alias"><%=Server.HTMLEncode(rs("alias") & "")%></div>
</label>
</div>
<%
rs.MoveNext
Loop
rs.Close
Set rs = Nothing
objConn.Close
%>
</div>
<div class="selected-count">
Selected: <span class="count" id="selectedCount">0</span> USB devices
(<span class="pages" id="pageCount">0</span> pages)
<label style="margin-left: 20px;">Start at cell:
<select id="startCell" onchange="updateSheets()" style="padding: 5px; font-size: 14px;">
<option value="1">1 - Top Left</option>
<option value="2">2 - Top Right</option>
<option value="3">3 - Middle Left</option>
<option value="4">4 - Middle Right</option>
<option value="5">5 - Bottom Left</option>
<option value="6">6 - Bottom Right</option>
</select>
</label>
</div>
<button class="print-btn" id="printBtn" onclick="window.print()" disabled>Print Labels</button>
<button class="clear-btn" onclick="clearSelection()">Clear All</button>
<button class="select-all-btn" onclick="selectAll()">Select All</button>
<a href="./displayusb.asp" class="back-btn">Back to USB Devices</a>
</div>
</div>
<div class="sheets-container" id="sheetsContainer">
</div>
<script>
var selectedUSBs = [];
var allUSBs = [];
var MINI_LABELS_PER_CELL = 12;
var CELLS_PER_PAGE = 6;
document.querySelectorAll('.usb-item').forEach(function(el) {
var onclick = el.getAttribute('onclick');
var match = onclick.match(/toggleUSB\(this, (\d+), '([^']*)', '([^']*)'\)/);
if (match) {
allUSBs.push({
id: parseInt(match[1]),
serial: match[2].replace(/\\'/g, "'"),
alias: match[3].replace(/\\'/g, "'"),
element: el
});
}
});
function toggleUSB(el, id, serial, alias) {
var checkbox = el.querySelector('input[type="checkbox"]');
var index = selectedUSBs.findIndex(function(u) { return u.id === id; });
if (index > -1) {
selectedUSBs.splice(index, 1);
checkbox.checked = false;
el.classList.remove('selected');
} else {
selectedUSBs.push({ id: id, serial: serial, alias: alias });
checkbox.checked = true;
el.classList.add('selected');
}
updateSheets();
}
function clearSelection() {
selectedUSBs = [];
document.querySelectorAll('.usb-item').forEach(function(el) {
el.classList.remove('selected');
el.querySelector('input[type="checkbox"]').checked = false;
});
updateSheets();
}
function selectAll() {
selectedUSBs = [];
allUSBs.forEach(function(u) {
selectedUSBs.push({ id: u.id, serial: u.serial, alias: u.alias });
u.element.classList.add('selected');
u.element.querySelector('input[type="checkbox"]').checked = true;
});
updateSheets();
}
function updateSheets() {
var numUSBs = selectedUSBs.length;
var startCell = parseInt(document.getElementById('startCell').value) || 1;
// Calculate cells needed, accounting for skipped cells on first page
var skippedCells = startCell - 1;
var numCells = Math.ceil(numUSBs / MINI_LABELS_PER_CELL);
var totalCellsNeeded = numCells + skippedCells;
var numPages = Math.ceil(totalCellsNeeded / CELLS_PER_PAGE) || 0;
if (numUSBs === 0) numPages = 0;
document.getElementById('selectedCount').textContent = numUSBs;
document.getElementById('pageCount').textContent = numPages;
document.getElementById('printBtn').disabled = numUSBs === 0;
var container = document.getElementById('sheetsContainer');
container.innerHTML = '';
if (numUSBs === 0) {
return;
}
var usbIndex = 0;
for (var page = 0; page < numPages; page++) {
var sheet = document.createElement('div');
sheet.className = 'print-sheet';
sheet.style.position = 'relative';
var sheetLabel = document.createElement('div');
sheetLabel.className = 'sheet-label';
sheetLabel.textContent = 'Page ' + (page + 1) + ' of ' + numPages;
sheet.appendChild(sheetLabel);
for (var cellNum = 1; cellNum <= CELLS_PER_PAGE; cellNum++) {
var cell = document.createElement('div');
// On first page, skip cells before startCell
var skipThisCell = (page === 0 && cellNum < startCell);
var cellHasContent = !skipThisCell && usbIndex < numUSBs;
cell.className = 'label-cell cell-' + cellNum + (cellHasContent ? ' has-content' : ' empty');
if (cellHasContent) {
var miniGrid = document.createElement('div');
miniGrid.className = 'mini-grid';
for (var miniPos = 0; miniPos < MINI_LABELS_PER_CELL; miniPos++) {
var usb = selectedUSBs[usbIndex];
var miniLabel = document.createElement('div');
miniLabel.className = 'mini-label' + (usb ? ' filled' : ' empty');
if (usb) {
var barcodeId = 'bc-' + page + '-' + cellNum + '-' + miniPos;
miniLabel.innerHTML = '<div class="barcode-container"><svg id="' + barcodeId + '"></svg></div><div class="serial-text">' + escapeHtml(usb.serial) + '</div>';
usbIndex++;
}
miniGrid.appendChild(miniLabel);
}
cell.appendChild(miniGrid);
} else {
cell.innerHTML = '<div class="empty-cell-text">Empty</div>';
}
sheet.appendChild(cell);
}
container.appendChild(sheet);
}
setTimeout(generateAllBarcodes, 50);
}
function generateAllBarcodes() {
var usbIndex = 0;
var numUSBs = selectedUSBs.length;
var startCell = parseInt(document.getElementById('startCell').value) || 1;
var skippedCells = startCell - 1;
var numCells = Math.ceil(numUSBs / MINI_LABELS_PER_CELL);
var totalCellsNeeded = numCells + skippedCells;
var numPages = Math.ceil(totalCellsNeeded / CELLS_PER_PAGE) || 0;
usbIndex = 0;
for (var page = 0; page < numPages; page++) {
for (var cellNum = 1; cellNum <= CELLS_PER_PAGE; cellNum++) {
// Skip cells before startCell on first page
if (page === 0 && cellNum < startCell) continue;
for (var miniPos = 0; miniPos < MINI_LABELS_PER_CELL; miniPos++) {
if (usbIndex < numUSBs) {
var usb = selectedUSBs[usbIndex];
var barcodeId = 'bc-' + page + '-' + cellNum + '-' + miniPos;
var el = document.getElementById(barcodeId);
if (el) {
try {
JsBarcode('#' + barcodeId, usb.serial, {
format: "CODE128",
width: 1,
height: 22,
displayValue: false,
margin: 0,
background: "transparent"
});
} catch(e) {
console.error('Barcode error:', usb.serial, e);
}
}
usbIndex++;
}
}
}
}
}
function escapeHtml(text) {
if (!text) return '';
var div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
</script>
</body>
</html>

195
usbsingle.asp Normal file
View File

@@ -0,0 +1,195 @@
<%@ Language=VBScript %>
<%
Option Explicit
Dim objConn, rs
%>
<!DOCTYPE html>
<html>
<head>
<!--#include file="./includes/sql.asp"-->
<title>Print USB Barcode Label</title>
<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js"></script>
<style>
/* ULINE S-20135: 8.5x11 sheet, 3x3 labels, 2 cols x 3 rows */
/* Each 3x3 label holds 3x4 grid of 1x0.75 mini-labels (12 per cell) */
@page { size: letter; margin: 0; }
body { font-family: Arial, sans-serif; background: #f0f0f0; margin: 0; padding: 20px; }
.no-print { margin-bottom: 20px; text-align: center; }
.print-btn { padding: 10px 30px; font-size: 16px; cursor: pointer; background: #667eea; color: white; border: none; border-radius: 5px; margin: 5px; }
.back-btn { padding: 10px 30px; font-size: 16px; cursor: pointer; background: #6c757d; color: white; border: none; border-radius: 5px; margin: 5px; text-decoration: none; }
.position-select { padding: 8px; font-size: 14px; margin-left: 10px; }
.copies-input { padding: 6px; font-size: 14px; width: 60px; text-align: center; }
.print-sheet { width: 8.5in; height: 11in; background: white; margin: 0 auto; position: relative; border: 1px solid #ccc; }
.label-cell {
width: 3in;
height: 3in;
position: absolute;
box-sizing: border-box;
border: 1px dashed #ccc;
overflow: hidden;
}
.label-cell.active { border: 2px solid #667eea; }
.label-cell.inactive { border: 1px dashed #ccc; }
/* Label cell positions */
.cell-1 { top: 0.875in; left: 1.1875in; }
.cell-2 { top: 0.875in; left: 4.3125in; }
.cell-3 { top: 4in; left: 1.1875in; }
.cell-4 { top: 4in; left: 4.3125in; }
.cell-5 { top: 7.125in; left: 1.1875in; }
.cell-6 { top: 7.125in; left: 4.3125in; }
.mini-grid { display: grid; grid-template-columns: repeat(3, 1in); grid-template-rows: repeat(4, 0.75in); width: 3in; height: 3in; }
.mini-label { width: 1in; height: 0.75in; display: flex; flex-direction: column; align-items: center; justify-content: center; box-sizing: border-box; padding: 0.02in; border: 1px dotted #ddd; overflow: hidden; }
.mini-label.filled { border: 1px solid #999; }
.mini-label.empty { background: #f8f8f8; border: 1px dotted #eee; }
.barcode-container { text-align: center; line-height: 0; }
.barcode-container svg { max-width: 0.9in; height: 24px; }
.serial-text { font-size: 6pt; font-weight: bold; font-family: monospace; text-align: center; margin-top: 1px; letter-spacing: 0.3px; }
@media print {
body { padding: 0; margin: 0; background: white; }
.no-print { display: none; }
.print-sheet { border: none; margin: 0; width: 8.5in; height: 11in; overflow: hidden; }
.label-cell { border: none !important; }
.label-cell.inactive { visibility: hidden; }
.mini-label { border: 1px dotted #ccc !important; }
.mini-label.empty { visibility: hidden; }
}
</style>
</head>
<body>
<%
Dim serialNumber, usbAlias, machineid, strSQL
serialNumber = Request.QueryString("serial")
machineid = Request.QueryString("machineid")
If machineid <> "" And IsNumeric(machineid) Then
strSQL = "SELECT serialnumber, alias FROM machines WHERE machineid = " & CLng(machineid) & " AND machinetypeid = 44"
Set rs = objConn.Execute(strSQL)
If Not rs.EOF Then
serialNumber = rs("serialnumber") & ""
usbAlias = rs("alias") & ""
End If
rs.Close
Set rs = Nothing
End If
objConn.Close
serialNumber = Trim(serialNumber)
If serialNumber = "" Then serialNumber = "00000000"
%>
<div class="no-print">
<h2>USB Barcode Label</h2>
<p>Serial: <strong><code><%=Server.HTMLEncode(serialNumber)%></code></strong>
<% If usbAlias <> "" Then %> (<%=Server.HTMLEncode(usbAlias)%>)<% End If %>
</p>
<button class="print-btn" onclick="window.print()">Print Label</button>
<a href="./displayusb.asp" class="back-btn">Back to USB Devices</a>
<div style="margin-top: 15px;">
<label>Position:
<select class="position-select" id="posSelect" onchange="updateLayout()">
<option value="1">1 - Top Left</option>
<option value="2">2 - Top Right</option>
<option value="3">3 - Middle Left</option>
<option value="4">4 - Middle Right</option>
<option value="5">5 - Bottom Left</option>
<option value="6">6 - Bottom Right</option>
</select>
</label>
<label style="margin-left: 20px;">Copies (1-12):
<input type="number" class="copies-input" id="copiesInput" value="12" min="1" max="12" onchange="updateLayout()">
</label>
</div>
<p style="color: #666; font-size: 12px; margin-top: 10px;">Prints on ULINE S-20135 label sheet (3"x3" cells). Each cell holds up to 12 mini-labels (1"x0.75").</p>
</div>
<div class="print-sheet">
<div class="label-cell cell-1 active" id="cell-1"></div>
<div class="label-cell cell-2 inactive" id="cell-2"></div>
<div class="label-cell cell-3 inactive" id="cell-3"></div>
<div class="label-cell cell-4 inactive" id="cell-4"></div>
<div class="label-cell cell-5 inactive" id="cell-5"></div>
<div class="label-cell cell-6 inactive" id="cell-6"></div>
</div>
<script>
var serialNumber = "<%=Server.HTMLEncode(serialNumber)%>";
function updateLayout() {
var pos = parseInt(document.getElementById('posSelect').value) || 1;
var copies = parseInt(document.getElementById('copiesInput').value) || 12;
if (copies < 1) copies = 1;
if (copies > 12) copies = 12;
document.getElementById('copiesInput').value = copies;
for (var i = 1; i <= 6; i++) {
var cell = document.getElementById('cell-' + i);
if (i === pos) {
cell.className = 'label-cell cell-' + i + ' active';
cell.innerHTML = buildMiniGrid(copies);
} else {
cell.className = 'label-cell cell-' + i + ' inactive';
cell.innerHTML = '';
}
}
setTimeout(function() {
for (var j = 0; j < copies; j++) {
var barcodeId = 'bc-' + j;
var el = document.getElementById(barcodeId);
if (el) {
try {
JsBarcode('#' + barcodeId, serialNumber, {
format: "CODE128",
width: 1,
height: 22,
displayValue: false,
margin: 0,
background: "transparent"
});
} catch(e) {
console.error('Barcode error:', e);
}
}
}
}, 50);
}
function buildMiniGrid(copies) {
var html = '<div class="mini-grid">';
for (var i = 0; i < 12; i++) {
if (i < copies) {
html += '<div class="mini-label filled">' +
'<div class="barcode-container"><svg id="bc-' + i + '"></svg></div>' +
'<div class="serial-text">' + escapeHtml(serialNumber) + '</div>' +
'</div>';
} else {
html += '<div class="mini-label empty"></div>';
}
}
html += '</div>';
return html;
}
function escapeHtml(text) {
if (!text) return '';
var div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
updateLayout();
</script>
</body>
</html>