02.05.2014 Views

Making custom domain objects RPT-enabled - Ocean - Schlumberger

Making custom domain objects RPT-enabled - Ocean - Schlumberger

Making custom domain objects RPT-enabled - Ocean - Schlumberger

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

<strong>Making</strong> <strong>custom</strong> <strong>domain</strong> <strong>objects</strong> <strong>RPT</strong>-<strong>enabled</strong><br />

April 12th, 2011 Carlos Rocha, Flávio Ivan Silva, <strong>Schlumberger</strong><br />

First published in SWITCH: SOFTWARE & IT CHANNEL NEWSLETTERS, 2011<br />

Reference Project Tool (<strong>RPT</strong>) is a Petrel* functionality that allows teams to collaborate by sharing<br />

data between two different projects. For instance, the user might want to use <strong>RPT</strong> to transfer well<br />

logs, templates, windows, seismic data and <strong>custom</strong> <strong>domain</strong> <strong>objects</strong> from one project to another.<br />

This article focuses on how to make <strong>custom</strong> <strong>objects</strong> available in the <strong>RPT</strong> interface through the<br />

implementation of specific <strong>Ocean</strong>* APIs. Code samples along with persistence issues are showed in<br />

order to illustrate the basics of the API.<br />

1. Introduction to <strong>RPT</strong><br />

Reference Project Tool is a Petrel functionality that allows users to easily compare and copy <strong>domain</strong><br />

<strong>objects</strong> (both native and <strong>custom</strong>ized) between two different projects: the one currently opened<br />

(Working project) and the other set as the reference (Background project).<br />

In the case of a <strong>custom</strong> object XYZ added to a given project, the user may open the Reference<br />

Project Tool dialog by going to File -> Reference Project Tool, and then defining the file path of<br />

another (Background) project. If both projects had a copy of a given XYZ object, the user may be<br />

able to see which of the instances of this object is newer, and possibly copy it from one project to<br />

another.<br />

<strong>Ocean</strong> Developer Best Practices: <strong>Ocean</strong> for Petrel framework Page 1 of 9<br />

Submitted by the developer community<br />

*Mark of <strong>Schlumberger</strong>. Other company, product, and service names are the properties of their respective owners. Copyright © 2011<br />

<strong>Schlumberger</strong>. All rights reserved.


Figure 1: <strong>RPT</strong> interface and the same XYZ object in both projects<br />

2. Persistence of a <strong>custom</strong> <strong>domain</strong> object<br />

As <strong>RPT</strong> involves opening and copying <strong>objects</strong> between different projects, the persistence of the<br />

<strong>objects</strong> in the Working and Background projects is a pre-requisite for having <strong>RPT</strong> properly<br />

functioning.<br />

The persistence of a <strong>custom</strong> <strong>domain</strong> object may happen according to two approaches: (a) by<br />

normal .NET serialization (ISerializable); or (b) by <strong>Ocean</strong> infra-structure for serialization<br />

(IIdentifiable and IDataSource).<br />

In this article, the <strong>Ocean</strong> approach will be used. Such strategy involves first giving to any <strong>custom</strong><br />

<strong>domain</strong> object instance an ID, or a DROID (Durable Runtime Object Identifier). This is<br />

accomplished by the implementation of the Slb.<strong>Ocean</strong>.Core.IIdentifiable interface.<br />

public class XYZObject : IIdentifiable<br />

{<br />

private Droid objectDroid = Droid.Empty;<br />

public XYZObject()<br />

{<br />

CustomDataSource ds = CustomDataSource.Get(DataManager.DataSourceManager);<br />

this.objectDroid = ds.Add(this);<br />

<strong>Ocean</strong> Developer Best Practices: <strong>Ocean</strong> for Petrel framework Page 2 of 9<br />

Submitted by the developer community<br />

*Mark of <strong>Schlumberger</strong>. Other company, product, and service names are the properties of their respective owners. Copyright © 2011<br />

<strong>Schlumberger</strong>. All rights reserved.


}<br />

public Droid Droid<br />

{<br />

get { return this.objectDroid; }<br />

}<br />

}<br />

With the implementation above, each XYZObject instance will have a unique identifier (Droid). This<br />

Droid is the key for saving this XYZObject in its CustomDataSource, which will be kept together<br />

with other data sources in the Petrel project DataManager.<br />

Figure 2: Schematic of the <strong>Ocean</strong> approach to serialization<br />

3. Implementing a <strong>custom</strong> data source<br />

To implement the <strong>custom</strong> data source, three steps must be followed.<br />

1) 1. Implementation of a data source factory, by extending DataSourceFactory, like in the example<br />

below:<br />

class CustomDSFactory : DataSourceFactory<br />

