Part marker: a misconfigured bay is now loud, and the listing loop is bounded
Every failure of Get-TelesisPortConfig was filed as "no Telesis marker configured on this PC" and exited 0. That is correct for the many PCs which are not marker bays, and wrong for a bay whose registry key EXISTS and could not be read: a half-configured marker was silently never backed up again, which is exactly the failure shape this feature was built to prevent. The absent-key case now throws a distinguishable exception and stays quiet; anything else is logged every cycle and counted as a skip. Get-TelesisFileList was unbounded. The receiver drives that loop, so a controller repeating a name or answering noise on a marginal cable would spin for ever - and not harmlessly, because it holds the MARKING port while GE-Enforce waits on the script with no timeout, so one chatty controller wedges the enforcement cycle and the operator cannot mark a part. Now capped by count, by elapsed time, and by repeat detection, all of which THROW: a partial listing presented as a complete snapshot would be worse than failing. Baud and Data Bits parse with TryParse. Note the narrower reason than it looks: [int]'' is 0 in PowerShell, so empty and non-numeric values were already handled by the zero-check. What was not handled is a numeric string too large for Int32, which throws on the cast and would have been filed as "not a marker bay". Verified in the win11 VM as SYSTEM, against both the old and new module: absent key stays quiet, a key without Port Id is loud, garbage and out-of-range baud both fall back to 9600, a real 19200 is still honoured, a repeating controller stops at once and endless unique names stop at the cap.
This commit is contained in:
@@ -79,8 +79,14 @@ function Get-TelesisPortConfig {
|
|||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param()
|
param()
|
||||||
|
|
||||||
|
# A distinguishable exception, not a bare string. The caller must be able to
|
||||||
|
# tell "this PC is not a marker bay" (quiet, correct, permanent) from "this
|
||||||
|
# marker bay is misconfigured" (loud, and someone needs to fix it). Filing
|
||||||
|
# both as the former is how a broken bay stops being backed up and nothing
|
||||||
|
# anywhere says so.
|
||||||
if (-not (Test-Path $script:MARKKEY)) {
|
if (-not (Test-Path $script:MARKKEY)) {
|
||||||
throw "No marker configuration at $script:MARKKEY - this PC does not drive a Telesis marker."
|
throw [System.Management.Automation.ItemNotFoundException]::new(
|
||||||
|
"No marker configuration at $script:MARKKEY - this PC does not drive a Telesis marker.")
|
||||||
}
|
}
|
||||||
$props = Get-ItemProperty -Path $script:MARKKEY -ErrorAction Stop
|
$props = Get-ItemProperty -Path $script:MARKKEY -ErrorAction Stop
|
||||||
|
|
||||||
@@ -106,10 +112,27 @@ function Get-TelesisPortConfig {
|
|||||||
# Defaults match the fleet's own configuration (9600-8-N-1 in WJPRT.reg), so
|
# Defaults match the fleet's own configuration (9600-8-N-1 in WJPRT.reg), so
|
||||||
# a key missing one value still produces a usable port rather than a zero
|
# a key missing one value still produces a usable port rather than a zero
|
||||||
# baud rate that fails at Open with an unhelpful message.
|
# baud rate that fails at Open with an unhelpful message.
|
||||||
$baud = [int]((Read-MarkValue 'Baud' '9600') -replace '\D', '')
|
# TryParse, not a cast, for the OUT-OF-RANGE case.
|
||||||
if (-not $baud) { $baud = 9600 }
|
#
|
||||||
$databits = [int]((Read-MarkValue 'Data Bits' '8') -replace '\D', '')
|
# An empty or non-numeric value was already handled: PowerShell coerces
|
||||||
if (-not $databits) { $databits = 8 }
|
# [int]'' to 0 (verified in the VM), so the -replace leaves '' and the
|
||||||
|
# zero-check below substitutes the default. What is NOT handled is a numeric
|
||||||
|
# string too large for Int32 - '99999999999999' throws on the cast, and the
|
||||||
|
# caller files that as "this PC is not a marker bay", so a real bay would
|
||||||
|
# stop being backed up with nothing saying why.
|
||||||
|
#
|
||||||
|
# Narrow, but the whole guard is here for garbage in a registry value, and
|
||||||
|
# leaving one shape of garbage fatal defeats the point.
|
||||||
|
[int]$baud = 0
|
||||||
|
if (-not [int]::TryParse(((Read-MarkValue 'Baud' '9600') -replace '\D', ''),
|
||||||
|
[ref]$baud) -or $baud -le 0) {
|
||||||
|
$baud = 9600
|
||||||
|
}
|
||||||
|
[int]$databits = 0
|
||||||
|
if (-not [int]::TryParse(((Read-MarkValue 'Data Bits' '8') -replace '\D', ''),
|
||||||
|
[ref]$databits) -or $databits -le 0) {
|
||||||
|
$databits = 8
|
||||||
|
}
|
||||||
|
|
||||||
[pscustomobject]@{
|
[pscustomobject]@{
|
||||||
PortName = $portid
|
PortName = $portid
|
||||||
@@ -350,12 +373,45 @@ function Get-TelesisFileList {
|
|||||||
'Fonts' { @{ Type = '4'; Start = 'R'; Step = ''; Extension = '.TFF' } }
|
'Fonts' { @{ Type = '4'; Start = 'R'; Step = ''; Extension = '.TFF' } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# BOUNDED, three ways. The receiver drives this loop, so a controller that
|
||||||
|
# keeps answering - repeating a name, or answering noise on a marginal cable
|
||||||
|
# - would spin here for ever. That is not a hang in a background job: this
|
||||||
|
# holds the MARKING port, and GE-Enforce waits on the script with no
|
||||||
|
# timeout, so one chatty controller wedges the whole enforcement cycle and
|
||||||
|
# the operator cannot mark a part.
|
||||||
|
#
|
||||||
|
# A truncated listing is a bad outcome too, so both limits are far above any
|
||||||
|
# real controller (a full bay carries tens of patterns, and a listing takes
|
||||||
|
# well under a second per name) and both THROW rather than returning a short
|
||||||
|
# list quietly. A partial snapshot presented as complete is the failure this
|
||||||
|
# whole feature exists to avoid.
|
||||||
|
$MAXNAMES = 2000
|
||||||
|
$BUDGETSECONDS = 90
|
||||||
|
|
||||||
$names = New-Object System.Collections.Generic.List[string]
|
$names = New-Object System.Collections.Generic.List[string]
|
||||||
|
$seen = New-Object System.Collections.Generic.HashSet[string]
|
||||||
|
$clock = [System.Diagnostics.Stopwatch]::StartNew()
|
||||||
|
|
||||||
$name = Invoke-TelesisCommand -Port $Port -Type $spec.Type -Data $spec.Start
|
$name = Invoke-TelesisCommand -Port $Port -Type $spec.Type -Data $spec.Start
|
||||||
while ($name) {
|
while ($name) {
|
||||||
|
if (-not $seen.Add($name)) {
|
||||||
|
throw ("$Class listing repeated '$name' after $($names.Count) entries - " +
|
||||||
|
'the controller is looping rather than ending the list.')
|
||||||
|
}
|
||||||
$names.Add($name)
|
$names.Add($name)
|
||||||
|
|
||||||
|
if ($names.Count -ge $MAXNAMES) {
|
||||||
|
throw ("$Class listing exceeded $MAXNAMES entries - refusing to keep " +
|
||||||
|
'the marking port while the controller talks indefinitely.')
|
||||||
|
}
|
||||||
|
if ($clock.Elapsed.TotalSeconds -ge $BUDGETSECONDS) {
|
||||||
|
throw ("$Class listing exceeded $BUDGETSECONDS seconds after " +
|
||||||
|
"$($names.Count) entries - releasing the marking port.")
|
||||||
|
}
|
||||||
|
|
||||||
$name = Invoke-TelesisCommand -Port $Port -Type $spec.Type -Data $spec.Step
|
$name = Invoke-TelesisCommand -Port $Port -Type $spec.Type -Data $spec.Step
|
||||||
}
|
}
|
||||||
|
$clock.Stop()
|
||||||
return [pscustomobject]@{
|
return [pscustomobject]@{
|
||||||
Class = $Class
|
Class = $Class
|
||||||
Extension = $spec.Extension
|
Extension = $spec.Extension
|
||||||
|
|||||||
@@ -328,11 +328,23 @@ if ($busy) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$portconfig = Get-TelesisPortConfig
|
$portconfig = Get-TelesisPortConfig
|
||||||
} catch {
|
} catch [System.Management.Automation.ItemNotFoundException] {
|
||||||
# Every PC type that is not a part-marker bay lands here, permanently.
|
# The registry key is absent: this PC is not a part-marker bay. Quiet and
|
||||||
|
# permanent, which is correct - most of the fleet lands here every cycle.
|
||||||
Write-ShopdbQuietState -Kind $KIND -State 'no-marker-config' -Message (
|
Write-ShopdbQuietState -Kind $KIND -State 'no-marker-config' -Message (
|
||||||
"No Telesis marker configured on this PC ($_). Nothing to do.")
|
"No Telesis marker configured on this PC ($_). Nothing to do.")
|
||||||
exit 0
|
exit 0
|
||||||
|
} catch {
|
||||||
|
# The key EXISTS and reading it failed: a marker bay that is misconfigured,
|
||||||
|
# or a registry permission problem. Filing this as "not a marker bay" - as
|
||||||
|
# this used to - meant a half-configured bay was never backed up again and
|
||||||
|
# nothing anywhere said so. It is the silent-failure shape this whole
|
||||||
|
# feature exists to avoid, so it is logged every cycle and counted as a
|
||||||
|
# skip, not swallowed.
|
||||||
|
$message = "Marker configuration present but unreadable: $_"
|
||||||
|
Write-ShopdbBackupLog -Kind $KIND -Message $message
|
||||||
|
Add-ShopdbBackupSkip -Kind $KIND -Reason 'marker-config-unreadable'
|
||||||
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# -MachineNumberFallback, not a shared default: a marker bay legitimately has
|
# -MachineNumberFallback, not a shared default: a marker bay legitimately has
|
||||||
|
|||||||
Reference in New Issue
Block a user