統合スケジュールのエントロピーを制御せよ:サブプロジェクト統合時における制約条件競合の自動解消アルゴリズム
大規模なプロジェクト管理において、マスタープロジェクト(Master Project)へのサブプロジェクトの統合は、避けられない儀式である。しかし、多くの現場ではこの「統合」の瞬間に、スケジュールという名の秩序が崩壊する。
サブプロジェクト側で設定された「Must Start On(開始日固定)」や「Start No Earlier Than(開始前禁止)」といった強力な制約条件(Constraints)が、マスタープロジェクト側の論理的な依存関係(Predecessors)と衝突し、計算エンジンが制御不能な日付の跳ね上がりや、現実離れしたクリティカルパスの生成を引き起こすからだ。
本稿では、単なる「日付の書き換え」ではない。プロジェクトの論理構造を維持しつつ、制約条件の不一致を決定論的に解消するための、極限まで最適化されたVBAアーキテクチャを解説する。
1. スケジュールエンジンの力学:なぜ制約条件は「毒」となるのか
MS Projectの計算エンジンは、依存関係(Logic)と制約条件(Constraint)の二重構造で動いている。
- 論理的依存関係: 「タスクAが終わったらタスクBを始める」という、プロジェクトの血流。
- 制約条件: 「タスクBは必ず〇月〇日に開始しなければならない」という、血流を止める栓。
サブプロジェクトを統合する際、サブプロジェクト内のタスクが「特定の絶対日付」に縛られていると、マスター側のリソース制約や先行タスクの遅延を無視してその日付に固執しようとする。これが制約条件の競合(Constraint Collision)である。
エンジニアが取るべき戦略は、統合の瞬間にこれらの「硬い制約」を、マスターの論理に従う「柔らかい制約(ASAP/ALAP)」へと動的に変換(Sanitize)することにある。
2. アーキテクチャ設計:3フェーズ・アプローチ
無秩序なループ処理は、大規模プロジェクトにおいてメモリ消費と計算時間の増大を招き、最悪の場合、アプリケーションのハングアップを引き起こす。我々は以下の3フェーズで処理を構成する。
1. Freeze Phase (静止フェーズ): 自動計算を停止し、エンジンの計算負荷をゼロにする。
2. Sanitization Phase (浄化フェーズ): 全タスクを走査し、不適切な制約条件を論理的な制約へ変換する。
3. Recalculation Phase (再計算フェーズ): 全ての変更を確定させ、一括でスケジュールを再構築する。
3. 実装:Constraint Sanitizer Engine
以下に、シニアエンジニアが実務でそのまま利用できるレベルの、堅牢な実装コードを示す。ここでは、オブジェクトの明示的な解放と、計算エンジンの制御に重点を置いている。
Option Explicit
' Windows API: 画面更新の制御(フリッカー防止とパフォーマンス向上)
If VBA7 Then
Declare PtrSafe Function LockWindowUpdate Lib "user32" (ByVal hwndLock As LongPtr) As Long
Else
Declare Function LockWindowUpdate Lib "user32" (ByVal hwndLock As Long) As Long
End If
''' <summary>
''' マスタープロジェクトへ統合されたサブプロジェクトの制約条件を、
''' 論理的な整合性を保ちつつ自動解消する。
''' </summary>
Public Sub SanitizeSubProjectConstraints()
Dim proj As Project
Dim subProj As Project
Dim t As Task
Dim startTime As Double
Set proj = ActiveProject
startTime = Timer
On Error GoTo ErrorHandler
' --- Phase 1: Freeze Phase ---
' 自動計算を停止。これが計算コストを抑える最大の鍵である。
proj.Calculation = pjManual
' LockWindowUpdateを使用して、UIの描画コストを完全に遮断する
LockWindowUpdate 0
Debug.Print "--- Sanitization Process Started ---"
' サブプロジェクトの列挙
' 注: プロジェクト構造によっては、Subprojectsコレクションを直接走査する
Dim sp As Project
For Each sp In proj.Subprojects
Debug.Print "Processing Subproject: " & sp.Name
' --- Phase 2: Sanitization Phase ---
' 各サブプロジェクト内の全タスクを走査
For Each t In sp.Tasks
' オブジェクトの存在確認(空の行対策)
If Not t Is Nothing Then
Call ApplySoftConstraint(t)
End If
Next t
Next sp
' --- Phase 3: Recalculation Phase ---
' 全ての変更を適用した後、一度だけ再計算を実行する
Debug.Print "Recalculating schedule..."
proj.Calculate
' UIのロック解除
LockWindowUpdate 0
Debug.Print "--- Process Completed in " & Format(Timer - startTime, "0.00") & "s ---"
MsgBox "制約条件の浄化が完了しました。", vbInformation
ExitProcedure:
' メモリ管理:オブジェクトの明示的な解放
' 大規模なプロジェクトでは、これがメモリリークを防ぐ唯一の手段である
Set t = Nothing
Set sp = Nothing
Set proj = Nothing
Exit Sub
ErrorHandler:
LockWindowUpdate 0
MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical
Resume ExitProcedure
End Sub
''' <summary>
''' タスクの制約条件を評価し、論理的な制約(ASAP/ALAP)へ変換する。
''' </summary>
''' <param name="targetTask">対象となるTaskオブジェクト</param>
Private Sub ApplySoftConstraint(ByRef targetTask As Task)
' 強力な制約(Must Start On, Must Finish On 等)を検知
' これらは、マスターのスケジュールの流れを破壊する要因となる
Select Case targetTask.ConstraintType
Case pjMustStartOn, pjMustFinishOn, pjStartNoLaterThan, pjFinishNoLaterThan
' ログ出力(デバッグ用)
' Debug.Print "Converting hard constraint for: " & targetTask.Name
' 制約を ASAP (As Soon As Possible) に緩和する
' これにより、依存関係に基づいた動的なスケジュール移動が可能になる
targetTask.ConstraintType = pjAsSoonAsPossible
Case pjStartNoEarlierThan, pjFinishNoEarlierThan
' これらは比較的「柔らかい」が、念のため ASAP に統一することで
' 予測可能性を高める設計判断も現場では存在する
' targetTask.ConstraintType = pjAsSoonAsPossible
Case Else
' すでに ASAP や ALAP、あるいは依存関係のみの場合は何もしない
End Select
End Sub
4. プロフェッショナルのための最適化知見
このスクリプトを実務環境、特に数千タスクを超える大規模エンタープライズ・プロジェクトに投入する際、以下の3点に留意せよ。
I. オブジェクト・ライフサイクルの厳格な管理
VBAのCOMオブジェクト(`Task`, `Project`)は、参照カウントが正しく管理されない限り、メモリ上に残り続ける。大規模プロジェクトでは、`Set t = Nothing` をループの最後、あるいはプロシージャの終了時に明示的に行うことが、システムの安定性を決定づける。
II. `Calculation` モードの戦略的制御
`proj.Calculate` をループ内で呼び出すことは、エンジニアとしての「死」を意味する。計算エンジンは、1つの日付変更に対してグラフ全体の再計算を試みるため、計算量は $O(N^2)$ に近い挙動を示す。必ず `pjManual` で固定し、最後に一度だけ `Calculate` を叩く、これが鉄則である。
III. Windows APIによる描画制御
`LockWindowUpdate` を使用している理由は、単なる見た目の問題ではない。MS ProjectのGUIは、タスクのプロパティ変更に伴ってガントチャートを再描画しようとする。この描画プロセス(GDIリソースの消費)をAPIレベルで抑制することで、実行速度を劇的に向上させることができる。
結論
マスタープロジェクトの統合は、単なるデータの結合ではない。それは、複数の異なる時間軸と制約条件を持つ「宇宙」を、一つの論理的な「秩序」へと再編する高度なエンジニアリング作業である。
制約条件という名の「不確定要素」を、コードによって決定論的な「論理」へと変換すること。それこそが、プロジェクト管理システムを構築するアーキテクトに課せられた責務である。
