viernes, 25 de abril de 2008

Queue blocked in project server caused by tasks getting enqueued

El otro día trabajando en una solución me encontré con una cola con más de 700 tareas en espera de proceso. Una vez depuradas las tareas y buscando cúales eran las que producían el bloqueo en la cola me encontré con diversas tareas con el estado "Getting enqueued".
Este estado se produce cuando hay algún cliente externo que está compartiendo datos con project server, por ejemplo una publicación de un proyecto desde Project Professional. Si finaliza la conexión con el servidor durante el proceso de sincronización y transmisión de los datos a Project Server la tarea se queda en éste estado, produciendo el bloqueo de la cola para dicho proyecto.
Soluciones:

a) Encontrar el cliente que estaba enviando los datos y que acabe de enviarlos (por ejemplo que el ordenador se haya suspendido mientras realizaba la actualización).

b) Eliminar las tareas que están en este estado marcando antes la opción "Cancelar trabajos en la cola de espera" dentro de "Opciones avanzadas". Éste proceso puede provocar la pérdida de los datos que no se hayan transmitido a Project Server.

martes, 8 de abril de 2008

Issue in Outlook integration when assignning tasks to a resource using PSI

We we assign a task to a resource using PSI Outlook add-in for Project Server is unable to import new assignments.
The root cause of the problem was due to the fact that assignments created entirely through the PSI do not show the link in the progress column that allows clicking through to the timephased entry pop-up.
Microsoft has released a Project Server Rollup KB950816 to solve this problem.
The hotfix will be public soon.

miércoles, 2 de abril de 2008

Types of assignments

This is the table of assignments values taken from msdn.
Most used are work resource (value =2) cost resource (value = 25) and material resource (value = 21 ).


BudgetCostResource
Value=51. Budget cost resource. Can be assigned to Project summary tasks only; cannot log on to Project Web Access.
BudgetMaterialResource
Value=52. Budget material resource. Can be assigned to Project summary tasks only; cannot log on to Project Web Access.
BudgetWorkResource
Value=50. Budget work resource. Can be assigned to Project summary tasks only; cannot log on to Project Web Access.
CAN_LOG_IN_MAXIMUM
Value=20. Any resource with a resource-type value less than this value can log on to Project Web Access.
CostResources
Value=25. Cost resource. Can be assigned to Project tasks; cannot log on to Project Web Access.
GenericBudgetCostResource
Value=54. Generic budget cost resource. Can be assigned to Project summary tasks only; cannot log on to Project Web Access.
GenericBudgetMaterialResource
Value=55. Generic budget material resource. Can be assigned to Project summary tasks only; cannot log on to Project Web Access.
GenericBudgetWorkResource
Value=53. Generic budget work resource. Can be assigned to Project summary tasks only; cannot log on to Project Web Access.
GenericCostResources
Value=26. Generic cost resource. Can be assigned to Project tasks; cannot log on to Project Web Access.
GenericMaterialResource
Value=22. Generic material resource. Can be assigned to Project tasks; cannot log on to Project Web Access.
GenericWorkResource
Value=20. Generic work resource. Can be assigned to Project tasks; cannot log on to Project Web Access.
INACTIVATED_OFFSET
Value=100. An inactive resource has the value of this enumeration added to its resource-type value.
IS_NONBUDGET_TYPE_MAXIMUM
Value=50. Any resource value less than this value is not a budget resource.
MaterialResource
Value=21. Material resource. Can be assigned to Project tasks; cannot log on to Project Web Access.
PureUser
Value=1. Pure user, such as Admins. Cannot be assigned to Project tasks; can use Project Web Access.
WinProjScratchpadResource
Value=-1. Internal use only; do not use.
WinProjSummaryResource
Value=-4. Internal use only; do not use.
WinProjUnassignedResource
Value=-3. Internal use only; do not use.
WinProjUnknownResource
Value=-2. Internal use only; do not use.
WorkResource
Value=2. Work resource. This is the default value. Can be assigned to Project tasks; can log on to Project Web Access.

Create a task assignment

Hi again,
Today I'm going to explain how to create a resource asignment and a cost assignment to a task using PSI.

Variables
ProjWS --> project web service initialized
proyectoid --> GUID of the project


1. First of all we need to get the ProjectDataSet of our project.

ProjectDataSet dsNew = projWS.ReadProject(proyectoid, Project.DataStoreEnum.WorkingStore);

2. Create a new Assignment row in the project

Project.ProjectDataSet.AssignmentRow ResRow = dsNew.Assignment.NewAssignmentRow();

3. Make the assignment between the user and the task

ResRow.PROJ_UID = projectUid; //GUID of the project
ResRow.ASSN_UID = Guid.NewGuid();
ResRow.RES_UID = resUid; // GUID of the resource
ResRow.RES_TYPE = AssignmentType; //Type of assignment
ResRow.TASK_UID = taskUid; //GUID of the task

4. Add the assignment cost if is a cost resource
if ( AssignmentType= 25) {
ResRow.ASSN_COST = ResourceCost;
}