{<br />

(…)<br />

public override IDataSource GetDataSource()<br />

<strong>Ocean</strong> Developer Best Practices: <strong>Ocean</strong> for Petrel framework Page 3 of 9<br />

Submitted by the developer community<br />

*Mark of <strong>Schlumberger</strong>. Other company, product, and service names are the properties of their respective owners. Copyright © 2011<br />

<strong>Schlumberger</strong>. All rights reserved.


{<br />

return new CustomDataSource(this.fileName, this.dataSourceID);<br />

}<br />

}<br />

2. Registering the factory in the Petrel project DataManager, on the Petrel Module initialization:<br />

public void Initialize()<br />

{<br />

CustomDSFactory dsf = new CustomDSFactory("CustomXYZ.dat", “My_DS_ID");<br />

PetrelSystem.AddDataSourceFactory(dsf);<br />

}<br />

Notice the CustomXYZ.dat file will contain the serialized <strong>custom</strong> <strong>objects</strong>:<br />

Figure 3: File containing the DataSource serialized<br />

3. Finally, the implementation of the <strong>custom</strong> data source that deals with<br />

serialization/deserialization and keeps a Hashtable to store the <strong>custom</strong> object and its Droid Id:<br />

public class CustomDataSource : IDataSource<br />

{<br />

public CustomDataSource(string fileName, string dataSourceIdentifier)<br />

{<br />

this.<strong>custom</strong>ObjectsDictionary = new Hashtable();<br />

(…)<br />

}<br />

// Adds an object and its droid to the dictionary<br />

public Droid Add(IIdentifiable <strong>custom</strong>Object)<br />

{<br />

Droid objectDroid = <strong>custom</strong>Object.Droid;<br />

if (objectDroid.Equals(Droid.Empty))<br />

objectDroid = new Droid(Id, Guid.NewGuid().ToString());<br />

this.<strong>custom</strong>ObjectsDictionary.Add(objectDroid, <strong>custom</strong>Object);<br />

return objectDroid;<br />

<strong>Ocean</strong> Developer Best Practices: <strong>Ocean</strong> for Petrel framework Page 4 of 9<br />

Submitted by the developer community<br />

*Mark of <strong>Schlumberger</strong>. Other company, product, and service names are the properties of their respective owners. Copyright © 2011<br />

<strong>Schlumberger</strong>. All rights reserved.


}<br />

// Called by the framework when user opens a Petrel project file<br />

public void Open()<br />

{<br />

(…)<br />

using (FileStream file = fileInfo.OpenRead())<br />

{<br />

BinaryFormatter bf = new BinaryFormatter();<br />

this.<strong>custom</strong>ObjectsDictionary = bf.Deserialize(file) as Hashtable;<br />

}<br />

}<br />

// Called by the framework when user saves a Petrel project file<br />

public void Save()<br />

{<br />

(…)<br />

using (FileStream fileStream = fileInfo.OpenWrite())<br />

{<br />

BinaryFormatter bf = new BinaryFormatter();<br />

bf.Serialize(fileStream, this.<strong>custom</strong>ObjectsDictionary);<br />

}<br />

}<br />

}<br />

The Add() function is used to store IIdentifiable <strong>objects</strong> and its droid in a hashtable. Open()<br />

deserializes a binary file in order to fill the hashtable and Save() serializes the hashtable into a file<br />

stream.<br />

4. Important notes about serialization<br />

There are two very important remarks to be done about serialization:<br />

1) 1. If a <strong>custom</strong> <strong>domain</strong> object contains references to other Petrel <strong>domain</strong> <strong>objects</strong>, it must serialize<br />

only the Droid’s of such references and not the <strong>objects</strong> themselves. Observe the example below:<br />

[NonSerialized]<br />

private SeismicCube internalSeismicCube;<br />

private Droid seismicCubeDroid;<br />

2) 2. Before calling the serialization (CustomDataSource.Save() method), any events on the <strong>objects</strong> to<br />

be serialized must be turned off.<br />

obj.Changed -= this .changedEventHandler;<br />

Such events must probably be linked again after the serialization has finished executing.<br />

<strong>Ocean</strong> Developer Best Practices: <strong>Ocean</strong> for Petrel framework Page 5 of 9<br />

Submitted by the developer community<br />

*Mark of <strong>Schlumberger</strong>. Other company, product, and service names are the properties of their respective owners. Copyright © 2011<br />

<strong>Schlumberger</strong>. All rights reserved.


5. Implementing <strong>RPT</strong><br />

To be <strong>RPT</strong>-<strong>enabled</strong>, a <strong>custom</strong> object must implement ISyncable methods: Overwrite, Copy,<br />

CompareTo and CreateSnapshot. Henceforth, the term “target” is used to mean the project where<br />

the <strong>custom</strong> object will be placed and the term “source” to mean which project it came from.<br />

5.1. Overwrite<br />

The Overwrite method is called when Petrel identifies that the object to be transferred is already<br />

present in the target side, which means that a overwriting is going to occur.<br />

Figure 4: The XYZ object at right will be overwritten by the one at left<br />

The code below shows an example of how the Overwrite method looks like when the XYZ <strong>custom</strong><br />

object is going to be overwritten.<br />

