Making custom domain objects RPT-enabled - Ocean - Schlumberger
Making custom domain objects RPT-enabled - Ocean - Schlumberger
Making custom domain objects RPT-enabled - Ocean - Schlumberger
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.