5. Add the new Assignment row to the ProjectDataSet and add it to the project

dsNew.Assignment.AddAssignmentRow(ResRow);
Guid jobGuid = Guid.NewGuid();
projWS.QueueAddToProject(jobGuid, sessionGuid, dsNew, false);

martes, 1 de abril de 2008

Change Project Start Date once it's created through PSI

I've seen several posts on internet of people having problems changing the project start date once the project has been created.
Here is an example of how to do it:

1. Read the project

ProjectDataSet templateProject = projWS.ReadProject(projectUID, Project.DataStoreEnum.WorkingStore);

2. create a copy to store the changes

ProjectDataSet temporal = ((Project.ProjectDataSet)(templateProject.Copy()));

3. Store the new date (fechaInicio.SelectedDate is a Daytime parameter)

temporal.Tables[temporal.Project.TableName].Rows[0][temporal.Project.PROJ_INFO_START_DATEColumn] = fechaInicio.SelectedDate;

4. Get the changes and store them in another ProjectDatatSet

ProjectDataSet updateProjectDataSet = new ProjectDataSet(); updateProjectDataSet.Project.Merge(temporal.Tables[temporal.Project.TableName], true);
updateProjectDataSet = ((Project.ProjectDataSet)updateProjectDataSet.GetChanges(DataRowState.Modified));

5. Check-in and publish the project

projWS.QueueUpdateProject(jobGuid, sessionGuid, updateProjectDataSet, false);
jobGuid = Guid.NewGuid();
projWS.QueueCheckInProject(jobGuid, newProject, true, sessionGuid,"");
WaitForQueueJobCompletion(jobGuid, 1);
jobGuid = Guid.NewGuid();
projWS.QueuePublish(jobGuid, newProject, true, "");
WaitForQueueJobCompletion(jobGuid, 1);

Modifying project tasks through PSI

Aunque parezca una tarea simple el hecho de modificar tareas de Project Server utilizando PSI deberemos tener en cuenta diferentes consideraciones que pueden provocar que los trabajos de actualización de las tareas provoquen un error en la cola de Project Server. Algunas de las consideraciones que deberemos tener son las siguientes:

1. El motor de planificación de Project Server funciona de la misma forma que el de Project Professional, de manera que si realizamos una modificación de fechas en una tarea que tiene predecesoras también deberemos modificar las fechas de las predecesoras. Dicho recálculo de fechas lo deberemos hacer nosotros teniendo en cuenta, por ejemplo, que los fines de semana no son días lectivos de trabajo por defecto en Project.

2. Las asignaciones de recursos no se podrán realizar sobre tareas resumen, sino que se realizarán sobre las tareas hijas. En el caso en el que realicemos una asignación sobre una tarea resumen ésta provocará un error general en la cola.

(más restricciones en próximos días)

Installing Project Server Service Pack 1

Durante la instalación del Service Pack 1 de Sharepoint o el de Projcet Server nos podemos encontrar con que el instalador se queda colgado en el paso 8 de 9. Ésto sucede cuando no se puede arrancar de nuevo el servicio de búsqueda por utilizar un usuario diferente al que se utilizó para la instalación de Project Server.
Para forzar que finalice de forma correcta la instalación se puede ejecutar el siguiente comando:
psconfig -cmd upgrade -inplace b2b -wait -force
Una vez realizado ésto se deberá arrancar el servicio de búsqueda a mano indicando el usuario adecuado.

Queue functions

Looking in projectTool you can find the functions to control the end state of the jobs that you are sending to the queue.
Let's take a look to these functions.


1. Function wich checks if there is an error in the job

private static bool checkStatusRowHasError(string errorInfo)
{
System.Xml.XmlTextReader xReader = new System.Xml.XmlTextReader(new System.IO.StringReader(errorInfo));
while (xReader.Read()) {
if (xReader.Name == "errinfo" && xReader.NodeType == System.Xml.XmlNodeType.Element) {
xReader.Read(); if (xReader.Value == "")
return false;
else return true; }
}
return false; }

2. Function wich waits until the job has finished or timeout has reached.

private bool WaitForQueueJobCompletion(Guid linkId, int messageType, TaskGrid.QueueSystemDerived queueSystem) {

QueueSystem.QueueStatusDataSet queueStatusDataSet = new QueueSystem.QueueStatusDataSet();
QueueSystem.QueueStatusRequestDataSet queueStatusRequestDataSet = new QueueSystem.QueueStatusRequestDataSet();
QueueSystem.QueueStatusRequestDataSet.StatusRequestRow statusRequestRow = queueStatusRequestDataSet.StatusRequest.NewStatusRequestRow();
statusRequestRow.JobGUID = linkId;

3. Coments BUGBUG are from Microsoft Engineers :) Sometimes they leave us some feedback of the development process...

