【実務・中級編】リソースの稼働率を考慮したタスクの自動スケジューリング – Project VBA解析バイブル

スポンサーリンク

リソースの悲鳴を聞け:Project VBAによる「真に実用的な」稼働率考慮型自動スケジューリングの極意

開発現場のリーダーであるあなたなら、よく知っているはずだ。
ガントチャート上でタスクを綺麗に並べ替えるだけのマクロなら、ネットの海を漁ればいくらでもある。だが、それらのコードを実際のプロジェクトに投入した瞬間、何が起きるか?

「リソースのダブルブッキング(過負荷)」だ。

人間は同時に2つのタスクを完遂することはできない。特定のエンジニアに負荷が集中し、プロジェクトは静かに、そして確実に破綻へと向かう。表面上のスケジュールがどれほど美しくとも、リソースの稼働率(キャパシティ)を無視した自動化は、単なる「机上の空論製造機」に過ぎない。

今回は、Project VBAのオブジェクトモデルの深淵に踏込み、「リソースの空き状況をリアルタイムで検知し、余剰キャパシティの隙間にタスクを自動配置する堅牢なスケジューリングエンジン」の設計思想とプロダクションコードを伝授する。

—

1. なぜ「単純なタスク移動」は実務で使い物にならないのか?

多くの開発者が犯す実装上のミスは、`Task.Start` や `Task.Finish` を単純にループで書き換えることだ。
これをやると何が起きるか。

1. カレンダー・稼働時間の無視: 土日や夜間、カスタムカレンダーの例外(休暇など)を考慮せず日付をいじると、Projectのエンジン内部で日付の整合性が崩れ、強制的な再計算(平準化)によってスケジュールがグチャグチャになる。
2. 過負荷(Overallocated)の検知漏れ: 「誰がそのタスクをやっているのか(Resource Assignment)」のコンテキスト落ち。リソースの割当単位(Units)を見ずにタスクを詰め込むと、一人の人間に1日16時間分の労働が割り当てられる。

プロフェッショナルなアプローチ

真に自動化すべきは、「タスクの日付を無理やり動かすこと」ではない。
「指定されたリソースの稼働状況(Remaining Work / Work)をカレンダーのタイムライン上で走査し、最初に見つかった空きスロット(Free Slot)にタスクのアンカーを打ち直すこと」である。

—

2. 堅牢なスケジューリングエンジンのアーキテクチャ

今回構築するエンジンは、以下の3つのレイヤーで構成する。

  • リソース・キャパシティ解析層: 指定リソースのカレンダーに基づき、指定期間内の稼働可能時間(単位時間あたりの残余容量)を算出する。
  • 空きスロット探索層: タイムラインを細切れ(例えば1時間単位、あるいは日単位)で走査し、リソースの負荷が100%未満の区間を特定する。
  • タスク配置・コミット層: 見つかった空きスロットにタスクの開始日・終了日をバインドし、Projectのスケジュールエンジンを破綻させずに書き込む。

—

3. 【プロダクションコード】稼働率考慮型自動スケジューリングモジュール

以下のコードは、エラーハンドリング、Projectのオブジェクトライフサイクル、そしてパフォーマンスへの配慮を組み込んだ実戦投入可能なVBAコードである。

Option Explicit

‘ ==============================================================================
‘ módulo: clsResourceScheduler
‘ 概要: リソースの稼働率を考慮し、未割当タスクを空きスロットに自動配置するエンジン
‘ ==============================================================================

Public Sub AutoScheduleTasksByResourceCapacity()
‘ パフォーマンス最適化のため描画と言語処理を停止
Application.ScreenUpdating = False

On Error GoTo ErrorHandler

Dim t As Task
Dim targetResource As Resource
Dim assignedResourceName As String

‘ サンプルとして「主要リソース: 開発A」をターゲットにする
assignedResourceName = “開発A”
Set targetResource = GetResourceByName(assignedResourceName)

If targetResource Is Nothing Then
MsgBox “指定されたリソースが見つかりません: ” & assignedResourceName, vbCritical
GoTo Cleanup
End If

‘ アクティブプロジェクト内の全タスクを走査
For Each t In ActiveProject.Tasks
‘ タスクが有効かつ、未完了、かつ該当リソースが割り当てられている場合を対象とする
If Not t Is Nothing Then
If t.Summary = False And t.PercentComplete < 100 Then If IsTaskAssignedToResource(t, targetResource) Then ' すでにスケジュール済みの場合はスキップ(必要に応じて調整) If t.Start > ActiveProject.CurrentDate Then

‘ リソースの空きスロットを計算し、開始日を再設定
Dim optimalStart As Date
optimalStart = FindNextAvailableSlot(targetResource, t.Duration / 60) ‘ Durationは分単位なので時間に変換

If optimalStart > 0 Then
t.Start = optimalStart
‘ 終了日はProjectのエンジンが自動計算するため直接触らない(整合性維持のため)
Debug.Print “タスク [” & t.Name & “] を ” & optimalStart & ” に再配置しました。”
End If

End If

End If
End If
End If
Next t

MsgBox “自動スケジューリングが正常に完了しました。”, vbInformation

Cleanup:
Application.ScreenUpdating = True
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical
Resume Cleanup
End Sub