public void Overwrite(object source, OverwriteContext context)<br />

{<br />

XYZObject xyzSource = source as XYZObject;<br />

this.x = xyzSource.X; // Copying the object fields from source to target (this)<br />

<strong>Ocean</strong> Developer Best Practices: <strong>Ocean</strong> for Petrel framework Page 6 of 9<br />

Submitted by the developer community<br />

*Mark of <strong>Schlumberger</strong>. Other company, product, and service names are the properties of their respective owners. Copyright © 2011<br />

<strong>Schlumberger</strong>. All rights reserved.


this.y = xyzSource.Y;<br />

this.lastModified = xyzSource.LastModified; // Datetime indicates changes<br />

this.droid = xyzSource.Droid; // When overwriting, droid is the same<br />

}<br />

5.2. Copy<br />

The Copy function is called either when XYZ is not present in the target side or when user selects<br />

“Copy mode” checkbox before transferring. In “Copy mode”, the <strong>custom</strong> object does not maintain<br />

its identity as a new one will be created in the target side.<br />

Figure 5: XYZ object being copied from left to right<br />

The code below shows the Copy function and its CopyContext parameter. A new XYZ object will be<br />

constructed depending on whether it has to keep its identity (KeepIdentity equals false when in<br />

“Copy mode”) and will be added to the target data source.<br />

public object Copy(CopyContext ct)<br />

{<br />

CustomDataSource source = CustomDataSource.Get(DataManager.DataSourceManager);<br />

CustomDataSource target = ct.TargetDataManager.GetSource(source.Id)<br />

as CustomDataSource;<br />

<strong>Ocean</strong> Developer Best Practices: <strong>Ocean</strong> for Petrel framework Page 7 of 9<br />

Submitted by the developer community<br />

*Mark of <strong>Schlumberger</strong>. Other company, product, and service names are the properties of their respective owners. Copyright © 2011<br />

<strong>Schlumberger</strong>. All rights reserved.


Creating and returning a new XYZObject<br />

return new XYZObject(this, target, ct.KeepIdentity);<br />

}<br />

5.3. CompareTo<br />

This function dictates the icon types of the <strong>RPT</strong> interface and it is called by the framework<br />

whenever comparisons are needed. There are two types of comparisons: (a) by identity, where the<br />

Droids of the <strong>objects</strong> are compared to check whether they are identical; (b) by version, where time<br />

stamp is compared. When the droids of both <strong>objects</strong> are equal, the comparison is by time stamp.<br />

This is shown in the code below.<br />

public ComparisonResult CompareTo(object other, CompareContext context)<br />

{<br />

if (other is XYZObject)<br />

{<br />

XYZObject xyz = other as XYZObject;<br />

switch (context.Comparison)<br />

{<br />

case WellKnownComparisons.ByIdentity:<br />

// Comparing the droids<br />

return this.Droid.Equals(xyz.Droid) ?<br />

ComparisonResult.Equal : ComparisonResult.NotEqual;<br />

case WellKnownComparisons. ByVersion:<br />

// Comparing the timestamps<br />

if (this.LastModified < xyz.LastModified)<br />

return ComparisonResult.LessAndNotEqual;<br />

else if (this.LastModified > xyz.LastModified)<br />

return ComparisonResult.GreaterAndNotEqual;<br />

return ComparisonResult.Equal;<br />

default:<br />

return ComparisonResult.NotSupported;<br />

}<br />

}<br />

return ComparisonResult.NotEqual;<br />

}<br />

5.4. CreateSnapshot<br />

This function is called to create a new <strong>custom</strong> object that will be passed to the Copy function, as<br />

part of CopyContext parameter. For the sake of this example, this function can simply return null<br />

as the Copy function already took care of the creation of the XYZ object.<br />

<strong>Ocean</strong> Developer Best Practices: <strong>Ocean</strong> for Petrel framework Page 8 of 9<br />

Submitted by the developer community<br />

*Mark of <strong>Schlumberger</strong>. Other company, product, and service names are the properties of their respective owners. Copyright © 2011<br />

<strong>Schlumberger</strong>. All rights reserved.


public object CreateSnapshot()<br />

{<br />

return null;<br />

}<br />

6. Conclusion<br />

In this article, we analyzed the basics of the <strong>Ocean</strong> API that deals with persistence and <strong>RPT</strong>. That<br />

involved going through the implementation of interfaces like IIdentifiable and ISyncable and we<br />

also mentioned important remarks to take into account when making your <strong>custom</strong> <strong>domain</strong> <strong>objects</strong><br />

<strong>RPT</strong>-<strong>enabled</strong>.<br />

<strong>Ocean</strong> Developer Best Practices: <strong>Ocean</strong> for Petrel framework Page 9 of 9<br />

Submitted by the developer community<br />

*Mark of <strong>Schlumberger</strong>. Other company, product, and service names are the properties of their respective owners. Copyright © 2011<br />

<strong>Schlumberger</strong>. All rights reserved.

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!