statusRequestRow.JobGroupGUID = Guid.NewGuid(); statusRequestRow.MessageType = -1; //BUGBUG - Rightnow lets just use -1 queueStatusRequestDataSet.StatusRequest.AddStatusRequestRow(statusRequestRow);
bool inProcess = true;
DateTime startTime = DateTime.Now; i
nt successState = (int)QueueConstants.JobState.Success; i
nt failedState = (int)QueueConstants.JobState.Failed;
int blockedState = (int)QueueConstants.JobState.CorrelationBlocked;
while (inProcess) { queueStatusDataSet = queueSystem.ReadJobStatus(queueStatusRequestDataSet, false, QueueSystem.SortColumn.Undefined, QueueSystem.SortOrder.Undefined);
foreach (QueueSystem.QueueStatusDataSet.StatusRow statusRow in queueStatusDataSet.Status)
{
if ((statusRow["ErrorInfo"] != System.DBNull.Value && checkStatusRowHasError(statusRow["ErrorInfo"].ToString()) == true) statusRow.JobCompletionState == blockedState statusRow.JobCompletionState == failedState)
{ ///Error en la cola
inProcess = false;
return false; }
if (statusRow.JobCompletionState == successState)
{ inProcess = false; return true; }
else { inProcess = true; System.Threading.Thread.Sleep(500); } }
DateTime endTime = DateTime.Now;
TimeSpan span = endTime.Subtract(startTime);
if (span.Seconds > 30) //Wait for only 20 secs - and then bail out.
{ ///Error en la cola
return false; } }
return true; }

Create a new Project from a template

Today, in our first post, we are going to learn how to crate a new project from a template using PSI.
PSI are web services wich allow us to connect with Project Server.
Let's see how to use them.
First open a new project and import PSI web references. The first one you need is project.asmx
You can find it in your server under the Project Web Access site.
http://servername/pwa/_vti_bin/PSI/project.asmx
Once you have imported it you can create new projects from a template.

Variables
server --> name of the server.
PROJECTWEBSERVICE --> Project web service adress

1. Create and configure connection
Project.Project projWS;
if (projWS == null)
{
projWS = new Project.Project();
projWS.Url = server + PROJECTWEBSERVICE;
projWS.CookieContainer = new CookieContainer();
projWS.Credentials = CredentialCache.DefaultCredentials;
}

2. Implement functions to get UIDs from templates
private Guid GetProjectTemplateGUID(string templateName) {
Guid tempateGuid = Guid.NewGuid();
Project.ProjectDataSet myProjectList = projWS.ReadProjectList();
foreach (DataRow row in myProjectList.Project) {
if ((string)row["PROJ_NAME"] == templateName) {
tempateGuid = (Guid)row["PROJ_UID"];
return tempateGuid;
}
}
return tempateGuid;
}

3. Create the project from the template

Guid plantillaid = GetProjectTemplateGUID(plantilla.Text);
Guid newProject = Guid.Empty;
newProject = projWS.CreateProjectFromTemplate(plantillaid, nombre.Text);

4. Once you have your new project Created let's change some of its information

4.1 Declare variables to read the project

Project.ProjectDataSet templateProject;
Guid jobGuid = Guid.NewGuid();
Guid sessionGuid = Guid.NewGuid();

4.2 Check-out the project to read it's properties

projWS.CheckOutProject(newProject, sessionGuid, "");
templateProject = projWS.ReadProject(newProject, Project.DataStoreEnum.WorkingStore);

4.3 Create a temp DataSet to store the changes.
//Hemos creado el proyecto, cambiamos los parámetros según los introducidos ProjectDataSet temporal = ((Project.ProjectDataSet)(templateProject.Copy()));

4.4 Change the start date of the project

temporal.Tables[temporal.Project.TableName].Rows[0][temporal.Project.PROJ_INFO_START_DATEColumn] = fechaInicio.SelectedDate;

4.5 Change the owner of the project

Guid propietario = GetResourceGuid(realUser);
if (propietario != Guid.Empty)
temporal.Tables[temporal.Project.TableName].Rows[0][temporal.Project.ProjectOwnerIDColumn] = propietario.ToString();
ProjectDataSet updateProjectDataSet = new ProjectDataSet(); updateProjectDataSet.Project.Merge(temporal.Tables[temporal.Project.TableName], true);
updateProjectDataSet = ((Project.ProjectDataSet)updateProjectDataSet.GetChanges(DataRowState.Modified));

4.6 If there are any changes --> store them

if (updateProjectDataSet != null)
{
projWS.QueueUpdateProject(jobGuid, sessionGuid, updateProjectDataSet, false);
WaitForQueueJobCompletion(jobGuid, 1);
}

4.7 Check-in and publish the project

jobGuid = Guid.NewGuid();
projWS.QueueCheckInProject(jobGuid, newProject, true, sessionGuid,"");
WaitForQueueJobCompletion(jobGuid, 1);
jobGuid = Guid.NewGuid();
projWS.QueuePublish(jobGuid, newProject, true, "");
WaitForQueueJobCompletion(jobGuid, 1);

Welcome to my project Server blog

This blog is created to explain all my experiences with project server and its PSI.
I will create a new post every day explaining all my experiences with Project Server and Project Professional.
Enjoy it !!!