Files
FT546MWS/scripts/mixed-pilot/control-mixed-pilot.ps1

102 lines
8.6 KiB
PowerShell

param(
[Parameter(Mandatory=$true)][string]$Server,
[Parameter(Mandatory=$true)][string]$Database,
[ValidateSet('status','enable','disable','uninstall')][string]$Mode='status',
[switch]$ConfirmPhysicalFit,
[switch]$AllProjectsMayMix,
[switch]$TestDatabase
)
$ErrorActionPreference='Stop'
[Console]::OutputEncoding=[Text.UTF8Encoding]::new($false)
if($TestDatabase -and $Database -notmatch '^FT546WMS_MixedPilot_Test_[a-f0-9]{32}$'){throw 'Invalid test database name'}
if(-not $TestDatabase -and $Database -ine 'FT546-wms'){throw 'Only FT546-wms is supported'}
$builder=[Data.SqlClient.SqlConnectionStringBuilder]::new()
$builder['Data Source']=$Server;$builder['Initial Catalog']=$Database;$builder['Integrated Security']=$true
$builder['Connect Timeout']=8;$builder['Application Name']='FT546MixedPilotControl'
$cn=[Data.SqlClient.SqlConnection]::new($builder.ConnectionString);$cn.Open();$tx=$null
function Cmd([string]$sql){$c=$cn.CreateCommand();$c.CommandText=$sql;$c.CommandTimeout=15;if($tx){$c.Transaction=$tx};$c}
function Exec([string]$sql){$c=Cmd $sql;try{[void]$c.ExecuteNonQuery()}finally{$c.Dispose()}}
function Scalar([string]$sql){$c=Cmd $sql;try{$c.ExecuteScalar()}finally{$c.Dispose()}}
function Query([string]$sql){
$c=Cmd $sql;$t=[Data.DataTable]::new()
try{$r=$c.ExecuteReader();try{$t.Load($r)}finally{$r.Dispose()}}finally{$c.Dispose()}
foreach($row in $t.Rows){$o=[ordered]@{};foreach($col in $t.Columns){$v=$row[$col.ColumnName];$o[$col.ColumnName]=if($v -is [DBNull]){$null}else{$v}};[pscustomobject]$o}
}
try {
Exec 'SET NOCOUNT ON; SET LOCK_TIMEOUT 2000; SET DEADLOCK_PRIORITY LOW;'
if((Scalar "SELECT OBJECT_ID('mixed_pilot.control','U')") -is [DBNull]){
if($Mode -eq 'status'){Write-Output '{"installed":false}';return};throw 'Pilot is not installed'
}
if($Mode -eq 'status'){
$report=[ordered]@{
utc=[DateTime]::UtcNow.ToString('o')
control=@(Query 'SELECT * FROM mixed_pilot.control')
slots=@(Query 'SELECT l.*,s.cargo_space_id,s.group_coding,s.the_floor,s.status,s.locked,s.tray_code FROM mixed_pilot.lane l LEFT JOIN dbo.bas_cargo_space s ON s.warehouse_id=l.warehouse_id AND s.group_coding_no=l.lane_code ORDER BY l.lane_code,s.group_coding')
admissions=@(Query 'SELECT TOP(100) * FROM mixed_pilot.admission ORDER BY created_at DESC')
waiting=@(Query "SELECT j.job_id,j.tray_code,j.cargo_space_id,j.status,h.created_at FROM mixed_pilot.dispatch_hold h JOIN dbo.mydo_job j ON j.job_id=h.job_id WHERE j.status='8'")
moves=@(Query 'SELECT * FROM mixed_pilot.move_audit WHERE completed_at IS NULL')
}
$report|ConvertTo-Json -Depth 6;return
}
$tx=$cn.BeginTransaction()
Exec "DECLARE @r int; EXEC @r=sys.sp_getapplock @Resource='FT546.mixed_pilot.inbound',@LockMode='Exclusive',@LockOwner='Transaction',@LockTimeout=1000; IF @r<0 THROW 51142,'An inbound allocation is active; retry later',1;"
if($Mode -eq 'disable'){
Exec 'UPDATE mixed_pilot.control SET enabled=0 WHERE id=1; UPDATE mixed_pilot.lane SET enabled=0;'
$tx.Commit();$tx.Dispose();$tx=$null;Write-Output 'DISABLED: no new mixed admissions; existing mixed-stock dispatch protection retained';return
}
if((Scalar 'SELECT installed FROM mixed_pilot.control WHERE id=1') -ne 1){throw 'Pilot hooks are already uninstalled'}
Exec "IF EXISTS(SELECT 1 FROM mixed_pilot.module_backup WHERE installed_hash<>HASHBYTES('SHA2_256',OBJECT_DEFINITION(OBJECT_ID(object_name))) OR OBJECT_DEFINITION(OBJECT_ID(object_name)) IS NULL) OR EXISTS(SELECT 1 FROM mixed_pilot.installed_module WHERE definition_hash<>HASHBYTES('SHA2_256',OBJECT_DEFINITION(OBJECT_ID(object_name))) OR OBJECT_DEFINITION(OBJECT_ID(object_name)) IS NULL) THROW 51143,'Module drift detected; no changes made',1;"
if($Mode -eq 'enable'){
if(-not $ConfirmPhysicalFit -or -not $AllProjectsMayMix){throw 'Enable requires -ConfirmPhysicalFit and -AllProjectsMayMix; do not infer these from empty SQL height fields'}
Exec @'
IF (SELECT COUNT(*) FROM mixed_pilot.lane)<>3 OR EXISTS(SELECT 1 FROM mixed_pilot.lane WHERE warehouse_id<>'01' OR lane_code NOT IN('C1203','C0204','C0205'))
THROW 51144,'Pilot whitelist differs from the reviewed three lanes',1;
DECLARE @locked int;
SELECT @locked=COUNT(*) FROM dbo.bas_cargo_space s WITH(UPDLOCK,HOLDLOCK)
JOIN mixed_pilot.lane l ON l.warehouse_id=s.warehouse_id AND l.lane_code=s.group_coding_no;
IF @locked<>6 OR EXISTS(SELECT 1 FROM mixed_pilot.lane l WHERE
(SELECT COUNT(*) FROM dbo.bas_cargo_space s WHERE s.warehouse_id=l.warehouse_id AND s.group_coding_no=l.lane_code AND TRY_CONVERT(int,s.group_coding)=1 AND s.this_cargo_area='C' AND s.status='2' AND s.locked='N' AND NULLIF(s.tray_code,'') IS NOT NULL)<>1
OR (SELECT COUNT(*) FROM dbo.bas_cargo_space s WHERE s.warehouse_id=l.warehouse_id AND s.group_coding_no=l.lane_code AND TRY_CONVERT(int,s.group_coding)=2 AND s.this_cargo_area='C' AND s.status='1' AND s.locked='N' AND NULLIF(s.tray_code,'') IS NULL)<>1)
THROW 51145,'A pilot lane no longer has an occupied rear and empty unlocked front; refresh candidate review',1;
IF EXISTS(SELECT 1 FROM dbo.mydo_job j WITH(UPDLOCK,HOLDLOCK) JOIN dbo.bas_cargo_space s ON s.cargo_space_id=j.cargo_space_id OR s.cargo_space_id=j.target_cargo_space_id OR s.tray_code=j.tray_code
JOIN mixed_pilot.lane l ON l.warehouse_id=s.warehouse_id AND l.lane_code=s.group_coding_no)
THROW 51146,'Pilot lane has an active task; no switches changed',1;
IF EXISTS(SELECT 1 FROM dbo.bas_cargo_space s JOIN mixed_pilot.lane l ON l.warehouse_id=s.warehouse_id AND l.lane_code=s.group_coding_no
WHERE (TRY_CONVERT(int,s.group_coding)=2 AND EXISTS(SELECT 1 FROM dbo.bas_stock x WHERE x.cargo_space_id=s.cargo_space_id))
OR (TRY_CONVERT(int,s.group_coding)=1 AND (
(SELECT COUNT(DISTINCT spare4) FROM dbo.bas_stock x WHERE x.cargo_space_id=s.cargo_space_id)<>1
OR EXISTS(SELECT 1 FROM dbo.bas_stock x WHERE x.cargo_space_id=s.cargo_space_id AND (NULLIF(x.spare4,'') IS NULL OR x.spare4='999' OR x.tray_code<>s.tray_code OR x.tray_code IS NULL OR ISNULL(x.out_quantity,0)<>0)))))
THROW 51147,'Pilot inventory/project consistency check failed',1;
UPDATE mixed_pilot.control SET enabled=1,all_projects_may_mix=1 WHERE id=1;
UPDATE mixed_pilot.lane SET enabled=1,physical_fit_confirmed=1;
'@
$tx.Commit();$tx.Dispose();$tx=$null;Write-Output 'ENABLED: C1203/120703, C0204/020704, C0205/020705; at most three simultaneous guest pallets';return
}
# Full uninstall requires all trial stock/tasks to be drained, including relocated trial pallets.
Exec @'
IF EXISTS(SELECT 1 FROM mixed_pilot.control WHERE enabled=1)
THROW 51148,'Disable admission before uninstalling',1;
IF EXISTS(SELECT 1 FROM dbo.bas_cargo_space s JOIN mixed_pilot.lane l ON l.warehouse_id=s.warehouse_id AND l.lane_code=s.group_coding_no
WHERE s.status<>'1' OR NULLIF(s.tray_code,'') IS NOT NULL
OR EXISTS(SELECT 1 FROM dbo.bas_stock x WHERE x.cargo_space_id=s.cargo_space_id)
OR EXISTS(SELECT 1 FROM dbo.mydo_job j WHERE j.cargo_space_id=s.cargo_space_id OR j.target_cargo_space_id=s.cargo_space_id))
OR EXISTS(SELECT 1 FROM mixed_pilot.move_audit a JOIN dbo.bas_stock s ON s.tray_code=a.tray_code)
OR EXISTS(SELECT 1 FROM mixed_pilot.move_audit a JOIN dbo.mydo_job j ON j.job_id=a.job_id)
OR EXISTS(SELECT 1 FROM mixed_pilot.dispatch_hold h JOIN dbo.mydo_job j ON j.job_id=h.job_id AND j.status='8')
THROW 51149,'Trial lanes or relocated trial pallets are not drained; keep protection installed',1;
'@
$backups=@(Query 'SELECT * FROM mixed_pilot.module_backup')
foreach($b in $backups){
Exec (('SET ANSI_NULLS '+$(if($b.ansi_nulls){'ON'}else{'OFF'}))+'; SET QUOTED_IDENTIFIER '+$(if($b.quoted_identifier){'ON'}else{'OFF'})+';')
$original=[regex]::Replace($b.original_definition,'(?i)\bCREATE\s+PROCEDURE','ALTER PROCEDURE')
Exec $original
}
Exec 'DROP TRIGGER dbo.tr_mixed_pilot_job_guard; DROP TRIGGER dbo.tr_mixed_pilot_slot_release; DROP TRIGGER dbo.tr_mixed_pilot_stock_release;'
foreach($name in @('allot_space_with_fallback','original_allot_space','original_yk_job_add','original_yk_complete','check_inbound','try_fallback','check_move','release_ready_jobs')){Exec "DROP PROCEDURE mixed_pilot.[$name]"}
Exec 'DROP VIEW mixed_pilot.blocked_jobs; UPDATE mixed_pilot.control SET installed=0,enabled=0 WHERE id=1;'
$tx.Commit();$tx.Dispose();$tx=$null
Write-Output 'UNINSTALLED: original procedure behavior restored; audit and original definitions retained in mixed_pilot tables'
} catch {
if($tx){try{$tx.Rollback()}catch{};$tx.Dispose();$tx=$null};throw
} finally {$cn.Dispose()}