‘ ——————————————————————————
‘ 指定名からResourceオブジェクトを取得するヘルパー
‘ ——————————————————————————
Private Function GetResourceByName(resName As String) As Resource
Dim r As Resource
For Each r In ActiveProject.Resources
If Not r Is Nothing Then
If r.Name = resName Then
Set GetResourceByName = r
Exit Function
End If
End If
Next r
Set GetResourceByName = Nothing
End Function

‘ ——————————————————————————
‘ タスクに指定リソースが割り当てられているか判定
‘ ——————————————————————————
Private Function IsTaskAssignedToResource(t As Task, r As Resource) As Boolean
Dim assn As Assignment
For Each assn In t.Assignments
If Not assn.Resource Is Nothing Then
If assn.Resource.ID = r.ID Then
IsTaskAssignedToResource = True
Exit Function
End If
End If
Next assn
IsTaskAssignedToResource = False
End Function

‘ ——————————————————————————
‘ リソースの稼働状況を走査し、必要時間を確保できる最初のスロットを返す
‘ ※実務向け簡易ロジック:日単位での残余容量をチェック
‘ ——————————————————————————
Private Function FindNextAvailableSlot(r As Resource, requiredHours As Double) As Date
Dim checkDate As Date
checkDate = ActiveProject.CurrentDate

‘ 最大90日先まで空きスロットを探す
Dim i As Long
For i = 0 To 90
‘ 土日を除外(プロジェクトカレンダーに従うのがベストだが簡略化のため標準ロジック)
If Weekday(checkDate, vbMonday) <= 5 Then ' 該当日のリソースの残余仕事量(Remaining Work)を確認 ' 簡易的に、1日の標準稼働時間を8時間とし、その日の割り当て済み時間を計算 Dim allocatedHoursOnDay As Double allocatedHoursOnDay = GetResourceWorkOnDate(r, checkDate) ' 8時間 - 割り当て済み時間 = 空き時間 とみなす If (8 - allocatedHoursOnDay) >= requiredHours Then
FindNextAvailableSlot = checkDate
Exit Function
End If
End If

checkDate = DateAdd(“d”, 1, checkDate)
Next i

‘ 見つからない場合は現在値を返す
FindNextAvailableSlot = ActiveProject.CurrentDate
End Function

‘ ——————————————————————————
‘ 指定日のリソースの負荷(Work)を集計する
‘ ——————————————————————————
Private Function GetResourceWorkOnDate(r As Resource, targetDate As Date) As Double
Dim assn As Assignment
Dim totalWorkMinutes As Double
totalWorkMinutes = 0

For Each assn In r.Assignments
If Not assn.Task Is Nothing Then
‘ タスクの期間内にtargetDateが含まれているか簡易判定
If targetDate >= assn.Task.Start And targetDate <= assn.Task.Finish Then ' 割り当てられた作業時間を日数で割るなどの概算 If assn.Task.Duration > 0 Then
totalWorkMinutes = totalWorkMinutes + (assn.Work / (assn.Task.Finish – assn.Task.Start + 1))
End If
End If
End If
Next assn

‘ 分を時間に変換
GetResourceWorkOnDate = (totalWorkMinutes / 60) / 1000 ‘ Project内部のWork単位(1000分=1分等の補正を考慮。環境により調整)
End Function

—

4. 現場で絶対に踏んではいけない「地雷」と対策

このコードを実務の巨大なMppファイル(数百タスク以上)に適用する際、以下の設計上の罠に注意してほしい。

罠1: `ScreenUpdating = False` の怠慢によるパフォーマンス崩壊

Project VBAは、オブジェクトのプロパティ(`Task.Start`など)を書き換えるたびに、背後で全体カレンダーの再計算とクリティカルパスの再検証走査を行おうとする。これを無防備に行うと、ループ1回あたり数秒かかり、数千タスクあるプロジェクトでは数時間のフリーズを引き起こす。

  • 対策: 必ず処理の冒頭で `Application.ScreenUpdating = False` をかけ、処理完了後に `True` に戻すこと。さらに、可能であれば `Calculation` モードを手動(Manual)に切り替えて一括処理し、最後に再計算(`Calculate`)を叩くのがプロの技だ。

罠2: 終了日(Finish)の強制的上書き

初心者は「開始日と終了日を両方セットしよう」とする。しかし、MS Projectは作業時間(Duration)とカレンダーから終了日を自動計算するエンジンを持っている。終了日をVBAで無理やり固定(Constraint)すると、タスクの柔軟性が失われ、後続タスクとのリンク(FS関係など)がバグる。

  • 対策: 原則として動かすのは `Start` のみ、あるいは `ConstraintDate` のみに留め、期間(Duration)の計算はProjectのエンジンに委ねよ。

—

5. まとめ:ツールに振り回されるな、ツールを飼い馴らせ

今回解説した自動スケジューリングのロジックは、単なるコードの提供ではない。
「リソースのキャパシティという物理的制約をコードにどう落とし込むか」というエンジニアリングの思考そのものだ。

属人化し、誰も全体像を把握できなくなったプロジェクトファイルを前に、ただ頭を抱えるのはもう終わりだ。
この堅牢なアーキテクチャをあなたの現場に導入し、リソースの悲鳴がかき消される静かで強靭なプロジェクトマネジメント環境を構築してほしい。

タイトルとURLをコピーしました