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()}