From c7834d4b99d4493d8e64d6358ba8304823b7eb6a Mon Sep 17 00:00:00 2001 From: cproudlock Date: Fri, 5 Dec 2025 16:36:44 -0500 Subject: [PATCH] Enhance WinRM remote script with app tracking and IP fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix FormTracePak app_id from 68 to 76 (68 is Media Creator Full Edition) - Add installed app tracking to remote WinRM script with embedded patterns - Add IP fallback for failed hostname connections (uses recorded 10.134.* IPs) - Add getRecordedIP API endpoint to lookup primary IP by hostname - Mark 10.134.*.* as primary IPs, other ranges as secondary/equipment IPs - Fix WinRM serialization issue by converting matched apps to JSON before return 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- api.asp | 936 +++++++++++++++++++++---- scripts/Update-PC-Minimal.ps1 | 473 +++++++++++++ scripts/Update-ShopfloorPCs-Remote.ps1 | 385 +++++++++- scripts/applications.csv | 21 + 4 files changed, 1657 insertions(+), 158 deletions(-) create mode 100644 scripts/Update-PC-Minimal.ps1 create mode 100644 scripts/applications.csv diff --git a/api.asp b/api.asp index 36c3d94..6d08906 100644 --- a/api.asp +++ b/api.asp @@ -1,33 +1,36 @@ <%@ Language=VBScript %> <% -Option Explicit -Response.Buffer = True -Response.ContentType = "application/json" - -' Declare and create database connection -Dim objConn, rs +' ============================================================================ +' DATABASE CONNECTION - Created directly in api.asp to avoid scoping issues +' ============================================================================ +Dim objConn, rs, DB_CONN_STRING +' Use direct MySQL ODBC driver connection (same as sql.asp) instead of DSN +DB_CONN_STRING = "Driver={MySQL ODBC 9.4 Unicode Driver};" & _ + "Server=192.168.122.1;" & _ + "Port=3306;" & _ + "Database=shopdb;" & _ + "User=570005354;" & _ + "Password=570005354;" & _ + "Option=3;" & _ + "Pooling=True;Max Pool Size=100;" +Session.Timeout = 15 Set objConn = Server.CreateObject("ADODB.Connection") -objConn.ConnectionString = "Driver={MySQL ODBC 9.4 Unicode Driver};" & _ - "Server=192.168.122.1;" & _ - "Port=3306;" & _ - "Database=shopdb;" & _ - "User=570005354;" & _ - "Password=570005354;" & _ - "Option=3;" +objConn.ConnectionString = DB_CONN_STRING objConn.Open Set rs = Server.CreateObject("ADODB.Recordset") +Response.Buffer = True +Response.ContentType = "application/json" + ' ============================================================================ ' ShopDB API - PowerShell Data Collection Endpoint ' ============================================================================ ' Purpose: Receive PC asset data from PowerShell scripts and store in Phase 2 schema ' Created: 2025-11-13 -' Modified: 2025-11-13 - Fixed objConn scoping, machinetypeid Phase 2 fix -' Schema: Phase 2 (machines table, machinetypeid 33/34/35 for PCs) +' Modified: 2025-11-21 - Use sql.asp include for database connection +' Schema: Phase 2 (machines table, machinetypeid 33-43 for PCs) ' ============================================================================ - - ' Error handling wrapper On Error Resume Next @@ -44,8 +47,14 @@ Select Case action UpdatePrinterMapping() Case "updateInstalledApps" UpdateInstalledApps() + Case "updateWinRMStatus" + UpdateWinRMStatus() Case "getDashboardData" GetDashboardData() + Case "getShopfloorPCs" + GetShopfloorPCs() + Case "getRecordedIP" + GetRecordedIP() Case Else SendError "Invalid action: " & action End Select @@ -98,6 +107,14 @@ Sub UpdateCompleteAsset() commConfigs = Request.Form("commConfigs") dncConfig = Request.Form("dncConfig") + ' VNC detection (optional) + Dim hasVnc + hasVnc = Trim(Request.Form("hasVnc") & "") + + ' WinRM detection (optional) + Dim hasWinRM + hasWinRM = Trim(Request.Form("hasWinRM") & "") + ' DNC/GE registry data dncDualPathEnabled = Request.Form("dncDualPathEnabled") dncPath1Name = Trim(Request.Form("dncPath1Name") & "") @@ -112,20 +129,210 @@ Sub UpdateCompleteAsset() Exit Sub End If - ' Get OS ID - osid = GetOrCreateOSID(osVersion) + ' Get OS ID (inlined to avoid VBScript scoping issues) + osid = 0 + If osVersion <> "" Then + Dim safeOsVersion, rsOS + safeOsVersion = Replace(osVersion, "'", "''") + Set rsOS = objConn.Execute("SELECT osid FROM operatingsystems WHERE operatingsystem = '" & safeOsVersion & "'") + If Not rsOS.EOF Then + osid = CLng(rsOS("osid")) + Else + objConn.Execute "INSERT INTO operatingsystems (operatingsystem) VALUES ('" & safeOsVersion & "')" + Set rsOS = objConn.Execute("SELECT LAST_INSERT_ID() AS newid") + osid = CLng(rsOS("newid")) + End If + rsOS.Close + Set rsOS = Nothing + End If pcstatusid = 3 ' Default to "In Use" ' Clear existing shopfloor data if this is a shopfloor PC Dim machineid If pcType = "Shopfloor" Then ClearShopfloorData hostname + If Err.Number <> 0 Then + Err.Clear + End If End If - ' Insert or update PC record - machineid = InsertOrUpdatePC(hostname, serialnumber, manufacturer, model, pcType, _ - loggedinuser, machinenumber, osid, pcstatusid, _ - warrantyEndDate, warrantyStatus, warrantyServiceLevel, warrantyDaysRemaining) + ' Clear any previous errors before InsertOrUpdatePC + Err.Clear + + ' Inline PC insert/update logic (avoids function scoping issues on production IIS) + Dim debugMsg + debugMsg = "" + + ' Check objConn + If objConn Is Nothing Then + SendError debugMsg & "1-objConn is Nothing" + Exit Sub + End If + debugMsg = debugMsg & "1-OK," + + If objConn.State <> 1 Then + SendError debugMsg & "2-objConn not open state=" & objConn.State + Exit Sub + End If + debugMsg = debugMsg & "2-OK," + + ' Get vendor ID inline + Dim vendorId, modelId, machineTypeId + Dim safeManufacturer, safeModel, rsVendor, rsModel + vendorId = 0 + modelId = 1 + + If manufacturer <> "" Then + safeManufacturer = Replace(manufacturer, "'", "''") + Set rsVendor = objConn.Execute("SELECT vendorid FROM vendors WHERE vendor = '" & safeManufacturer & "'") + If Err.Number <> 0 Then + SendError debugMsg & "3-vendor query failed: " & Err.Description + Exit Sub + End If + debugMsg = debugMsg & "3-OK," + + If Not rsVendor.EOF Then + vendorId = CLng(rsVendor("vendorid")) + debugMsg = debugMsg & "3a-found " & vendorId & "," + Else + objConn.Execute "INSERT INTO vendors (vendor) VALUES ('" & safeManufacturer & "')" + If Err.Number <> 0 Then + SendError debugMsg & "3b-vendor insert failed: " & Err.Description + Exit Sub + End If + Set rsVendor = objConn.Execute("SELECT LAST_INSERT_ID() AS newid") + vendorId = CLng(rsVendor("newid")) + debugMsg = debugMsg & "3b-created " & vendorId & "," + End If + rsVendor.Close + Set rsVendor = Nothing + End If + debugMsg = debugMsg & "4-vendorId=" & vendorId & "," + + ' Get model ID inline + If model <> "" And vendorId > 0 Then + safeModel = Replace(model, "'", "''") + Set rsModel = objConn.Execute("SELECT modelnumberid FROM models WHERE modelnumber = '" & safeModel & "' AND vendorid = " & vendorId) + If Err.Number <> 0 Then + SendError debugMsg & "5-model query failed: " & Err.Description + Exit Sub + End If + debugMsg = debugMsg & "5-OK," + + If Not rsModel.EOF Then + modelId = CLng(rsModel("modelnumberid")) + Else + objConn.Execute "INSERT INTO models (modelnumber, vendorid, notes, isactive) VALUES ('" & safeModel & "', " & vendorId & ", 'Auto-imported', 1)" + If Err.Number <> 0 Then + SendError debugMsg & "5b-model insert failed: " & Err.Description + Exit Sub + End If + Set rsModel = objConn.Execute("SELECT LAST_INSERT_ID() AS newid") + modelId = CLng(rsModel("newid")) + End If + rsModel.Close + Set rsModel = Nothing + End If + debugMsg = debugMsg & "6-modelId=" & modelId & "," + + ' Get machine type ID + Select Case LCase(pcType) + Case "standard" + machineTypeId = 33 + Case "engineer", "engineering" + machineTypeId = 34 + Case "shopfloor", "shop floor" + machineTypeId = 35 + Case "cmm" + machineTypeId = 41 + Case "wax trace", "waxtrace", "wax" + machineTypeId = 42 + Case "keyence", "measuring", "measuring tool" + machineTypeId = 43 + Case Else + machineTypeId = 33 + End Select + debugMsg = debugMsg & "7-typeId=" & machineTypeId & "," + + ' Check if PC exists + Dim strSQL, rsResult, safeHostname, safeSerial + safeHostname = Replace(hostname, "'", "''") + strSQL = "SELECT machineid FROM machines WHERE hostname = '" & safeHostname & "' AND machinetypeid IN (33,34,35,36,37,38,39,40,41,42,43)" + Set rsResult = objConn.Execute(strSQL) + If Err.Number <> 0 Then + SendError debugMsg & "8-PC check failed: " & Err.Description + Exit Sub + End If + debugMsg = debugMsg & "8-OK," + + If Not rsResult.EOF Then + ' PC exists - UPDATE + machineid = rsResult("machineid") + rsResult.Close + Set rsResult = Nothing + debugMsg = debugMsg & "9-UPDATE id=" & machineid & "," + + safeSerial = Replace(serialnumber, "'", "''") + + ' Determine VNC value (1 or 0) + Dim vncValue + If hasVnc = "1" Then + vncValue = 1 + Else + vncValue = 0 + End If + + ' Determine WinRM value (1 or 0) + Dim winrmValue + If hasWinRM = "1" Then + winrmValue = 1 + Else + winrmValue = 0 + End If + + strSQL = "UPDATE machines SET serialnumber='" & safeSerial & "', modelnumberid=" & modelId & ", machinetypeid=" & machineTypeId & ", osid=" & osid & ", isvnc=" & vncValue & ", iswinrm=" & winrmValue & ", lastupdated=NOW() WHERE machineid=" & machineid + objConn.Execute strSQL + If Err.Number <> 0 Then + SendError debugMsg & "10-UPDATE failed: " & Err.Description + Exit Sub + End If + debugMsg = debugMsg & "10-OK," + Else + ' PC doesn't exist - INSERT + rsResult.Close + Set rsResult = Nothing + debugMsg = debugMsg & "9-INSERT," + + safeSerial = Replace(serialnumber, "'", "''") + + ' Determine VNC value for insert (reuse logic from update branch) + Dim vncValueInsert + If hasVnc = "1" Then + vncValueInsert = 1 + Else + vncValueInsert = 0 + End If + + ' Determine WinRM value for insert + Dim winrmValueInsert + If hasWinRM = "1" Then + winrmValueInsert = 1 + Else + winrmValueInsert = 0 + End If + + strSQL = "INSERT INTO machines (hostname, serialnumber, modelnumberid, machinetypeid, osid, machinestatusid, isvnc, iswinrm, lastupdated) VALUES ('" & safeHostname & "', '" & safeSerial & "', " & modelId & ", " & machineTypeId & ", " & osid & ", " & pcstatusid & ", " & vncValueInsert & ", " & winrmValueInsert & ", NOW())" + objConn.Execute strSQL + If Err.Number <> 0 Then + SendError debugMsg & "10-INSERT failed: " & Err.Description + Exit Sub + End If + Set rsResult = objConn.Execute("SELECT LAST_INSERT_ID() AS newid") + machineid = CLng(rsResult("newid")) + rsResult.Close + Set rsResult = Nothing + debugMsg = debugMsg & "10-OK id=" & machineid & "," + End If If Err.Number <> 0 Then SendError "Failed to insert/update PC: " & Err.Description @@ -139,6 +346,18 @@ Sub UpdateCompleteAsset() LogToFile "PC record created/updated. machineid: " & machineid + ' Clear existing network interfaces before inserting new ones (prevents duplicates on re-scan) + If networkInterfaces <> "" Then + Dim cmdClearInterfaces + Set cmdClearInterfaces = Server.CreateObject("ADODB.Command") + cmdClearInterfaces.ActiveConnection = objConn + cmdClearInterfaces.CommandText = "DELETE FROM communications WHERE machineid = ? AND comstypeid = 1" + cmdClearInterfaces.Parameters.Append cmdClearInterfaces.CreateParameter("@machineid", 3, 1, , CLng(machineid)) + cmdClearInterfaces.Execute + LogToFile "Cleared " & cmdClearInterfaces.RecordsAffected & " existing network interfaces" + Set cmdClearInterfaces = Nothing + End If + ' Insert network interfaces Dim interfaceCount interfaceCount = 0 @@ -177,7 +396,7 @@ Sub UpdateCompleteAsset() UpdateWarrantyData machineid, warrantyEndDate, warrantyStatus, warrantyServiceLevel, warrantyDaysRemaining End If - ' Send success response + ' Send success response (flattened to avoid nested dictionary issues) Dim responseObj Set responseObj = Server.CreateObject("Scripting.Dictionary") responseObj.Add "success", True @@ -185,14 +404,10 @@ Sub UpdateCompleteAsset() responseObj.Add "machineid", machineid responseObj.Add "hostname", hostname responseObj.Add "operation", "complete" - - Dim dataObj - Set dataObj = Server.CreateObject("Scripting.Dictionary") - dataObj.Add "networkInterfaces", interfaceCount - dataObj.Add "commConfigs", commConfigCount - dataObj.Add "dncConfig", dncSuccess - dataObj.Add "relationshipCreated", relationshipCreated - responseObj.Add "data", dataObj + responseObj.Add "networkInterfacesCount", interfaceCount + responseObj.Add "commConfigsCount", commConfigCount + responseObj.Add "dncConfigSuccess", dncSuccess + responseObj.Add "relationshipCreated", relationshipCreated SendResponse responseObj End Sub @@ -224,25 +439,36 @@ Sub UpdatePrinterMapping() Dim printerid, matchMethod printerid = 0 matchMethod = "" + Dim safePrinterFQDN + safePrinterFQDN = Replace(printerFQDN, "'", "''") - ' Try exact printer name match + ' Try exact FQDN match first Dim strSQL, rsResult - strSQL = "SELECT printerid FROM printers WHERE printername = ?" - Set rsResult = ExecuteParameterizedQuery(objConn, strSQL, Array(printerFQDN)) + strSQL = "SELECT printerid FROM printers WHERE fqdn = '" & safePrinterFQDN & "'" + Set rsResult = objConn.Execute(strSQL) If Not rsResult.EOF Then printerid = rsResult("printerid") - matchMethod = "name" + matchMethod = "fqdn" End If rsResult.Close Set rsResult = Nothing - ' Try IP address match if FQDN looks like an IP + ' Try printer windows name match + If printerid = 0 Then + strSQL = "SELECT printerid FROM printers WHERE printerwindowsname = '" & safePrinterFQDN & "'" + Set rsResult = objConn.Execute(strSQL) + If Not rsResult.EOF Then + printerid = rsResult("printerid") + matchMethod = "windowsname" + End If + rsResult.Close + Set rsResult = Nothing + End If + + ' Try IP address match If printerid = 0 And IsIPAddress(printerFQDN) Then - strSQL = "SELECT p.printerid FROM printers p " & _ - "INNER JOIN machines m ON p.machineid = m.machineid " & _ - "LEFT JOIN communications c ON m.machineid = c.machineid AND c.isprimary = 1 " & _ - "WHERE c.address = ?" - Set rsResult = ExecuteParameterizedQuery(objConn, strSQL, Array(printerFQDN)) + strSQL = "SELECT printerid FROM printers WHERE ipaddress = '" & safePrinterFQDN & "'" + Set rsResult = objConn.Execute(strSQL) If Not rsResult.EOF Then printerid = rsResult("printerid") matchMethod = "ip" @@ -270,18 +496,14 @@ Sub UpdatePrinterMapping() Exit Sub End If - ' Send success response + ' Send success response (flattened to avoid nested dictionary issues) Dim responseObj Set responseObj = Server.CreateObject("Scripting.Dictionary") responseObj.Add "success", True responseObj.Add "message", "Printer mapping updated" - - Dim dataObj - Set dataObj = Server.CreateObject("Scripting.Dictionary") - dataObj.Add "printerId", printerid - dataObj.Add "machinesUpdated", 1 - dataObj.Add "matchMethod", matchMethod - responseObj.Add "data", dataObj + responseObj.Add "printerId", printerid + responseObj.Add "machinesUpdated", 1 + responseObj.Add "matchMethod", matchMethod SendResponse responseObj End Sub @@ -310,110 +532,325 @@ Sub UpdateInstalledApps() End If ' Parse JSON array of apps (simple parsing) + LogToFile "Raw installedApps: " & installedApps + Dim appsArray appsArray = ParseJSONArray(installedApps) LogToFile "Parsed apps array, count: " & (UBound(appsArray) + 1) - ' Clear existing app mappings for this PC - Dim cmdDelete - Set cmdDelete = Server.CreateObject("ADODB.Command") - cmdDelete.ActiveConnection = objConn - cmdDelete.CommandText = "DELETE FROM installedapps WHERE machineid = ?" - cmdDelete.Parameters.Append cmdDelete.CreateParameter("@machineid", 3, 1, , CLng(machineid)) - cmdDelete.Execute + ' Debug: show first parsed item + If UBound(appsArray) >= 0 Then + LogToFile "First item after parse: " & appsArray(0) + End If + + ' Clear existing app mappings for this PC using direct SQL + Dim deleteSQL + deleteSQL = "DELETE FROM installedapps WHERE machineid = " & CLng(machineid) + LogToFile "DELETE SQL: " & deleteSQL + objConn.Execute deleteSQL + + If Err.Number <> 0 Then + LogToFile "ERROR on DELETE: " & Err.Number & " - " & Err.Description + Err.Clear + Else + LogToFile "DELETE completed successfully" + End If ' Insert new app mappings - Dim appCount, i + Dim appCount, i, appName, appVersion, appid, appversionid, cmdInsert, appidStr, insertSQL + Dim debugLoopError, safeVer, verSQL, rsVer + debugLoopError = "" appCount = 0 + Err.Clear For i = 0 To UBound(appsArray) - Dim appName, appVersion - ' PowerShell sends lowercase 'name' and 'version' keys - appName = Trim(GetJSONValue(appsArray(i), "name") & "") + ' Get appid directly (pre-mapped from CSV) + appid = 0 + appidStr = Trim(GetJSONValue(appsArray(i), "appid") & "") + If appidStr <> "" And IsNumeric(appidStr) Then appid = CLng(appidStr) + + ' Get version and app name for logging appVersion = Trim(GetJSONValue(appsArray(i), "version") & "") + appName = Trim(GetJSONValue(appsArray(i), "appname") & "") - LogToFile "App " & i & ": name='" & appName & "', version='" & appVersion & "'" + LogToFile "App " & i & ": appid=" & appid & ", appname='" & appName & "', version='" & appVersion & "'" - If appName <> "" Then - ' Get or create application ID - Dim appid - appid = GetOrCreateApplication(appName, appVersion) + If appid > 0 Then + appversionid = 0 + Err.Clear - LogToFile "GetOrCreateApplication returned appid: " & appid + ' Version lookup and creation using global rs recordset + If appVersion <> "" And Not IsNull(appVersion) Then + safeVer = Replace(appVersion, "'", "''") + verSQL = "SELECT appversionid FROM appversions WHERE appid = " & CLng(appid) & " AND version = '" & safeVer & "'" - If appid > 0 Then - ' Insert mapping (installedapps table uses appid not applicationid) - Dim cmdInsert - Set cmdInsert = Server.CreateObject("ADODB.Command") - cmdInsert.ActiveConnection = objConn - cmdInsert.CommandText = "INSERT INTO installedapps (machineid, appid, isactive) VALUES (?, ?, ?)" - cmdInsert.Parameters.Append cmdInsert.CreateParameter("@machineid", 3, 1, , CLng(machineid)) - cmdInsert.Parameters.Append cmdInsert.CreateParameter("@appid", 3, 1, , CLng(appid)) - cmdInsert.Parameters.Append cmdInsert.CreateParameter("@isactive", 11, 1, , 1) ' 1 = active - cmdInsert.Execute - - If Err.Number = 0 Then - appCount = appCount + 1 + rs.Open verSQL, objConn, 0, 1 + If Err.Number <> 0 Then + debugLoopError = debugLoopError & " | rs.Open err:" & Err.Number + Err.Clear + ElseIf Not rs.EOF Then + appversionid = CLng(rs("appversionid")) + Else + ' Version not found - create it + If rs.State = 1 Then rs.Close + verSQL = "INSERT INTO appversions (appid, version) VALUES (" & CLng(appid) & ", '" & safeVer & "')" + objConn.Execute verSQL + If Err.Number = 0 Then + ' Get the new ID + rs.Open "SELECT LAST_INSERT_ID() AS newid", objConn, 0, 1 + If Err.Number = 0 And Not rs.EOF Then + appversionid = CLng(rs("newid")) + End If + If Err.Number <> 0 Then Err.Clear + Else + Err.Clear + End If End If + If rs.State = 1 Then rs.Close + End If + + ' Insert app + If appversionid > 0 Then + insertSQL = "INSERT INTO installedapps (machineid, appid, appversionid) VALUES (" & CLng(machineid) & ", " & CLng(appid) & ", " & CLng(appversionid) & ")" + Else + insertSQL = "INSERT INTO installedapps (machineid, appid) VALUES (" & CLng(machineid) & ", " & CLng(appid) & ")" + End If + objConn.Execute insertSQL + + If Err.Number = 0 Then + appCount = appCount + 1 + Else + debugLoopError = debugLoopError & " | INSERT error: " & Err.Number & " - " & Err.Description + Err.Clear End If End If Next LogToFile "Installed apps inserted: " & appCount - ' Send success response + ' Send success response with debug info Dim responseObj Set responseObj = Server.CreateObject("Scripting.Dictionary") responseObj.Add "success", True responseObj.Add "message", "Installed applications updated" - - Dim dataObj - Set dataObj = Server.CreateObject("Scripting.Dictionary") - dataObj.Add "appsProcessed", appCount - responseObj.Add "data", dataObj + responseObj.Add "appsProcessed", appCount + responseObj.Add "machineid", machineid + responseObj.Add "debugError", debugLoopError SendResponse responseObj End Sub Sub GetDashboardData() ' Simple health check endpoint - Dim responseObj + Dim responseObj, connStatus Set responseObj = Server.CreateObject("Scripting.Dictionary") responseObj.Add "success", True - responseObj.Add "message", "ShopDB API is online" + responseObj.Add "message", "ShopDB API is online - v13 (inlined all queries)" responseObj.Add "version", "1.0" responseObj.Add "schema", "Phase 2" + ' Debug: check objConn status + If objConn Is Nothing Then + connStatus = "objConn is Nothing" + ElseIf objConn.State = 1 Then + connStatus = "objConn is Open" + Else + connStatus = "objConn state=" & objConn.State + End If + responseObj.Add "connStatus", connStatus + SendResponse responseObj End Sub +Sub GetShopfloorPCs() + ' Returns list of all active shopfloor PCs for remote management + On Error Resume Next + + Dim rsPC, strSQL, pcList, pcCount, pcData + + ' Query active shopfloor PCs only (pctype = 'Shopfloor') + ' Include hostname, machineid, machinenumber (equipment), IP address, last updated + strSQL = "SELECT m.machineid, m.hostname, m.machinenumber, m.serialnumber, " & _ + "m.loggedinuser, m.lastupdated, " & _ + "c.address AS ipaddress, " & _ + "pt.typename AS pctype " & _ + "FROM machines m " & _ + "LEFT JOIN communications c ON m.machineid = c.machineid AND c.isprimary = 1 AND c.comstypeid = 1 " & _ + "INNER JOIN pctype pt ON m.pctypeid = pt.pctypeid " & _ + "WHERE m.isactive = 1 " & _ + "AND pt.typename = 'Shopfloor' " & _ + "ORDER BY m.hostname ASC" + + Set rsPC = objConn.Execute(strSQL) + + If Err.Number <> 0 Then + SendError "Database error: " & Err.Description + Exit Sub + End If + + ' Build JSON array of PCs + pcList = "" + pcCount = 0 + + Do While Not rsPC.EOF + If pcList <> "" Then pcList = pcList & "," + + ' Build individual PC object + pcData = "{" + pcData = pcData & """machineid"":" & rsPC("machineid") & "," + pcData = pcData & """hostname"":""" & EscapeJSON(rsPC("hostname") & "") & """," + pcData = pcData & """machinenumber"":""" & EscapeJSON(rsPC("machinenumber") & "") & """," + pcData = pcData & """serialnumber"":""" & EscapeJSON(rsPC("serialnumber") & "") & """," + pcData = pcData & """ipaddress"":""" & EscapeJSON(rsPC("ipaddress") & "") & """," + pcData = pcData & """loggedinuser"":""" & EscapeJSON(rsPC("loggedinuser") & "") & """," + pcData = pcData & """pctype"":""" & EscapeJSON(rsPC("pctype") & "") & """," + + ' Handle lastupdated date + If Not IsNull(rsPC("lastupdated")) Then + pcData = pcData & """lastupdated"":""" & FormatDateTime(rsPC("lastupdated"), 2) & " " & FormatDateTime(rsPC("lastupdated"), 4) & """" + Else + pcData = pcData & """lastupdated"":null" + End If + + pcData = pcData & "}" + pcList = pcList & pcData + pcCount = pcCount + 1 + + rsPC.MoveNext + Loop + + rsPC.Close + Set rsPC = Nothing + + ' Send response + Response.Write "{""success"":true,""count"":" & pcCount & ",""data"":[" & pcList & "]}" +End Sub + +Sub GetRecordedIP() + On Error Resume Next + Err.Clear + + Dim hostname, ipaddress + hostname = Trim(Request.Form("hostname")) + + If hostname = "" Then + SendError "Missing hostname parameter" + Exit Sub + End If + + ' Look up primary IP (10.134.*.*) from communications table for this hostname + Dim cmd, rs + Set cmd = Server.CreateObject("ADODB.Command") + cmd.ActiveConnection = objConn + cmd.CommandText = "SELECT c.address FROM communications c " & _ + "INNER JOIN machines m ON c.machineid = m.machineid " & _ + "WHERE m.hostname = ? AND c.address LIKE '10.134.%' " & _ + "ORDER BY c.isprimary DESC, c.communicationid ASC LIMIT 1" + cmd.Parameters.Append cmd.CreateParameter("@hostname", 200, 1, 50, hostname) + + Set rs = cmd.Execute() + + If Err.Number <> 0 Then + SendError "Database error: " & Err.Description + Exit Sub + End If + + If Not rs.EOF Then + ipaddress = rs("address") & "" + Response.Write "{""success"":true,""hostname"":""" & hostname & """,""ipaddress"":""" & ipaddress & """}" + Else + Response.Write "{""success"":false,""hostname"":""" & hostname & """,""ipaddress"":null,""message"":""No 10.134.*.* IP found""}" + End If + + rs.Close + Set rs = Nothing + Set cmd = Nothing +End Sub + ' ============================================================================ ' HELPER FUNCTIONS - PC MANAGEMENT ' ============================================================================ -Function InsertOrUpdatePC(hostname, serialnumber, manufacturer, model, pcType, _ +Function InsertOrUpdatePC(conn, hostname, serialnumber, manufacturer, model, pcType, _ loggedinuser, machinenumber, osid, pcstatusid, _ warrantyEndDate, warrantyStatus, warrantyServiceLevel, warrantyDaysRemaining) On Error Resume Next + Err.Clear - ' Get or create vendor/model IDs and determine machine type + ' Step 1: Check conn (passed as parameter) + If conn Is Nothing Then + Err.Raise 1001, "InsertOrUpdatePC", "conn is Nothing" + InsertOrUpdatePC = 0 + Exit Function + End If + + If conn.State <> 1 Then + Err.Raise 1002, "InsertOrUpdatePC", "conn not open, state=" & conn.State + InsertOrUpdatePC = 0 + Exit Function + End If + + ' Step 2: Get vendor ID (inlined to avoid VBScript scoping issues) Dim vendorId, modelId, machineTypeId - vendorId = GetOrCreateVendor(objConn, manufacturer) - modelId = GetOrCreateModel(objConn, model, vendorId) + Dim safeManufacturer, safeModel, rsVendor, rsModel + vendorId = 0 + modelId = 1 + + If manufacturer <> "" Then + safeManufacturer = Replace(manufacturer, "'", "''") + Set rsVendor = objConn.Execute("SELECT vendorid FROM vendors WHERE vendor = '" & safeManufacturer & "'") + If Not rsVendor.EOF Then + vendorId = CLng(rsVendor("vendorid")) + Else + ' Create new vendor + objConn.Execute "INSERT INTO vendors (vendor) VALUES ('" & safeManufacturer & "')" + Set rsVendor = objConn.Execute("SELECT LAST_INSERT_ID() AS newid") + vendorId = CLng(rsVendor("newid")) + End If + rsVendor.Close + Set rsVendor = Nothing + End If + + ' Step 3: Get model ID (inlined) + If model <> "" And vendorId > 0 Then + safeModel = Replace(model, "'", "''") + Set rsModel = objConn.Execute("SELECT modelnumberid FROM models WHERE modelnumber = '" & safeModel & "' AND vendorid = " & vendorId) + If Not rsModel.EOF Then + modelId = CLng(rsModel("modelnumberid")) + Else + ' Create new model + objConn.Execute "INSERT INTO models (modelnumber, vendorid, notes, isactive) VALUES ('" & safeModel & "', " & vendorId & ", 'Auto-imported via PowerShell', 1)" + Set rsModel = objConn.Execute("SELECT LAST_INSERT_ID() AS newid") + modelId = CLng(rsModel("newid")) + End If + rsModel.Close + Set rsModel = Nothing + End If + + ' Step 4: Get machine type ID machineTypeId = GetMachineTypeIdFromPCType(pcType) + ' Override machineTypeId based on machinenumber pattern + ' WJPRT* = Wax Trace PC (42) + If machinenumber <> "" Then + If UCase(Left(machinenumber, 5)) = "WJPRT" Then + machineTypeId = 42 ' PC - Wax Trace + LogToFile "Detected WJPRT pattern in machinenumber, setting machineTypeId to 42 (PC - Wax Trace)" + End If + End If + ' Ensure all IDs are numeric (fallback to safe defaults if empty) If Not IsNumeric(vendorId) Or vendorId = "" Then vendorId = 0 If Not IsNumeric(modelId) Or modelId = "" Then modelId = 1 ' TBD model - If Not IsNumeric(machineTypeId) Or machineTypeId = "" Then machineTypeId = 33 ' Standard PC + If Not IsNumeric(machineTypeId) Or machineTypeId = "" Then machineTypeId = 36 ' PC - Standard LogToFile "Vendor ID: " & vendorId & ", Model ID: " & modelId & ", Machine Type ID: " & machineTypeId ' Check if PC already exists (Phase 2: identify PCs by machinetypeid 33,34,35) Dim strSQL, rsResult, safeHostname safeHostname = Replace(hostname, "'", "''") - strSQL = "SELECT machineid FROM machines WHERE hostname = '" & safeHostname & "' AND machinetypeid IN (33,34,35)" + strSQL = "SELECT machineid FROM machines WHERE hostname = '" & safeHostname & "' AND machinetypeid IN (33,34,35,36,37,38,39,40,41,42,43)" Set rsResult = objConn.Execute(strSQL) Dim machineid @@ -491,7 +928,7 @@ Function InsertOrUpdatePC(hostname, serialnumber, manufacturer, model, pcType, _ "osid = " & sqlOsId & ", " & _ "machinestatusid = " & sqlStatusId & ", " & _ "lastupdated = NOW() " & _ - "WHERE machineid = " & CLng(machineid) & " AND machinetypeid IN (33,34,35)" + "WHERE machineid = " & CLng(machineid) & " AND machinetypeid IN (33,34,35,36,37,38,39,40,41,42,43)" LogToFile "UPDATE SQL built: " & Left(strSQL, 200) & "..." @@ -597,7 +1034,7 @@ Function GetMachineidByHostname(hostname) Dim strSQL, rsResult, safeHostname safeHostname = Replace(hostname, "'", "''") - strSQL = "SELECT machineid FROM machines WHERE hostname = '" & safeHostname & "' AND machinetypeid IN (33,34,35)" + strSQL = "SELECT machineid FROM machines WHERE hostname = '" & safeHostname & "' AND machinetypeid IN (33,34,35,36,37,38,39,40,41,42,43)" Set rsResult = objConn.Execute(strSQL) If Not rsResult.EOF Then @@ -677,10 +1114,16 @@ Function InsertNetworkInterfaces(machineid, networkInterfacesJSON) If interfaceName = "" Then interfaceName = "Interface " & (i + 1) - ' Determine if primary (first interface with valid IP) - Dim isPrimary + ' Determine if primary - 10.134.*.* is always primary for shopfloor PCs + Dim isPrimary, isPrimaryFromJson isPrimary = 0 - If i = 0 And ipAddress <> "" Then isPrimary = 1 + isPrimaryFromJson = GetJSONValue(interfacesArray(i), "IsPrimary") + If isPrimaryFromJson = True Or isPrimaryFromJson = "true" Or isPrimaryFromJson = "True" Then + isPrimary = 1 + ElseIf Left(ipAddress, 7) = "10.134." Then + ' Fallback: 10.134.*.* is always primary + isPrimary = 1 + End If ' Insert into communications table Dim cmdInsert @@ -688,7 +1131,7 @@ Function InsertNetworkInterfaces(machineid, networkInterfacesJSON) cmdInsert.ActiveConnection = objConn cmdInsert.CommandText = "INSERT INTO communications (" & _ "machineid, comstypeid, address, macaddress, " & _ - "subnetmask, gateway, interfacename, isprimary, isactive" & _ + "subnetmask, defaultgateway, interfacename, isprimary, isactive" & _ ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" ' Prepare parameter values (VBScript doesn't have IIf) @@ -894,10 +1337,10 @@ Function CreatePCMachineRelationship(pcMachineid, machineNumber) Exit Function End If - ' Find equipment by machine number (Phase 2: PCs are machinetypeid 33-35, Equipment is 1-32) + ' Find equipment by machine number (Phase 2: PCs are machinetypeid 33-43, Equipment is 1-32) Dim strSQL, rsResult, safeMachineNumber safeMachineNumber = Replace(machineNumber, "'", "''") - strSQL = "SELECT machineid FROM machines WHERE machinenumber = '" & safeMachineNumber & "' AND machinetypeid NOT IN (33,34,35)" + strSQL = "SELECT machineid FROM machines WHERE machinenumber = '" & safeMachineNumber & "' AND machinetypeid NOT IN (33,34,35,36,37,38,39,40,41,42,43)" LogToFile "CreatePCMachineRelationship: Executing SQL: " & strSQL Set rsResult = objConn.Execute(strSQL) @@ -923,7 +1366,7 @@ Function CreatePCMachineRelationship(pcMachineid, machineNumber) rsResult.Close Set rsResult = Nothing - LogToFile "CreatePCMachineRelationship: Creating relationship PC " & pcMachineid & " → Controls → Equipment " & equipmentMachineid + LogToFile "CreatePCMachineRelationship: Creating relationship PC " & pcMachineid & " -> Controls -> Equipment " & equipmentMachineid ' Get "Controls" relationship type ID strSQL = "SELECT relationshiptypeid FROM relationshiptypes WHERE relationshiptype = 'Controls'" @@ -942,7 +1385,7 @@ Function CreatePCMachineRelationship(pcMachineid, machineNumber) rsResult.Close Set rsResult = Nothing - ' Check if relationship already exists (PC → Equipment) + ' Check if relationship already exists (PC -> Equipment) strSQL = "SELECT relationshipid FROM machinerelationships " & _ "WHERE machineid = " & CLng(pcMachineid) & " AND related_machineid = " & CLng(equipmentMachineid) & " AND relationshiptypeid = " & CLng(relationshiptypeid) LogToFile "CreatePCMachineRelationship: Checking for duplicate: " & strSQL @@ -960,7 +1403,7 @@ Function CreatePCMachineRelationship(pcMachineid, machineNumber) rsResult.Close Set rsResult = Nothing - ' Create new Controls relationship (PC → Equipment) + ' Create new Controls relationship (PC -> Equipment) ' Fixed: PC should be machineid, Equipment should be related_machineid Dim cmdInsert Set cmdInsert = Server.CreateObject("ADODB.Command") @@ -991,8 +1434,8 @@ Sub UpdateWarrantyData(machineid, warrantyEndDate, warrantyStatus, warrantyServi ' Check if warranty record exists Dim strSQL, rsResult - strSQL = "SELECT warrantyid FROM warranties WHERE machineid = ?" - Set rsResult = ExecuteParameterizedQuery(objConn, strSQL, Array(machineid)) + strSQL = "SELECT warrantyid FROM warranties WHERE machineid = " & CLng(machineid) + Set rsResult = objConn.Execute(strSQL) If Not rsResult.EOF Then ' UPDATE existing warranty @@ -1054,14 +1497,26 @@ End Sub ' HELPER FUNCTIONS - LOOKUPS ' ============================================================================ -Function GetOrCreateVendor(conn, vendorName) +Function GetOrCreateVendor(connString, vendorName) On Error Resume Next + Err.Clear If vendorName = "" Then GetOrCreateVendor = 0 Exit Function End If + ' Create local connection for this function + Dim localConn + Set localConn = Server.CreateObject("ADODB.Connection") + localConn.ConnectionString = connString + localConn.Open + + If Err.Number <> 0 Then + GetOrCreateVendor = 0 + Exit Function + End If + ' Sanitize vendor name (prevent SQL injection) Dim safeName safeName = Replace(vendorName, "'", "''") @@ -1069,10 +1524,12 @@ Function GetOrCreateVendor(conn, vendorName) ' Check if vendor exists Dim strSQL, rsResult strSQL = "SELECT vendorid FROM vendors WHERE vendor = '" & safeName & "'" - Set rsResult = conn.Execute(strSQL) + Set rsResult = localConn.Execute(strSQL) If Err.Number <> 0 Then LogToFile "ERROR querying vendor: " & Err.Description + localConn.Close + Set localConn = Nothing GetOrCreateVendor = 0 Exit Function End If @@ -1081,6 +1538,8 @@ Function GetOrCreateVendor(conn, vendorName) GetOrCreateVendor = CLng(rsResult("vendorid")) rsResult.Close Set rsResult = Nothing + localConn.Close + Set localConn = Nothing LogToFile "Found existing vendor ID: " & GetOrCreateVendor Exit Function End If @@ -1089,20 +1548,24 @@ Function GetOrCreateVendor(conn, vendorName) ' Create new vendor strSQL = "INSERT INTO vendors (vendor) VALUES ('" & safeName & "')" - conn.Execute strSQL + localConn.Execute strSQL If Err.Number <> 0 Then LogToFile "ERROR creating vendor: " & Err.Description + localConn.Close + Set localConn = Nothing GetOrCreateVendor = 0 Exit Function End If ' Get new vendor ID strSQL = "SELECT LAST_INSERT_ID() AS newid" - Set rsResult = conn.Execute(strSQL) + Set rsResult = localConn.Execute(strSQL) GetOrCreateVendor = CLng(rsResult("newid")) rsResult.Close Set rsResult = Nothing + localConn.Close + Set localConn = Nothing LogToFile "Created new vendor ID: " & GetOrCreateVendor End Function @@ -1120,9 +1583,10 @@ Function GetOrCreateModel(conn, modelName, vendorId) End If ' Check if model exists for this vendor - Dim strSQL, rsResult - strSQL = "SELECT modelnumberid FROM models WHERE modelnumber = ? AND vendorid = ?" - Set rsResult = ExecuteParameterizedQuery(conn, strSQL, Array(modelName, vendorId)) + Dim strSQL, rsResult, safeModelName + safeModelName = Replace(modelName, "'", "''") + strSQL = "SELECT modelnumberid FROM models WHERE modelnumber = '" & safeModelName & "' AND vendorid = " & CLng(vendorId) + Set rsResult = conn.Execute(strSQL) If Not rsResult.EOF Then GetOrCreateModel = rsResult("modelnumberid") @@ -1226,9 +1690,10 @@ Function GetOrCreateApplication(appName, appVersion) End If ' Check if application exists (applications table has appid and appname columns) - Dim strSQL, rsResult - strSQL = "SELECT appid FROM applications WHERE appname = ?" - Set rsResult = ExecuteParameterizedQuery(objConn, strSQL, Array(appName)) + Dim strSQL, rsResult, safeAppName + safeAppName = Replace(appName, "'", "''") + strSQL = "SELECT appid FROM applications WHERE appname = '" & safeAppName & "'" + Set rsResult = objConn.Execute(strSQL) If Err.Number <> 0 Then LogToFile "ERROR querying applications: " & Err.Description @@ -1248,22 +1713,13 @@ Function GetOrCreateApplication(appName, appVersion) LogToFile "Application not found, creating new..." - ' Create new application (note: applications table doesn't have version column) - ' Version info is tracked elsewhere or ignored for now + ' Create new application Dim cmdInsert Set cmdInsert = Server.CreateObject("ADODB.Command") cmdInsert.ActiveConnection = objConn cmdInsert.CommandText = "INSERT INTO applications (appname, appdescription, isactive) VALUES (?, ?, ?)" cmdInsert.Parameters.Append cmdInsert.CreateParameter("@appname", 200, 1, 50, appName) - - ' Use version as description if available - Dim pDescription - If appVersion <> "" Then - pDescription = "Version: " & appVersion - Else - pDescription = "Auto-detected application" - End If - cmdInsert.Parameters.Append cmdInsert.CreateParameter("@description", 200, 1, 255, pDescription) + cmdInsert.Parameters.Append cmdInsert.CreateParameter("@description", 200, 1, 255, "Auto-detected application") cmdInsert.Parameters.Append cmdInsert.CreateParameter("@isactive", 11, 1, , 1) cmdInsert.Execute @@ -1282,25 +1738,149 @@ Function GetOrCreateApplication(appName, appVersion) Set rsResult = Nothing End Function +Function GetOrCreateAppVersion(conn, appId, appVersion) + ' Returns appversionid for the given app and version + ' Creates the version record if it doesn't exist + ' conn parameter passed explicitly to avoid VBScript scoping issues + On Error Resume Next + Err.Clear + + LogToFile "GetOrCreateAppVersion called with appId=" & appId & ", appVersion='" & appVersion & "'" + + ' Validate connection object first + If conn Is Nothing Then + LogToFile "ERROR: conn parameter is Nothing" + GetOrCreateAppVersion = 0 + Err.Clear + Exit Function + End If + + If conn.State <> 1 Then + LogToFile "ERROR: conn is not open, state=" & conn.State + GetOrCreateAppVersion = 0 + Err.Clear + Exit Function + End If + + If appId = 0 Or appId = "" Then + LogToFile "ERROR: appId is empty or zero" + GetOrCreateAppVersion = 0 + Err.Clear + Exit Function + End If + + ' If no version provided, return 0 (NULL will be stored) + If appVersion = "" Or IsNull(appVersion) Then + LogToFile "No version provided, returning 0" + GetOrCreateAppVersion = 0 + Err.Clear + Exit Function + End If + + ' Check if version exists for this app + Dim strSQL, rsResult, safeVersion + safeVersion = Replace(appVersion, "'", "''") + strSQL = "SELECT appversionid FROM appversions WHERE appid = " & CLng(appId) & " AND version = '" & safeVersion & "'" + LogToFile "SELECT SQL: " & strSQL + Set rsResult = conn.Execute(strSQL) + + If Err.Number <> 0 Then + LogToFile "ERROR querying appversions: " & Err.Number & " - " & Err.Description + Err.Clear + GetOrCreateAppVersion = 0 + Exit Function + End If + + ' Check if recordset is valid + If rsResult Is Nothing Then + LogToFile "ERROR: rsResult is Nothing after Execute" + GetOrCreateAppVersion = 0 + Err.Clear + Exit Function + End If + + If Not rsResult.EOF Then + GetOrCreateAppVersion = rsResult("appversionid") + LogToFile "Found existing appversionid: " & GetOrCreateAppVersion + rsResult.Close + Set rsResult = Nothing + Err.Clear + Exit Function + End If + rsResult.Close + Set rsResult = Nothing + + LogToFile "Version not found, creating new..." + + ' Create new version record using direct SQL (omit isactive - let it default, BIT column causes type mismatch) + Dim insertSQL + insertSQL = "INSERT INTO appversions (appid, version) VALUES (" & CLng(appId) & ", '" & safeVersion & "')" + LogToFile "INSERT SQL: " & insertSQL + conn.Execute insertSQL + + If Err.Number <> 0 Then + LogToFile "ERROR creating app version: " & Err.Number & " - " & Err.Description + Err.Clear + GetOrCreateAppVersion = 0 + Exit Function + End If + + ' Get new version ID + strSQL = "SELECT LAST_INSERT_ID() AS newid" + Set rsResult = conn.Execute(strSQL) + + If Err.Number <> 0 Then + LogToFile "ERROR getting LAST_INSERT_ID: " & Err.Number & " - " & Err.Description + Err.Clear + GetOrCreateAppVersion = 0 + Exit Function + End If + + If Not rsResult.EOF Then + GetOrCreateAppVersion = CLng(rsResult("newid")) + LogToFile "Created new app version with id: " & GetOrCreateAppVersion + Else + LogToFile "ERROR: LAST_INSERT_ID returned no rows" + GetOrCreateAppVersion = 0 + End If + rsResult.Close + Set rsResult = Nothing + Err.Clear +End Function + Function GetMachineTypeIdFromPCType(pcTypeString) On Error Resume Next ' Direct mapping from pcType parameter to machinetypeid (Phase 2 schema) - ' Phase 2 PC Machine Types: 33=Standard PC, 34=Engineering PC, 35=Shopfloor PC + ' Phase 2 PC Machine Types: + ' 36=PC - Standard, 37=PC - Shopfloor, 38=PC - Engineer + ' 41=PC - CMM, 42=PC - Wax Trace, 43=PC - Measuring Tool Dim pcTypeClean pcTypeClean = Trim(UCase(pcTypeString)) Select Case pcTypeClean Case "ENGINEER", "ENGINEERING" - GetMachineTypeIdFromPCType = 34 ' Engineering PC + GetMachineTypeIdFromPCType = 38 ' PC - Engineer Case "SHOPFLOOR", "SHOP FLOOR" - GetMachineTypeIdFromPCType = 35 ' Shopfloor PC + GetMachineTypeIdFromPCType = 37 ' PC - Shopfloor + Case "CMM" + GetMachineTypeIdFromPCType = 41 ' PC - CMM (runs PC-DMIS, goCMM, DODA) + Case "WAX TRACE", "WAXTRACE", "WAX" + GetMachineTypeIdFromPCType = 42 ' PC - Wax Trace (runs Formtracepak, FormStatusMonitor) + Case "KEYENCE" + GetMachineTypeIdFromPCType = 43 ' PC - Measuring Tool - Keyence (runs Keyence VR Series) + Case "EAS1000" + GetMachineTypeIdFromPCType = 43 ' PC - Measuring Tool - EAS1000 (runs GageCal, NI Software) + Case "PART MARKER", "PARTMARKER" + GetMachineTypeIdFromPCType = 43 ' PC - Measuring Tool - Part Marker (0615 machines) + Case "MEASURING", "MEASURING TOOL" + GetMachineTypeIdFromPCType = 43 ' PC - Measuring Tool (generic) Case "STANDARD", "" - GetMachineTypeIdFromPCType = 33 ' Standard PC + GetMachineTypeIdFromPCType = 36 ' PC - Standard Case Else - LogToFile "Unknown pcType '" & pcTypeString & "', defaulting to Standard PC (33)" - GetMachineTypeIdFromPCType = 33 ' Default to Standard PC + LogToFile "Unknown pcType '" & pcTypeString & "', defaulting to PC - Standard (36)" + GetMachineTypeIdFromPCType = 36 ' Default to PC - Standard End Select LogToFile "Mapped pcType '" & pcTypeString & "' to machinetypeid: " & GetMachineTypeIdFromPCType @@ -1524,6 +2104,31 @@ Function EscapeJSON(value) EscapeJSON = result End Function +Function ExecuteParameterizedQuery(conn, strSQL, params) + On Error Resume Next + + Dim cmd, i + Set cmd = Server.CreateObject("ADODB.Command") + cmd.ActiveConnection = conn + cmd.CommandText = strSQL + cmd.CommandType = 1 ' adCmdText + + ' Add parameters + If IsArray(params) Then + For i = 0 To UBound(params) + If IsNull(params(i)) Then + cmd.Parameters.Append cmd.CreateParameter("p" & i, 200, 1, 255, Null) + ElseIf IsNumeric(params(i)) Then + cmd.Parameters.Append cmd.CreateParameter("p" & i, 3, 1, , CLng(params(i))) + Else + cmd.Parameters.Append cmd.CreateParameter("p" & i, 200, 1, 255, CStr(params(i))) + End If + Next + End If + + Set ExecuteParameterizedQuery = cmd.Execute +End Function + Sub LogToFile(message) On Error Resume Next @@ -1551,4 +2156,67 @@ Sub LogToFile(message) Set fso = Nothing End Sub +' ============================================================================ +' UPDATE WINRM STATUS +' Called when remote PowerShell script successfully connects to a PC +' ============================================================================ +Sub UpdateWinRMStatus() + On Error Resume Next + + Dim hostname, hasWinRM + hostname = Trim(Request.Form("hostname") & "") + hasWinRM = Trim(Request.Form("hasWinRM") & "") + + ' Also accept query string for GET requests + If hostname = "" Then hostname = Trim(Request.QueryString("hostname") & "") + If hasWinRM = "" Then hasWinRM = Trim(Request.QueryString("hasWinRM") & "") + + ' Validate + If hostname = "" Then + SendError "hostname is required" + Exit Sub + End If + + ' Default to enabled if not specified + Dim winrmValue + If hasWinRM = "0" Then + winrmValue = 0 + Else + winrmValue = 1 + End If + + ' Find PC by hostname + Dim safeHostname, strSQL, rs + safeHostname = Replace(hostname, "'", "''") + strSQL = "SELECT machineid FROM machines WHERE hostname = '" & safeHostname & "' AND pctypeid IS NOT NULL" + Set rs = objConn.Execute(strSQL) + + If rs.EOF Then + rs.Close + Set rs = Nothing + SendError "PC not found: " & hostname + Exit Sub + End If + + Dim machineid + machineid = rs("machineid") + rs.Close + Set rs = Nothing + + ' Update WinRM status + strSQL = "UPDATE machines SET iswinrm = " & winrmValue & " WHERE machineid = " & machineid + objConn.Execute strSQL + + If Err.Number <> 0 Then + SendError "Update failed: " & Err.Description + Exit Sub + End If + + LogMessage "WinRM status updated for " & hostname & " (machineid=" & machineid & ") to " & winrmValue + + ' Send success response + Response.ContentType = "application/json" + Response.Write "{""success"":true,""message"":""WinRM status updated"",""hostname"":""" & hostname & """,""iswinrm"":" & winrmValue & "}" +End Sub + %> diff --git a/scripts/Update-PC-Minimal.ps1 b/scripts/Update-PC-Minimal.ps1 new file mode 100644 index 0000000..baf29b2 --- /dev/null +++ b/scripts/Update-PC-Minimal.ps1 @@ -0,0 +1,473 @@ +# Minimal PC data collection script +# For locked-down PCs with restricted permissions + +$apiUrl = "https://tsgwp00525.rd.ds.ge.com/shopdb/api.asp" +$logFile = "$env:TEMP\shopdb-update.log" + +# Start fresh log +"$(Get-Date) - Starting minimal PC update" | Out-File $logFile + +$data = @{ + action = 'updateCompleteAsset' + hostname = $env:COMPUTERNAME + manufacturer = 'Unknown' + model = 'Unknown' + serialNumber = 'Unknown' + pcType = 'Measuring' # Default, will be updated if PC-DMIS is found +} + +"Hostname: $($data.hostname)" | Tee-Object -FilePath $logFile -Append + +# Try to get serial number +try { + $bios = Get-CimInstance -ClassName Win32_BIOS -ErrorAction Stop + $data.serialNumber = $bios.SerialNumber + "Serial: $($data.serialNumber)" | Tee-Object -FilePath $logFile -Append +} catch { + "ERROR getting serial: $_" | Tee-Object -FilePath $logFile -Append +} + +# Try to get manufacturer/model +try { + $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop + $data.manufacturer = $cs.Manufacturer + $data.model = $cs.Model + "Manufacturer: $($data.manufacturer)" | Tee-Object -FilePath $logFile -Append + "Model: $($data.model)" | Tee-Object -FilePath $logFile -Append +} catch { + "ERROR getting system info: $_" | Tee-Object -FilePath $logFile -Append +} + +# Get IP address using ipconfig (no elevated permissions required) +$interfaces = @() +try { + $ipconfig = ipconfig /all + $currentIP = "" + $currentMAC = "" + + foreach ($line in $ipconfig) { + if ($line -match '^\S') { + # New adapter section - save previous if valid + if ($currentIP -and $currentIP -notlike '127.*' -and $currentIP -notlike '169.254.*') { + $interfaces += @{ + IPAddress = $currentIP + MACAddress = $currentMAC + } + "IP: $currentIP | MAC: $currentMAC" | Tee-Object -FilePath $logFile -Append + } + $currentIP = "" + $currentMAC = "" + } + elseif ($line -match 'IPv4 Address.*:\s*(\d+\.\d+\.\d+\.\d+)') { + $currentIP = $matches[1] -replace '\(Preferred\)','' + } + elseif ($line -match 'Physical Address.*:\s*([0-9A-F-]+)') { + $currentMAC = $matches[1] -replace '-',':' + } + } + # Don't forget the last adapter + if ($currentIP -and $currentIP -notlike '127.*' -and $currentIP -notlike '169.254.*') { + $interfaces += @{ + IPAddress = $currentIP + MACAddress = $currentMAC + } + "IP: $currentIP | MAC: $currentMAC" | Tee-Object -FilePath $logFile -Append + } +} catch { + "ERROR getting network info: $_" | Tee-Object -FilePath $logFile -Append +} + +if ($interfaces.Count -gt 0) { + $data.networkInterfaces = ($interfaces | ConvertTo-Json -Compress) +} + +# Try to get OS +try { + $os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop + $data.osVersion = $os.Caption + "OS: $($data.osVersion)" | Tee-Object -FilePath $logFile -Append +} catch { + "ERROR getting OS: $_" | Tee-Object -FilePath $logFile -Append +} + +# Try to get logged in user +try { + $data.loggedInUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name + "User: $($data.loggedInUser)" | Tee-Object -FilePath $logFile -Append +} catch { + "ERROR getting user: $_" | Tee-Object -FilePath $logFile -Append +} + +# Try to get machine number (no admin required for reading HKLM) +try { + $machineNo = $null + + # Primary location: GE Aircraft Engines DNC registry (64-bit and 32-bit) + $regPaths = @( + "HKLM:\SOFTWARE\GE Aircraft Engines\DNC\General", + "HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\General" + ) + + foreach ($regPath in $regPaths) { + if (Test-Path $regPath) { + $regValue = (Get-ItemProperty -Path $regPath -Name "MachineNo" -ErrorAction SilentlyContinue).MachineNo + if ($regValue) { + $machineNo = $regValue + "Machine # from registry ($regPath): $machineNo" | Tee-Object -FilePath $logFile -Append + break + } + } + } + + # Fall back to DNC.ini file + if (-not $machineNo) { + $dncIniPath = "C:\DNC\DNC.ini" + if (Test-Path $dncIniPath) { + $iniContent = Get-Content $dncIniPath -Raw -ErrorAction SilentlyContinue + if ($iniContent -match 'MachineNo\s*=\s*(.+)') { + $machineNo = $matches[1].Trim() + "Machine # from DNC.ini: $machineNo" | Tee-Object -FilePath $logFile -Append + } + } + } + + if ($machineNo) { + # Check if machine number is a generic/placeholder value + # These should not be sent to API - must be set manually + # Generic machine numbers - don't send to API but can help identify PC type + $genericMachineTypes = @{ + "^WJPRT" = "Measuring" # Generic printer/measuring tool + "^WJCMM" = "CMM" # Generic CMM + "^WJMEAS" = "Measuring" # Generic measuring + "^0600$" = "Wax Trace" # Wax trace machines + "^0612$" = "Part Marker" # Part markers + "^0613$" = "Part Marker" # Part markers + "^0615" = "Part Marker" # Part markers + "^TEST" = $null # Test machines - no type hint + "^TEMP" = $null # Temporary - no type hint + "^DEFAULT"= $null # Default value - no type hint + "^0+$" = $null # All zeros - no type hint + } + + $isGeneric = $false + $genericTypeHint = $null + foreach ($pattern in $genericMachineTypes.Keys) { + if ($machineNo -match $pattern) { + $isGeneric = $true + $genericTypeHint = $genericMachineTypes[$pattern] + break + } + } + + if ($isGeneric) { + if ($genericTypeHint) { + "Machine # '$machineNo' is generic ($genericTypeHint) - NOT sending to API (requires manual assignment)" | Tee-Object -FilePath $logFile -Append + # Store the type hint for later use in PC type detection + $script:genericTypeHint = $genericTypeHint + } else { + "Machine # '$machineNo' is generic/placeholder - NOT sending to API (requires manual assignment)" | Tee-Object -FilePath $logFile -Append + } + # Don't set $data.machineNo - leave it out of API call + } else { + $data.machineNo = $machineNo + } + } else { + "No machine number found" | Tee-Object -FilePath $logFile -Append + } +} catch { + "ERROR getting machine number: $_" | Tee-Object -FilePath $logFile -Append +} + +# Check for VNC installation +try { + $hasVnc = $false + + # Check registry for installed programs (both 32-bit and 64-bit) + $regPaths = @( + "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", + "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" + ) + + foreach ($path in $regPaths) { + if (Test-Path $path) { + $apps = Get-ItemProperty $path -ErrorAction SilentlyContinue | + Where-Object { $_.DisplayName -like "*VNC Server*" -or $_.DisplayName -like "*VNC Connect*" -or $_.DisplayName -like "*RealVNC*" } + if ($apps) { + $hasVnc = $true + "VNC detected: $($apps.DisplayName -join ', ')" | Tee-Object -FilePath $logFile -Append + break + } + } + } + + # Also check for VNC service + if (-not $hasVnc) { + $vncService = Get-Service -Name "vncserver*" -ErrorAction SilentlyContinue + if ($vncService) { + $hasVnc = $true + "VNC service detected: $($vncService.Name)" | Tee-Object -FilePath $logFile -Append + } + } + + if ($hasVnc) { + $data.hasVnc = "1" + } else { + $data.hasVnc = "0" + "No VNC detected" | Tee-Object -FilePath $logFile -Append + } +} catch { + "ERROR checking VNC: $_" | Tee-Object -FilePath $logFile -Append + $data.hasVnc = "0" +} + +# Check for WinRM status +try { + $hasWinRM = $false + + # Check if WinRM service is running + $winrmService = Get-Service -Name "WinRM" -ErrorAction SilentlyContinue + if ($winrmService -and $winrmService.Status -eq "Running") { + # Also verify WinRM is configured to accept connections + try { + $winrmConfig = winrm get winrm/config/service 2>$null + if ($winrmConfig -match "AllowRemoteAccess\s*=\s*true") { + $hasWinRM = $true + "WinRM enabled and accepting connections" | Tee-Object -FilePath $logFile -Append + } else { + "WinRM service running but remote access not enabled" | Tee-Object -FilePath $logFile -Append + } + } catch { + # If we can't check config, assume it's enabled if service is running + $hasWinRM = $true + "WinRM service running (config check skipped)" | Tee-Object -FilePath $logFile -Append + } + } else { + "WinRM service not running" | Tee-Object -FilePath $logFile -Append + } + + if ($hasWinRM) { + $data.hasWinRM = "1" + } else { + $data.hasWinRM = "0" + } +} catch { + "ERROR checking WinRM: $_" | Tee-Object -FilePath $logFile -Append + $data.hasWinRM = "0" +} + +# Load applications.csv for app matching +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$csvPath = Join-Path $scriptDir "applications.csv" +$appMappings = @() + +if (Test-Path $csvPath) { + try { + $appMappings = Import-Csv $csvPath | Where-Object { $_.enabled -eq "1" -and $_.app_id } + "Loaded $($appMappings.Count) app mappings from CSV" | Tee-Object -FilePath $logFile -Append + } catch { + "ERROR loading applications.csv: $_" | Tee-Object -FilePath $logFile -Append + } +} else { + "WARNING: applications.csv not found at $csvPath" | Tee-Object -FilePath $logFile -Append +} + +# Get installed applications from registry and match against CSV +$matchedApps = @() +$hasPcDmis = $false +$hasFormTracePak = $false +$hasKeyence = $false +$hasEAS1000 = $false +$hasGoCMM = $false +$hasDODA = $false +$hasFormStatusMonitor = $false +$hasGageCal = $false +$hasNISoftware = $false +try { + $regPaths = @( + "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", + "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" + ) + + # Get all installed apps + $installedApps = @() + foreach ($path in $regPaths) { + if (Test-Path $path) { + $apps = Get-ItemProperty $path -ErrorAction SilentlyContinue | + Where-Object { $_.DisplayName -and $_.DisplayName.Trim() -ne "" } + foreach ($app in $apps) { + $installedApps += @{ + DisplayName = $app.DisplayName.Trim() + Version = if ($app.DisplayVersion) { $app.DisplayVersion.Trim() } else { "" } + } + } + } + } + + "Found $($installedApps.Count) installed applications" | Tee-Object -FilePath $logFile -Append + + # Match against CSV patterns + foreach ($mapping in $appMappings) { + $patterns = $mapping.search_patterns -split '\|' + foreach ($installedApp in $installedApps) { + $matched = $false + foreach ($pattern in $patterns) { + if ($installedApp.DisplayName -match $pattern) { + $matched = $true + break + } + } + if ($matched) { + # Avoid duplicates + if (-not ($matchedApps | Where-Object { $_.appid -eq $mapping.app_id })) { + $matchedApps += @{ + appid = [int]$mapping.app_id + appname = $mapping.app_name + version = $installedApp.Version + } + "$($mapping.app_name) (ID:$($mapping.app_id)) detected: $($installedApp.DisplayName)" | Tee-Object -FilePath $logFile -Append + + # Check for PC type indicators + switch ($mapping.app_name) { + "PC-DMIS" { $hasPcDmis = $true } + "goCMM" { $hasGoCMM = $true } + "DODA" { $hasDODA = $true } + "FormTracePak" { $hasFormTracePak = $true } + "FormStatusMonitor" { $hasFormStatusMonitor = $true } + "Keyence VR Series" { $hasKeyence = $true } + "GageCal" { $hasGageCal = $true } + "NI Software" { $hasNISoftware = $true } + } + } + break + } + } + } + + "Matched $($matchedApps.Count) tracked applications" | Tee-Object -FilePath $logFile -Append + + if ($matchedApps.Count -gt 0) { + $data.installedApps = ($matchedApps | ConvertTo-Json -Compress) + } +} catch { + "ERROR getting installed apps: $_" | Tee-Object -FilePath $logFile -Append +} + +# File path fallbacks for apps that may not be in registry +# Check PC-DMIS +if (-not $hasPcDmis) { + try { + $pcDmisPaths = @( + "C:\ProgramData\Hexagon\PC-DMIS*", + "C:\Program Files\Hexagon\PC-DMIS*", + "C:\Program Files (x86)\Hexagon\PC-DMIS*", + "C:\Program Files\WAI\PC-DMIS*", + "C:\Program Files (x86)\WAI\PC-DMIS*" + ) + foreach ($dmisPath in $pcDmisPaths) { + if (Test-Path $dmisPath) { + $hasPcDmis = $true + "PC-DMIS found at: $dmisPath" | Tee-Object -FilePath $logFile -Append + break + } + } + } catch { + "ERROR checking PC-DMIS paths: $_" | Tee-Object -FilePath $logFile -Append + } +} + +# Check UDC if not already matched +if (-not ($matchedApps | Where-Object { $_.appid -eq 2 })) { + if (Test-Path "C:\Program Files\UDC") { + $matchedApps += @{ appid = 2; appname = "UDC"; version = "" } + "UDC found at: C:\Program Files\UDC" | Tee-Object -FilePath $logFile -Append + } +} + +# Check eDNC if not already matched +if (-not ($matchedApps | Where-Object { $_.appid -eq 8 })) { + if (Test-Path "C:\Program Files (x86)\DNC") { + $matchedApps += @{ appid = 8; appname = "eDNC"; version = "" } + "eDNC found at: C:\Program Files (x86)\DNC" | Tee-Object -FilePath $logFile -Append + } +} + +# Check FormTracePak if not already matched +if (-not $hasFormTracePak) { + try { + $formTracePaths = @( + "C:\Program Files\MitutoyoApp*", + "C:\Program Files (x86)\MitutoyoApp*" + ) + foreach ($ftPath in $formTracePaths) { + if (Test-Path $ftPath) { + $hasFormTracePak = $true + if (-not ($matchedApps | Where-Object { $_.appid -eq 76 })) { + $matchedApps += @{ appid = 76; appname = "FormTracePak"; version = "" } + } + "FormTracePak found at: $ftPath" | Tee-Object -FilePath $logFile -Append + break + } + } + } catch { + "ERROR checking FormTracePak paths: $_" | Tee-Object -FilePath $logFile -Append + } +} + +# Update installedApps data if we found more apps via file paths +if ($matchedApps.Count -gt 0) { + $data.installedApps = ($matchedApps | ConvertTo-Json -Compress) +} + +# Set PC type based on application detection +# Priority: CMM > Wax Trace > Keyence > EAS1000 > Part Marker > Generic hint > default Measuring +$isCMM = ($hasPcDmis -or $hasGoCMM -or $hasDODA) +$isWaxTrace = ($hasFormTracePak -or $hasFormStatusMonitor) +$isEAS1000 = ($hasGageCal -or $hasNISoftware) + +if ($isCMM) { + $data.pcType = "CMM" + $detected = @() + if ($hasPcDmis) { $detected += "PC-DMIS" } + if ($hasGoCMM) { $detected += "goCMM" } + if ($hasDODA) { $detected += "DODA" } + "PC Type set to: CMM ($($detected -join ', '))" | Tee-Object -FilePath $logFile -Append +} elseif ($isWaxTrace) { + $data.pcType = "Wax Trace" + $detected = @() + if ($hasFormTracePak) { $detected += "FormTracePak" } + if ($hasFormStatusMonitor) { $detected += "FormStatusMonitor" } + "PC Type set to: Wax Trace ($($detected -join ', '))" | Tee-Object -FilePath $logFile -Append +} elseif ($hasKeyence) { + $data.pcType = "Keyence" + "PC Type set to: Keyence (Keyence VR Series)" | Tee-Object -FilePath $logFile -Append +} elseif ($isEAS1000) { + $data.pcType = "EAS1000" + $detected = @() + if ($hasGageCal) { $detected += "GageCal" } + if ($hasNISoftware) { $detected += "NI Software" } + "PC Type set to: EAS1000 ($($detected -join ', '))" | Tee-Object -FilePath $logFile -Append +} elseif ($script:genericTypeHint) { + # Use generic machine number hint when no software detected + $data.pcType = $script:genericTypeHint + "PC Type set to: $($script:genericTypeHint) (from generic machine # - requires manual assignment)" | Tee-Object -FilePath $logFile -Append +} else { + "No specialized apps detected, keeping type: $($data.pcType)" | Tee-Object -FilePath $logFile -Append +} + +# Send to API +"Sending to API: $apiUrl" | Tee-Object -FilePath $logFile -Append + +try { + $response = Invoke-RestMethod -Uri $apiUrl -Method Post -Body $data -ErrorAction Stop + "API Response: $($response | ConvertTo-Json -Compress)" | Tee-Object -FilePath $logFile -Append + + if ($response.success) { + "SUCCESS - MachineID: $($response.machineid)" | Tee-Object -FilePath $logFile -Append + } else { + "FAILED - $($response.message)" | Tee-Object -FilePath $logFile -Append + } +} catch { + "ERROR calling API: $_" | Tee-Object -FilePath $logFile -Append +} + +"$(Get-Date) - Done" | Tee-Object -FilePath $logFile -Append diff --git a/scripts/Update-ShopfloorPCs-Remote.ps1 b/scripts/Update-ShopfloorPCs-Remote.ps1 index 8b250dd..0cd9bb2 100644 --- a/scripts/Update-ShopfloorPCs-Remote.ps1 +++ b/scripts/Update-ShopfloorPCs-Remote.ps1 @@ -339,19 +339,30 @@ function Get-RemotePCInfo { $ip = $adapter.IPAddress[$i] # Only include IPv4 addresses if ($ip -match '^\d+\.\d+\.\d+\.\d+$') { + # 10.134.*.* is always primary for shopfloor PCs + $isPrimary = ($ip -like "10.134.*") + # Secondary/equipment IPs: 192.168.*, 10.0.*, 100.* or any non-10.134 private IP + $isMachineNetwork = ( + ($ip -like "192.168.*") -or + ($ip -like "10.0.*") -or + ($ip -like "100.*") -or + (($ip -like "10.*") -and ($ip -notlike "10.134.*")) + ) $networkInterfaces += @{ IPAddress = $ip MACAddress = $adapter.MACAddress SubnetMask = if ($adapter.IPSubnet) { $adapter.IPSubnet[$i] } else { "" } DefaultGateway = if ($adapter.DefaultIPGateway) { $adapter.DefaultIPGateway[0] } else { "" } InterfaceName = $adapter.Description - IsMachineNetwork = ($ip -like "192.168.*" -or $ip -like "10.0.*") + IsPrimary = $isPrimary + IsMachineNetwork = $isMachineNetwork } } } } } - $result.NetworkInterfaces = $networkInterfaces + # Sort so 10.134.*.* (primary) comes first + $result.NetworkInterfaces = $networkInterfaces | Sort-Object -Property @{Expression={$_.IsPrimary}; Descending=$true} # Get DNC configuration if it exists $dncConfig = @{} @@ -360,23 +371,57 @@ function Get-RemotePCInfo { $iniContent = Get-Content $dncIniPath -Raw # Parse INI file for common DNC settings if ($iniContent -match 'Site\s*=\s*(.+)') { $dncConfig.Site = $matches[1].Trim() } - if ($iniContent -match 'CNC\s*=\s*(.+)') { $dncConfig.CNC = $matches[1].Trim() } - if ($iniContent -match 'NCIF\s*=\s*(.+)') { $dncConfig.NCIF = $matches[1].Trim() } - if ($iniContent -match 'MachineNumber\s*=\s*(.+)') { $dncConfig.MachineNumber = $matches[1].Trim() } - if ($iniContent -match 'FTPHost\s*=\s*(.+)') { $dncConfig.FTPHostPrimary = $matches[1].Trim() } + if ($iniContent -match 'Cnc\s*=\s*(.+)') { $dncConfig.CNC = $matches[1].Trim() } + if ($iniContent -match 'NcIF\s*=\s*(.+)') { $dncConfig.NCIF = $matches[1].Trim() } + if ($iniContent -match 'MachineNo\s*=\s*(.+)') { $dncConfig.MachineNo = $matches[1].Trim() } + if ($iniContent -match 'FtpHostPrimary\s*=\s*(.+)') { $dncConfig.FTPHostPrimary = $matches[1].Trim() } } $result.DNCConfig = $dncConfig # Get machine number from registry or DNC config $machineNo = $null - # Try registry first - $regPath = "HKLM:\SOFTWARE\GE\ShopFloor" - if (Test-Path $regPath) { - $machineNo = (Get-ItemProperty -Path $regPath -Name "MachineNumber" -ErrorAction SilentlyContinue).MachineNumber + # Try registry first - GE Aircraft Engines DNC location + $regPaths = @( + "HKLM:\SOFTWARE\GE Aircraft Engines\DNC\General", + "HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\General" + ) + foreach ($regPath in $regPaths) { + if (Test-Path $regPath) { + $machineNo = (Get-ItemProperty -Path $regPath -Name "MachineNo" -ErrorAction SilentlyContinue).MachineNo + if ($machineNo) { break } + } } # Fall back to DNC config - if (-not $machineNo -and $dncConfig.MachineNumber) { - $machineNo = $dncConfig.MachineNumber + if (-not $machineNo -and $dncConfig.MachineNo) { + $machineNo = $dncConfig.MachineNo + } + + # Check if machine number is generic (don't send to API) + $genericTypeHint = $null + if ($machineNo) { + $genericMachineTypes = @{ + "^WJPRT" = "Measuring" # Generic printer/measuring tool + "^WJCMM" = "CMM" # Generic CMM + "^WJMEAS" = "Measuring" # Generic measuring + "^0600$" = "Wax Trace" # Wax trace machines + "^0612$" = "Part Marker" # Part markers + "^0613$" = "Part Marker" # Part markers + "^0615" = "Part Marker" # Part markers + "^TEST" = $null # Test machines + "^TEMP" = $null # Temporary + "^DEFAULT"= $null # Default value + "^0+$" = $null # All zeros + } + + foreach ($pattern in $genericMachineTypes.Keys) { + if ($machineNo -match $pattern) { + $genericTypeHint = $genericMachineTypes[$pattern] + $result.IsGenericMachineNo = $true + $result.GenericTypeHint = $genericTypeHint + $machineNo = $null # Don't send generic machine numbers + break + } + } } $result.MachineNo = $machineNo @@ -413,25 +458,100 @@ function Get-RemotePCInfo { } $result.HasVnc = $hasVnc - # Check for PC-DMIS installation (CMM software) - $hasPcDmis = $false + # ================================================================ + # Detect installed applications for PC type classification + # ================================================================ + + # Get all installed apps once for efficiency (with version info) + $installedApps = @() + $installedAppsWithVersion = @() foreach ($path in $regPaths) { if (Test-Path $path) { - $pcDmisApps = Get-ItemProperty $path -ErrorAction SilentlyContinue | - Where-Object { $_.DisplayName -like "*PC-DMIS*" -or $_.DisplayName -like "*PCDMIS*" } - if ($pcDmisApps) { - $hasPcDmis = $true + $apps = Get-ItemProperty $path -ErrorAction SilentlyContinue | + Where-Object { $_.DisplayName -and $_.DisplayName.Trim() -ne "" } + foreach ($app in $apps) { + $installedApps += $app.DisplayName + $installedAppsWithVersion += @{ + DisplayName = $app.DisplayName.Trim() + Version = if ($app.DisplayVersion) { $app.DisplayVersion.Trim() } else { "" } + } + } + } + } + + # ================================================================ + # Match against tracked applications (embedded from applications.csv) + # ================================================================ + $trackedAppPatterns = @( + @{ app_id = 2; app_name = "UDC"; patterns = @("Universal Data Collection") } + @{ app_id = 4; app_name = "CLM"; patterns = @("PPDCS", "CLM") } + @{ app_id = 6; app_name = "PC-DMIS"; patterns = @("PC-DMIS", "PCDMIS") } + @{ app_id = 7; app_name = "Oracle"; patterns = @("OracleDatabase", "Oracle Database", "Oracle.*Database") } + @{ app_id = 8; app_name = "eDNC"; patterns = @("eDNC") } + @{ app_id = 22; app_name = "OpenText"; patterns = @("OpenText", "CSF") } + @{ app_id = 30; app_name = "Tanium"; patterns = @("^Tanium Client") } + @{ app_id = 76; app_name = "FormTracePak"; patterns = @("FormTracePak", "Formtracepak", "Form Trace", "FormTrace") } + @{ app_id = 69; app_name = "Keyence VR Series"; patterns = @("VR-3000", "VR-5000", "VR-6000", "KEYENCE VR") } + @{ app_id = 70; app_name = "Genspect"; patterns = @("Genspect") } + @{ app_id = 71; app_name = "GageCal"; patterns = @("GageCal") } + @{ app_id = 72; app_name = "NI Software"; patterns = @("^NI-", "National Instruments", "NI System", "NI Measurement", "NI LabVIEW") } + @{ app_id = 73; app_name = "goCMM"; patterns = @("goCMM") } + @{ app_id = 74; app_name = "DODA"; patterns = @("Dovetail Digital Analysis", "DODA") } + @{ app_id = 75; app_name = "FormStatusMonitor"; patterns = @("FormStatusMonitor") } + ) + + $matchedApps = @() + foreach ($tracked in $trackedAppPatterns) { + foreach ($installedApp in $installedAppsWithVersion) { + $matched = $false + foreach ($pattern in $tracked.patterns) { + if ($installedApp.DisplayName -match $pattern) { + $matched = $true + break + } + } + if ($matched) { + # Avoid duplicates + if (-not ($matchedApps | Where-Object { $_.appid -eq $tracked.app_id })) { + $matchedApps += @{ + appid = $tracked.app_id + appname = $tracked.app_name + version = $installedApp.Version + displayname = $installedApp.DisplayName + } + } break } } } + # Store matched apps as JSON string to survive WinRM serialization + $result.MatchedAppsCount = $matchedApps.Count + $result.MatchedAppNames = ($matchedApps | ForEach-Object { $_.appname }) -join ", " + $result.AllInstalledApps = ($installedApps | Sort-Object) -join "|" + $result.AllInstalledAppsCount = $installedApps.Count + if ($matchedApps.Count -gt 0) { + $result.MatchedAppsJson = ($matchedApps | ConvertTo-Json -Compress) + } else { + $result.MatchedAppsJson = "" + } + + # CMM Detection: PC-DMIS, goCMM, DODA + $hasPcDmis = $false + $hasGoCMM = $false + $hasDODA = $false + foreach ($app in $installedApps) { + if ($app -match "PC-DMIS|PCDMIS") { $hasPcDmis = $true } + if ($app -match "^goCMM") { $hasGoCMM = $true } + if ($app -match "Dovetail Digital Analysis|DODA") { $hasDODA = $true } + } # Also check common PC-DMIS installation paths if (-not $hasPcDmis) { $pcDmisPaths = @( "C:\Program Files\Hexagon\PC-DMIS*", "C:\Program Files (x86)\Hexagon\PC-DMIS*", "C:\Program Files\WAI\PC-DMIS*", - "C:\Program Files (x86)\WAI\PC-DMIS*" + "C:\Program Files (x86)\WAI\PC-DMIS*", + "C:\ProgramData\Hexagon\PC-DMIS*" ) foreach ($dmisPath in $pcDmisPaths) { if (Test-Path $dmisPath) { @@ -441,6 +561,76 @@ function Get-RemotePCInfo { } } $result.HasPcDmis = $hasPcDmis + $result.HasGoCMM = $hasGoCMM + $result.HasDODA = $hasDODA + $result.IsCMM = ($hasPcDmis -or $hasGoCMM -or $hasDODA) + + # Wax Trace Detection: FormTracePak, FormStatusMonitor + $hasFormTracePak = $false + $hasFormStatusMonitor = $false + foreach ($app in $installedApps) { + if ($app -match "FormTracePak|Formtracepak|Form Trace|FormTrace") { $hasFormTracePak = $true } + if ($app -match "FormStatusMonitor") { $hasFormStatusMonitor = $true } + } + # Check file path fallback + if (-not $hasFormTracePak) { + $ftPaths = @("C:\Program Files\MitutoyoApp*", "C:\Program Files (x86)\MitutoyoApp*") + foreach ($ftPath in $ftPaths) { + if (Test-Path $ftPath) { $hasFormTracePak = $true; break } + } + } + $result.HasFormTracePak = $hasFormTracePak + $result.HasFormStatusMonitor = $hasFormStatusMonitor + $result.IsWaxTrace = ($hasFormTracePak -or $hasFormStatusMonitor) + + # Keyence Detection: VR Series, Keyence VR USB Driver + $hasKeyence = $false + foreach ($app in $installedApps) { + if ($app -match "VR-3000|VR-5000|VR-6000|KEYENCE VR") { + $hasKeyence = $true + break + } + } + $result.HasKeyence = $hasKeyence + $result.IsKeyence = $hasKeyence + + # EAS1000 Detection: GageCal, NI Software (National Instruments) + $hasGageCal = $false + $hasNISoftware = $false + foreach ($app in $installedApps) { + if ($app -match "^GageCal") { $hasGageCal = $true } + if ($app -match "^NI-|National Instruments|NI System|NI Measurement|NI LabVIEW") { $hasNISoftware = $true } + } + $result.HasGageCal = $hasGageCal + $result.HasNISoftware = $hasNISoftware + $result.IsEAS1000 = ($hasGageCal -or $hasNISoftware) + + # Genspect Detection (could be Keyence or EAS1000) + $hasGenspect = $false + foreach ($app in $installedApps) { + if ($app -match "^Genspect") { + $hasGenspect = $true + break + } + } + $result.HasGenspect = $hasGenspect + + # Determine PC Type based on detected software + # Priority: CMM > Wax Trace > Keyence > EAS1000 > Generic hint > Measuring (default) + $detectedPcType = "Measuring" # Default for shopfloor PCs + if ($result.IsCMM) { + $detectedPcType = "CMM" + } elseif ($result.IsWaxTrace) { + $detectedPcType = "Wax Trace" + } elseif ($result.IsKeyence) { + $detectedPcType = "Keyence" + } elseif ($result.IsEAS1000) { + $detectedPcType = "EAS1000" + } elseif ($result.GenericTypeHint) { + # Use generic machine number hint when no software detected + $detectedPcType = $result.GenericTypeHint + } + $result.DetectedPcType = $detectedPcType $result.Success = $true } catch { @@ -467,8 +657,13 @@ function Send-PCDataToApi { ) try { - # Determine PC type based on machine number - $pcType = if ($PCData.MachineNo) { "Shopfloor" } else { "Standard" } + # Determine PC type - use detected type from software analysis, fallback to machine number + $pcType = "Measuring" # Default + if ($PCData.DetectedPcType) { + $pcType = $PCData.DetectedPcType + } elseif ($PCData.MachineNo) { + $pcType = "Shopfloor" + } # Build the POST body $postData = @{ @@ -504,6 +699,11 @@ function Send-PCDataToApi { $postData.dncConfig = ($PCData.DNCConfig | ConvertTo-Json -Compress) } + # Add matched/tracked applications (already JSON from remote scriptblock) + if ($PCData.MatchedAppsJson -and $PCData.MatchedAppsJson -ne "") { + $postData.installedApps = $PCData.MatchedAppsJson + } + # Send to API $response = Invoke-RestMethod -Uri $ApiUrl -Method Post -Body $postData -ErrorAction Stop @@ -694,6 +894,30 @@ foreach ($result in $results) { if ($result.MachineNo) { Write-Log " Machine #: $($result.MachineNo)" -Level "INFO" + } elseif ($result.IsGenericMachineNo) { + Write-Log " Machine #: (generic - requires manual assignment)" -Level "WARNING" + } + + # Show detected PC type and software + if ($result.DetectedPcType) { + $detectedSoftware = @() + if ($result.HasPcDmis) { $detectedSoftware += "PC-DMIS" } + if ($result.HasGoCMM) { $detectedSoftware += "goCMM" } + if ($result.HasDODA) { $detectedSoftware += "DODA" } + if ($result.HasFormTracePak) { $detectedSoftware += "FormTracePak" } + if ($result.HasFormStatusMonitor) { $detectedSoftware += "FormStatusMonitor" } + if ($result.HasKeyence) { $detectedSoftware += "Keyence VR" } + if ($result.HasGageCal) { $detectedSoftware += "GageCal" } + if ($result.HasNISoftware) { $detectedSoftware += "NI Software" } + if ($result.HasGenspect) { $detectedSoftware += "Genspect" } + + $softwareStr = if ($detectedSoftware.Count -gt 0) { " (" + ($detectedSoftware -join ", ") + ")" } else { "" } + Write-Log " PC Type: $($result.DetectedPcType)$softwareStr" -Level "INFO" + } + + # Log tracked apps count + if ($result.MatchedAppsCount -and $result.MatchedAppsCount -gt 0) { + Write-Log " Tracked Apps: $($result.MatchedAppsCount) matched ($($result.MatchedAppNames))" -Level "INFO" } # Pass the result hashtable directly to the API function @@ -706,6 +930,22 @@ foreach ($result in $results) { if ($apiResult.Success) { Write-Log " -> Updated in ShopDB (MachineID: $($apiResult.MachineId))" -Level "SUCCESS" + + # Update WinRM status - since we successfully connected via WinRM, mark it as enabled + try { + $winrmBody = @{ + action = "updateWinRMStatus" + hostname = $result.Hostname + hasWinRM = "1" + } + $winrmResponse = Invoke-RestMethod -Uri $ApiUrl -Method Post -Body $winrmBody -ErrorAction Stop + if ($winrmResponse.success) { + Write-Log " -> WinRM status updated" -Level "SUCCESS" + } + } catch { + Write-Log " -> WinRM status update failed (non-critical): $_" -Level "WARNING" + } + $successCount++ $successPCs += @{ Hostname = $result.Hostname @@ -731,17 +971,114 @@ foreach ($result in $results) { Write-Host "" } -# Report any connection errors +# Collect connection errors for IP fallback retry +$connectionFailures = @() foreach ($err in $remoteErrors) { $targetPC = if ($err.TargetObject) { $err.TargetObject } else { "Unknown" } Write-Log "[FAIL] ${targetPC}: $($err.Exception.Message)" -Level "ERROR" - $failCount++ - $failedPCs += @{ + $connectionFailures += @{ Hostname = $targetPC + FQDN = $targetPC Error = $err.Exception.Message } } +# IP Fallback: Retry failed connections using recorded IP addresses (10.134.*.*) +if ($connectionFailures.Count -gt 0) { + Write-Host "" + Write-Log "Attempting IP fallback for $($connectionFailures.Count) failed connection(s)..." -Level "INFO" + + foreach ($failure in $connectionFailures) { + # Extract hostname from FQDN + $hostname = $failure.FQDN -replace "\..*$", "" + + # Query API for recorded IP address + try { + $ipLookupBody = @{ + action = "getRecordedIP" + hostname = $hostname + } + $ipResponse = Invoke-RestMethod -Uri $ApiUrl -Method Post -Body $ipLookupBody -ErrorAction Stop + + if ($ipResponse.success -and $ipResponse.ipaddress -and $ipResponse.ipaddress -match "^10\.134\.") { + $fallbackIP = $ipResponse.ipaddress + Write-Log " Found recorded IP for ${hostname}: $fallbackIP - retrying..." -Level "INFO" + + # Retry connection using IP address + $ipSessionParams = @{ + ComputerName = $fallbackIP + ScriptBlock = (Get-RemotePCInfo) + SessionOption = $sessionOption + Authentication = 'Negotiate' + ErrorAction = 'Stop' + } + if ($Credential) { + $ipSessionParams.Credential = $Credential + } + + try { + $ipResult = Invoke-Command @ipSessionParams + + if ($ipResult.Success) { + Write-Log "[OK] $($ipResult.Hostname) (via IP: $fallbackIP)" -Level "SUCCESS" + Write-Log " Serial: $($ipResult.SerialNumber) | Model: $($ipResult.Model) | OS: $($ipResult.OSVersion)" -Level "INFO" + + if ($ipResult.NetworkInterfaces -and $ipResult.NetworkInterfaces.Count -gt 0) { + $ips = ($ipResult.NetworkInterfaces | ForEach-Object { $_.IPAddress }) -join ", " + Write-Log " IPs: $ips" -Level "INFO" + } + + if ($ipResult.DetectedPcType) { + Write-Log " PC Type: $($ipResult.DetectedPcType)" -Level "INFO" + } + + if ($ipResult.MatchedAppsCount -and $ipResult.MatchedAppsCount -gt 0) { + Write-Log " Tracked Apps: $($ipResult.MatchedAppsCount) matched ($($ipResult.MatchedAppNames))" -Level "INFO" + } + + # Send to API + Write-Log " Sending to API..." -Level "INFO" + $apiResult = Send-PCDataToApi -PCData $ipResult -ApiUrl $ApiUrl + + if ($apiResult.Success) { + Write-Log " -> Updated in ShopDB (MachineID: $($apiResult.MachineId))" -Level "SUCCESS" + $successCount++ + $successPCs += @{ + Hostname = $ipResult.Hostname + MachineId = $apiResult.MachineId + Serial = $ipResult.SerialNumber + ViaIP = $fallbackIP + } + } else { + Write-Log " -> API Error: $($apiResult.Message)" -Level "ERROR" + $failCount++ + $failedPCs += @{ Hostname = $hostname; Error = "API: $($apiResult.Message)" } + } + } else { + Write-Log "[FAIL] $hostname (via IP: $fallbackIP): $($ipResult.Error)" -Level "ERROR" + $failCount++ + $failedPCs += @{ Hostname = $hostname; Error = $ipResult.Error } + } + } catch { + Write-Log "[FAIL] $hostname (via IP: $fallbackIP): $($_.Exception.Message)" -Level "ERROR" + $failCount++ + $failedPCs += @{ Hostname = $hostname; Error = $_.Exception.Message } + } + } else { + # No valid IP found, add to failed list + Write-Log " No 10.134.*.* IP recorded for $hostname - skipping fallback" -Level "WARNING" + $failCount++ + $failedPCs += @{ Hostname = $hostname; Error = $failure.Error } + } + } catch { + Write-Log " Failed to lookup IP for ${hostname}: $($_.Exception.Message)" -Level "WARNING" + $failCount++ + $failedPCs += @{ Hostname = $hostname; Error = $failure.Error } + } + Write-Host "" + } +} + # Final Summary Write-Host "" Write-Host "=" * 70 -ForegroundColor Cyan diff --git a/scripts/applications.csv b/scripts/applications.csv new file mode 100644 index 0000000..3c60089 --- /dev/null +++ b/scripts/applications.csv @@ -0,0 +1,21 @@ +app_name,app_id,search_patterns,description,enabled +UDC,2,"Universal Data Collection","Universal Data Collection - Primary shopfloor application",1 +CLM,4,"PPDCS","Legacy UDC/PPDCS application",1 +PPDCS,4,"PPDCS","Legacy UDC/PPDCS application (alias)",1 +Oracle,7,"OracleDatabase|Oracle Database|Oracle.*Database","Oracle Database installations",1 +Tanium,30,"^Tanium Client","Tanium Client - Software Deployment and Bitlocker Keys",1 +PC-DMIS,6,"PC-DMIS|PCDMIS","PC-DMIS CMM Measurement Software",1 +OpenText,22,"OpenText|CSF","OpenText CSF Document Management",1 +eDNC,8,"eDNC","Enhanced DNC - CNC File Transfer",1 +FormTracePak,76,"FormTracePak|Formtracepak|Form Trace|FormTrace","FormTracePak Wax Trace Inspection Software",1 +Keyence VR Series,69,"VR-3000|VR-5000|VR-6000|KEYENCE VR","Keyence VR Series 3D Measurement Software",1 +Genspect,70,"Genspect","Genspect Inspection Software",1 +GageCal,71,"GageCal","GageCal Gage Calibration Software (EAS1000)",1 +NI Software,72,"NI-|National Instruments|NI System|NI Measurement|NI LabVIEW|NI Package|NI Certificates|NI Logos|NI Trace|NI mDNS|NI Auth|NI Network|NI Service","National Instruments Software Suite",1 +goCMM,73,"goCMM","goCMM - CMM Companion Software",1 +DODA,74,"Dovetail Digital Analysis|DODA","Dovetail Digital Analysis Software (CMM)",1 +FormStatusMonitor,75,"FormStatusMonitor","FormStatusMonitor - Wax Trace Companion Software",1 +Java,,"Java|JavaRuntimeEnvironment","Java Runtime Environment",0 +Chrome,,"Google Chrome","Google Chrome browser",0 +Office,,"Microsoft Office","Microsoft Office suite",0 +VNC,,"VNC|RealVNC","VNC remote access software",0 \ No newline at end of file