PSI, Scheduling Engine and Task Start and Finish Dates

When working with the task start and finish dates via the Project PSI, you might find some strange behaviors, it is that these two fields are used by the scheduling engine to calculate your project’s schedule. How to work with these two fields and why they may not be set to values that you expect.  When you first create a task, you can set the start date and finish date for the task.

Whenever you call QueueUpdateProject, QueueAddtoProject or QueueDeleteProject, we reschedule the project after applying the changes.

When you create or update a project, the PSI can process up to 1000 rows of data at the same time. If the total number of rows of new or updated data in all tables of ProjectDataSet exceeds 1000, the PSI returns the ProjectExceededItemsLimit error.

There are some limitations and differences between the Project Server scheduling engine and the Microsoft Project Professional scheduling engine. For example, Project Server does not schedule subprojects or links to other projects, and does not calculate earned value fields. For more information, see the Project Scheduling on the Server section in Project Server Programmability (http://go.microsoft.com/fwlink/?LinkId=191606) in the MSDN Library Online.

Publishing and server-side scheduling

Project Server 2010 supports both manual and automated project schedule updates. The default process is to update projects manually. That is, the project manager opens the project in Microsoft Project Professional, applies the changes, and then saves and publishes the project to make the changes available to everyone. The scheduling engine in Microsoft Project Professional calculates scheduling changes for manual updates.

The scheduling engine in Project Server enables automated project updates by using the PSI. Project Server allows the published version of a project to be updated while a project manager is using the draft version, by using the following steps:

1. Project Server applies updates and reschedules the published version automatically.

2. Project Server saves the update to apply to the draft version when either of the following events occurs:

a. Microsoft Project Professional opens the project.

b. Microsoft Project Professional tries to publish the project.

3. If there is a conflict, the project manager must resolve it before the draft version can be published.

The below sample code shows you how to create a new task and how to set these fields:

dsP = new WSProject.ProjectDataSet();

WSProject.ProjectDataSet.TaskRow taskRow = dsP.Task.NewTaskRow();
// Set the requied fields
taskRow.PROJ_UID = projGuid;
taskRow.TASK_UID = taskGuid;
taskRow.TASK_NAME = "Example Task 3"

// Set the start and finish dates
taskRow.TASK_START_DATE = new DateTime(2007, 01, 20);
taskRow.TASK_FINISH_DATE = new DateTime(2007, 01, 20);

taskRow.AddPosition = (int)PSLibrary.Task.AddPositionType.Last;

dsP.Task.AddTaskRow(taskRow);

projWS.QueueAddToProject(jobGuid, sessGuid, dsP, false);

The above sample code sets the start and finish date for the task to be January 20th, 2007. When I publish the project and view it in Project Center Drill Down, this is what I get:

You might notice that the start date and finish date are not set to January 20th, but instead January 17th. This is because the start date of the project is set to January 17th. When the scheduling engine works out the schedule, it looks at the task I just created and determines that it has no constrains, thus it can be started right when the project begins. Thus the scheduling engine changes the start and finish date to January 17th.

Now, lets create another task that is dependent on the one we just created. This time, we will make it’s start and finish date January 31st, 2007:

// Create a second task

dsP = new WSProject.ProjectDataSet();
taskRow = dsP.Task.NewTaskRow();

Guid task2Guid = Guid.NewGuid();
jobGuid = Guid.NewGuid();

// Set the requied fields
taskRow.PROJ_UID = projGuid;
taskRow.TASK_UID = task2Guid;
taskRow.TASK_NAME = "Example Task 4"

// Set the start and finish dates
taskRow.TASK_START_DATE = new DateTime(2007, 01, 31);
taskRow.TASK_FINISH_DATE = new DateTime(2007, 01, 31);

taskRow.AddPosition = (int)PSLibrary.Task.AddPositionType.Last;

dsP.Task.AddTaskRow(taskRow);

// Here we make it dependent on the task we created before

WSProject.ProjectDataSet.DependencyRow dependRow = dsP.Dependency.NewDependencyRow();

dependRow.PROJ_UID = projGuid;
dependRow.LINK_PRED_UID = taskGuid;
dependRow.LINK_SUCC_UID = task2Guid;
dependRow.LINK_UID = Guid.NewGuid();

dsP.Dependency.AddDependencyRow(dependRow);

projWS.QueueAddToProject(jobGuid, sessGuid, dsP, false);

PublishProject(projGuid);

Again you will notices that the schedule engine has moved the task forward to January 18th:

This is because the dependency we added to the new task on the one we had previously created.

Lets say that you have a task that you need to schedule, but you know it cannot start before a certain date, due to some external factors from your project. In this case, you do not want the scheduling engine to move your task forward beyond that date. In this case, we need to set the TASK_CONSTRAINT_DATE and TASK_CONSTRAINT_TYPE fields. The below sample shows how to do this:

// Create a task with a constraint
dsP = new WSProject.ProjectDataSet();

WSProject.ProjectDataSet.TaskRow taskRow = dsP.Task.NewTaskRow();
// Set the requied fields
taskRow.PROJ_UID = projGuid;
taskRow.TASK_UID = taskGuid;
taskRow.TASK_NAME = "Example Task"

// Set the start and finish dates
taskRow.TASK_START_DATE = new DateTime(2007, 01, 22);
taskRow.TASK_FINISH_DATE = new DateTime(2007, 01, 22);

taskRow.TASK_CONSTRAINT_DATE = new DateTime(2007, 01, 22);
taskRow.TASK_CONSTRAINT_TYPE = (short)Library.Task.ConstraintType.StartNoEarlierThan;
taskRow.AddPosition = (int)PSLibrary.Task.AddPositionType.Last;

dsP.Task.AddTaskRow(taskRow);

projWS.QueueAddToProject(jobGuid, sessGuid, dsP, false);

PublishProject(projGuid);

Here is what we get:

So finally we are able to create a task and have it start on a particular date, but there is a catch. You can only put one type of constraint on your task. Here are a list of constraint types that you can use:

 

Constraint Type Description
AsLateAsPossible Schedules the task as late as it can without delaying subsequent tasks. Use no constraint date.
AsSoonAsPossible Schedules the task to start as early as it can. Use no constraint date.
FinishNoEarlierThan Schedules the task to finish on or after the constraint date.
FinishNoLaterThan Schedules the task to finish on or before the constraint date.
MustFinishOn Schedules the task to finish on the constraint date. Once selected the task will not be moveable on the timescale.
MustStartOn Schedules the task to start on the constraint date. Once selected the task will not be movable on the timescale.
StartNoEarlierThan Schedules the task to start on or after the constraint date.
StartNoLaterThan Schedules the task to start on or before the constraint date.

 

Hopefully you have a somewhat of an idea about creating a task and how the start and finish date is affected by the scheduling engine. Now, lets take a look at updating a task’s start and finish date. If you need to update a tasks start or finish date, you will quickly learn that you cannot simply read the project data set, find the task you want to update in the task table and update the start and finish date fields like this:

dsP.Tables[dsP.Task.TableName].Rows[1][dsP.Task.TASK_START_DATEColumn] = new DateTime(2007, 12, 03);

You will quickly run into the following runtime exception:

Column ‘TASK_START_DATE’ is read only.

As the exception states, this is because the start date and finish date are read only fields. These fields are read only because they are calculated fields and cannot be set when updating a project data set.

So how can you get around this? Again, you can place constraints on the dates like we did when creating tasks. The scheduling engine will honor the constraint when calculating the schedule, but remember, there are other factors that affect the calculation. Such as the number of resource assigned to the tasks and the amount of work required to complete the task. So if you constrain your start date, it will affect your finish date. This is why you can only place one constraint on a task. The below example shows how you can update a task that must start on January 15th, 2007:

dsP = projWS.ReadProject(projGuid, ProjOutlookConnector.WSProject.DataStoreEnum.WorkingStore);

dsP.Tables[dsP.Task.TableName].Rows[1][dsP.Task.TASK_CONSTRAINT_TYPEColumn] = (short)Library.Task.ConstraintType.MustStartOn;
dsP.Tables[dsP.Task.TableName].Rows[1][dsP.Task.TASK_CONSTRAINT_DATEColumn] = new DateTime(2007, 01, 15);

projWS.QueueUpdateProject(Guid.NewGuid(), sessGuid, dsP, false);

PublishProject(projGuid);

Here is what you will see in PWA after publishing the project:

We have primarily focused on the factors that affect the start date. Just like the start date, the finish date is affected by many factors that the scheduling engine takes under consideration. For example, the finish date is affected by number of resources assigned to the task, calendar exceptions, such as weekends, specific exceptions in individual resource calendars, and the amount of work assigned required to complete the task.

These are only some basic examples. Project’s schedule engine is very complex and there are a number of factors that affect the start and finish date of a task. Hopefully I have given you some insight why your start and finish dates change.

Chris Boyd

When creating a ProjectDataSet.TaskRow, you must specify TASK_DUR_FMT. Otherwise, later use of the project in Project Professional can result in unpredictable behavior, including possible data loss.

Advertisements