制約とリソースの戦場を支配する:MS Project スケジュール計算エンジンの極限制御
大規模プロジェクトの管理において、MS Projectの「リソース平準化(Resource Leveling)」は、諸刃の剣である。
リソースの過不足を解消しようとエンジンを回した瞬間、緻密に組まれた「制約条件(Constraints)」が悲鳴を上げ、スケジュールは予測不能な形で押し出される。現場のPM(プロジェクトマネージャー)は、制約を優先してリソースの衝突を無視するか、リソースを優先してマイルストーンを破壊するかという、不毛な二択を迫られることになる。
シニアエンジニア、あるいは社内システムを設計するアーキテクトにとって、この問題は「設定の不備」ではなく、「計算エンジンの制御ロジックの欠如」として捉えるべきだ。
本稿では、VBAを用いて「制約条件」と「リソース平準化」の競合を論理的に解体し、ビジネスルールに基づいた自動修正を行う、極めて堅牢な制御アーキテクチャについて解説する。
1. 根本的な問題:計算エンジンの決定論的挙動の欠如
MS Projectの計算エンジンは、タスクの制約条件(Must Start On, As Soon As Possible等)と、リソースの利用可能性を同時に解こうとする。しかし、これらが衝突した際、エンジンの挙動は「制約の硬さ」に依存する。
- Hard Constraint (pjTaskMustStartOn等): エンジンはこの制約を絶対とし、リソースが不足していてもタスクを動かさない。結果、リソースの重複(Over-allocation)が残る。
- Soft Constraint (pjTaskAsSoonAsPossible等): エンジンはリソース平準化の要請に従い、制約を無視してタスクをスライドさせる。
実務における真の課題は、「どの制約を『硬い』ものとし、どの制約を『柔らかい』ものとして再定義するか」という意思決定の自動化にある。これを手動で行うのは、数千タスク規模のプロジェクトでは不可能だ。
2. アーキテクチャ設計:競合解消のアルゴリズム
我々が構築すべきは、単なるループ処理ではない。以下のフェーズを持つ、決定論的な制御プロセスである。
1. スキャンフェーズ: リソース過不足(Over-allocation)が発生しているタスクを特定する。
2. 衝突検知フェーズ: 特定されたタスクが、どの種類の制約(ConstraintType)によってスケジュールがロックされているかを判定する。
3. 意思決定フェーズ: ビジネスロジック(例:重要度、コスト、クリティカルパスの有無)に基づき、「制約の緩和」か「リソースの追加」かを決定する。
4. 実行フェーズ: 制約のタイプを書き換え、計算エンジンを再トリガーする。
3. 実装:極限のメモリ管理とパフォーマンス最適化
大規模なプロジェクトファイル(数千〜数万タスク)を扱う際、VBAエンジニアが陥る最大の罠は、COMオブジェクトのライフサイクル管理の失敗によるメモリリークと、再計算のオーバーヘッドである。
以下のコードは、単なる「動くコード」ではない。メモリ消費を最小限に抑え、大規模環境での実行を想定したプロフェッショナル仕様のフレームワークである。
Option Explicit
' Windows API: プロセスの応答性を維持するためのスリープ(必要に応じて)
' 大規模処理中にUIがフリーズするのを防ぎ、システム的なデッドロックを回避する
If VBA7 Then
Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Else
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
End If
' 定数定義: MS Projectの内部定数(参照設定がない環境への配慮)
Private Const pjTaskMustStartOn As Long = 3
Private Const pjTaskAsSoonAsPossible As Long = 0
Private Const pjTaskStartNoFinish As Long = 4
''' <summary>
''' リソース平準化と制約条件の競合を解決するメインエンジン
''' </summary>
Public Sub ResolveConstraintResourceConflict()
Dim app As MSProject.Application
Set app = MSProject fact.Application
Dim t As MSProject.Task
Dim conflictCount As Long
Dim startTime As Double
On Error GoTo ErrorHandler
' パフォーマンス最適化: 計算モードをマニュアルにし、画面更新を停止
app.Calculation = pjManual
app.ScreenUpdating = False
startTime = Timer
conflictCount = 0
' 1. スキャンフェーズ: 全タスクを走査
' Note: Tasksコレクションの直接ループは、大規模プロジェクトではメモリ負荷が高い。
' 必要に応じて、特定のフィルタリングされたViewからアクセスすることを検討せよ。
For Each t In app.ActiveProject.Tasks
If Not t Is Nothing Then
' リソースの過不足が発生しているかチェック
If t.ResourceOverallocated Then
' 2. 衝突検知フェーズ
' 制約条件が "Must Start On" 等の「硬い制約」であるかを確認
If t.ConstraintType = pjTaskMustStartOn Or t.ConstraintType = pjTaskMustFinishOn Then
' 3. 意思決定フェーズ (ここがビジネスロジックの中核)
' 例: タスクの優先度(Priority)が低い場合は、制約を緩和(Soft)する
If t.Priority < 500 Then ' 500はMS Projectの標準的な閾値
' 4. 実行フェーズ: 制約の書き換え
' 制約を ASAP (As Soon As Possible) に変更することで、
' 次回の平準化実行時にエンジンがタスクを動かせるようにする。
t.ConstraintType = pjTaskAsSoonAsPossible
conflictCount = conflictCount + 1
' メモリ負荷軽減のため、適宜システムに制御を戻す(極めて大規模な場合)
If conflictCount Mod 100 = 0 Then DoEvents
End If
End If
End If
End If
Next t
' 最終的な計算の再実行
app.Calculate
' 状態の復元
app.Calculation = pjAutomatic
app.ScreenUpdating = True
MsgBox "処理完了: " & conflictCount & " 件の制約を緩和しました。" & vbCrLf & _
"実行時間: " & Format(Timer - startTime, "0.00") & " 秒", vbInformation
Exit Sub
ErrorHandler:
' エラーハンドリング: オブジェクトの解放を確実に行う
app.Calculation = pjAutomatic
app.ScreenUpdating = True
MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical
' メモリ解放の儀式
Set t = Nothing
Set app = Nothing
End Sub
4. アーキテクトの視点:レガシー環境とシステム間連携
このソリューションを実務に投入する際、以下の3点に留意せよ。
オブジェクトの明示的解放
VBAにおける `Set t = Nothing` は、単なる作法ではない。MS Projectのような巨大なCOMサーバーを操作する場合、参照カウントが適切に減少しなければ、Excelや他の連携アプリケーションとの間で、プロセスがゾンビ化(メモリ上に残り続ける現象)を引き起こす。特に、ループ内でオブジェクトを生成・破棄する場合は、ライフサイクルを厳格に管理せよ。
決定論的なデバッグ
「なぜこのタスクが動かなかったのか?」という問いに対し、「エンジンの仕様です」という回答はエンジニアとして失格だ。制約条件の変更履歴を、カスタムプロパティ(`t.SetCustomField`)にログとして残す仕組みを組み込め。これにより、後からの監査(Audit)が可能になる。
システム間連携への拡張
このロジックは、MS Project単体で完結させるべきではない。理想的なアーキテクチャでは、リソースのマスターデータはERP(SAP等)にあり、制約の優先順位ルールは外部の意思決定エンジン(Python/C#等)からAPI経由で注入されるべきだ。VBAは、その「実行部隊(Execution Layer)」として、COMインターフェースを介して指示を具現化する役割に徹する。
結論
リソース平準化と制約条件の競合は、プロジェクト管理における「エントロピーの増大」そのものである。これをVBAで制御するということは、単にコードを書くことではなく、プロジェクトの論理構造をコードによって再定義することを意味する。
エンジニアが目指すべきは、ツールを使う者ではなく、計算エンジンの挙動を完全に支配する者である。
