Intergraph

Shortening CustomCommand development time by using a proxy

One of the most time-consuming processes when developing with Hexagon G/Technology is creating custom commands. The usual development approach consists of the following iterative process:

  • Compile custom command
  • Launch G/Technology
  • Test custom command
  • Exit G/Technology
  • Modify custom command
During testing, only limited changes to the source code are allowed by the Visual Studio Edit and Continue function, effectively requiring multiple iterations of this sequence.

The process of starting and closing G/Technology can easiliy takeup multiple minutes and you need to close it because G/Technology will lock any assemblies containing your custom commands.

Customcommands are written in DotNet and therefore run in something called an “Assembly Domain”, in this case the G/Technology Assembly Domain. When you create a custom command loading other custom commands in its own assembly domain, dotnet will not load the same assembly again in fact creating a proxy. The following code snippet demonstrates this:

string assembly = @"c:\Program Files (86)\Intergraph\GTechnology\YourCustomCommand.dll";
byte[] assemblyBytes = File.ReadAllBytes(assembly);
System.Reflection.Assembly assemblyToLoad = Assembly.Load(assemblyBytes);

Type entryClass = assemblyToLoad.GetTypes().FirstOrDefault(t ⇒ typeof(IGTCustomCommandModal).IsAssignableFrom(t));
if( entryClass != null)
{
    IGTCustomCommandModal CCModal = (IGTCustomCommandModal)assemblyToLoad.CreateInstance(entryClass.FullName);
    CCModal.Activate();
    return;
}

entryClass = assemblyToLoad.GetTypes().FirstOrDefault(t ⇒ typeof(IGTCustomCommandModeless).IsAssignableFrom(t));
if( entryClass != null)
{
    IGTCustomCommandModeless CCModeless = (IGTCustomCommandModeless)assemblyToLoad.CreateInstance(entryClass.FullName);
    CCModeless.Activate(_customCommandHelper);
    return;
}

This snippet scans an assembly for a type implementing either interface ‘IGTCustomCommandModal‘ or ‘IGTCustomCommandModeless‘, which both can be found in namespace ‘Intergraph.GTechnology.Interfaces‘ and are needed to implement customcommands. If a type implements one of these interfaces, the proxy customcommand creates an instance of it loading it in the assembly domain.

When this approach is used to load custom commands, G/Technology will not lock the containing assemblies after closing the custom command, enabling much shorter development cycles.

The technique constists of 2 or more custom commands, the first one being the proxy, the second one being the custom command to be developed. Once the first customcommand is started, it will show a dialog where the assembly containing the custom command to be developed should be entered :

Proxy dialog

Some extra querying to the G3E_CUSTOMCOMMAND table allows to provide useful metadata as shown in the pop-up window.

Then attach the Visual Studio debugger and press ‘Launch’ and any breakpoint in your customcommand should be hit and it can be tested. Once done, detach the debugger, modify code, compile and attach again, etc., etc. :

Debugging session active

An example of a session using this technique this can be seen in this this youtube video, showing a debug session of a custom command called “Swap Inner Ducts” which will just show a dialog and a messagebox, but the important part is that the customcommand is changed, recompiled and executed again using the Visual Studio debugger without leaving G/Technology.

Youtube video

Sources can be downloaded from here.

Credits go to Jan Stuckens for initially coming up with this idea.

Thanks to Michaël Demanet and Didier de Bisschop from Proximus for use of their environment to test this technique.

Notes :

  • the proxy needs to be built with debugging information, else breakpoints in the target customcommand won’t be hit
  • All referenced assemblies need to be loaded
  • This technique has been used with assemblies containing a single custom command, assemblies with multiple custom commands where not tested
  • G/Technology version 10.04.2002.00035 was used to test this approach

Invoking Oracle Spatial from a Custom Command

Consider a requirement where the centroid of a set of points needs to be retrieved within a custom command. This can be achieved using the ‘GTDataProvider.DataContext.Execute‘-method:

IGTApplication GTApp = GTClassFactory.Create<IGTApplication>( );

string coors = "0, 0, 0, 10, 0, 0, 10, 10, 0, 0, 10, 0, 0, 0, 0"; // change to actual coordinates
string sql = $@"WITH shape AS
  ( 
  	select 1 id, sdo_geometry( 3003, NULL, NULL
         , sdo_elem_info_array( 1, 1003, 1)
         , sdo_ordinate_array({coors} )) G FROM dual
  )
SELECT s.ID, t.x, t.y FROM shape S, sdo_util.getvertices( sdo_geom.sdo_centroid( S.G)) t";

ADODB.Recordset rs = GTApp.DataContext.Execute( sql, out _, (int)ADODB.CommandTypeEnum.adCmdText);

if( rs == null) return;
if( rs.RecordCount == 0 ) return;
rs.MoveFirst( );

string x = System.Convert.ToString( rs.Fields[ "X"].Value);
string y = System.Convert.ToString( rs.Fields[ "Y"].Value);

System.Diagnostics.Debug.WriteLine( string.Format( "x={0}, y={1}", x, y)); // x=5, y=5

rs.Close();
rs = null;

Rubberbanding in G/Technology

Whenever a user is digitizing a polygon, he expects feedback when moving the cursor before actually adding a point, the system should ‘rubberband’ the position under the cursor and only add it to a collection of points after the user left-mouse clicks in the Window. Key to rubberbending in G/Technology is responding to three events :

  1. CustomCommandHelper_Click
  2. CustomCommandHelper_MouseMove
  3. CustomCommandHelper_DblClick

CustomCommandHelper_Click

private void CustomCommandHelper_Click( object sender, GTMouseEventArgs e )
{
  if( this.PntCnt == 0)
  {
    this.PntCnt++;

    this.Idx = this.GeometryCreationService.AddGeometry( GTClassFactory.Create<IGTPolygonGeometry>(), StyleId);
    this.GeometryCreationService.AppendPoint( this.Idx, e.WorldPoint);
    return;
  }

  this.GeometryCreationService.AppendPoint( this.Idx, e.WorldPoint);
}

CustomCommandHelper_MouseMove

private void CustomCommandHelper_MouseMove( object sender, GTMouseEventArgs e )
{
  if( this.PntCnt == 0)
  {
    this.setGtStatusBar( "Click to start digitizing");
    return;
  }

  if( this.PntCnt == 1)
  {
    this.setGtStatusBar( "Click to add point");
    this.GeometryCreationService.SetDynamicPoint( this.Idx, e.WorldPoint);
    return;
  }
				
  this.setGtStatusBar( "Click to add point, Doubleclick to end");
  this.GeometryCreationService.SetDynamicPoint( this.Idx, e.WorldPoint);
}

CustomCommandHelper_DblClick

private void CustomCommandHelper_DblClick( object sender, GTMouseEventArgs e )
{
  this.PntCnt = 0;
}

Besides these 3 events, the following general code is used:

general code

private IGTApplication _GTApp = null;
private IGTApplication GTApp
{
  get
  {
    if( this._GTApp == null)
    {
      this._GTApp = GTClassFactory.Create<IGTApplication>();
    }

    return( this._GTApp);
   }
}

private IGTGeometryCreationService GeometryCreationService { get; set; } = GTClassFactory.Create<IGTGeometryCreationService>();

private void setGtStatusBar( string message)
{
  this.GTApp.SetStatusBarText( GTStatusPanelConstants.gtaspcMessage, message);
}

private int Idx { get; set; }
private const int StyleId = 5010;

The complete code can be downloaded from here.

Invalid Fiber Couplers

Fiber Couplers are used to connect Fiber Inner Ducts in a Fiber Branch Enclosure. You can connect Fiber Inner Ducts using the Fiber Feature Editor by selecting the two Fiber Inner Ducts and choosing the ‘Couple’-icon :

Connecting Fiber Inner Ducts

Connecting Fiber Inner Ducts

This will connect the 2 selected Fiber Inner ducts using a Fiber Coupler. After coupling the Fiber Inner Ducts the picture looks like this :

Connected Inner Ducts

Connected Inner Ducts

The two selected Fiber Inner Ducts are now connected via a Fiber Coupler which you can see in Feature Explorer if you go to the connected features of one of the Fiber Inner Ducts :

Explorer feature in Fiber Feature Editor

Explore feature in Fiber Feature Editor

Explored feature in Feature Editor

Explored feature in Feature Editor

Fiber Couplers are only allowed to be connected to Fiber Inner Ducts and they can only exist in the system when they have a connection. The following Fiber Couplers are invalid :

  • Fiber Couplers with more then 2 connections
  • Fiber Couplers with a single connection
  • Fiber Couplers without a connection
  • Fiber Couplers connected to other features then Fiber Inner Ducts

The following AdHoc-query will display all Fiber Couplers with invalid connections :

with T as
(      
select 	4300 		g3e_fno
	,	a.g3e_fid
	,	4301 		g3e_cno
	,	1 			g3e_cid	
	,	count(*) 	cnt	 
	from gc_ne_connect a
		inner join gc_ne_connect b on ( (b.node1_id = a.node1_id) or ( b.node2_id = a.node1_id) )
			and b.g3e_fno != 4300
	where a.g3e_fno = 4300 
		and a.node1_id != 0 
		and a.node2_id != 0
		group by a.g3e_fid
UNION
select 4300	g3e_fno
	,	g3e_fid
    ,	4301 g3e_cno
    ,	1	g3e_cid
    , 0 cnd
    from gc_ne_connect
    where g3e_fno = 4300 
    and node1_id = 0 and node2_id = 0
)select g3e_fno
	,	g3e_fid
    ,	g3e_cno
    ,	g3e_cid
    ,	cnt from t 
    where cnt != 2
    order by cnt desc

The result looks like this :

invalid Fiber Couplers

invalid Fiber Couplers

Hope this helps, Stephan

AdHoc Queries

Fiber Cables

Introduction

G/Technology provides functionality to run dynamic queries, the so called ‘Ad-Hoc queries’. A Large telecom provider in the Netherlands is moving Fiber Cables from CRAMER to FOW and AdHoc-queries provide a great tool to analyze and visualize migrated data

At this provider Fiber Cables are contained by Fiber Inner Ducts who themselves are contained by Fiber Ducts. A fiber Duct has a Tag marked on it a.k.a. B217982 so workers can identify a Duct. Fiber Ducts go from Fiber Branch Enclosure to Fiber Branch Enclosure, Fiber Inner Ducts also do. Fiber Cables go from Fiber Splice Enclosures to Fiber Splice Enclosures. Fiber Branch Enclosures contain Fiber Splice Enclosures. This is illustrated by the following figure:

Fiber Cables

Fiber Cables

Click here for the legend.

This network is registered in 2 systems, CRAMER NIM (CRAMER Network Inventory Management) and GEOS FOW (GTechnology Fiber Optic Works), but Fiber Cables are registered only in NIM. This is illustrated by the next figures:

NIM-GEOS

NIM-GEOS

Fiber Cables are only registred in NIM, not in GEOS FOW. To enable better management of Fiber assets, registration of Fiber Cables need to be moved from NIM to GEOS FOW. Fiber Branch- and Fiber Splice Enclosures have an asset-id which is a number like ‘1001’ Fiber Cables have a name like ‘Hd-Hd 1’. This name is also maintained with Fiber Inner Ducts in NIM and is used to put Fiber Cables from NIM into the right Fiber Inner Duct in GEOS FOW. In the current GEOS FOW system where there are no Fiber Cables (yet) present, this looks like this.
The Fiber Inner Ducts with the Fiber Cable name on it are indicated in red and are contained in a Duct. You can also see that the ‘Contains’-node of the Fiber Inner Ducts do not have a value, but this is where migrated Fiber Cables will appear.

When all these assets are registered correctly, Fiber Cables can be migrated from NIM to FOW. Both systems NIM and GEOS FOW are running for several years so mismatches between the 2 systems exists and not all Fiber Cables can be migrated correctly. To analyze and visualize the quality of migration, Ad-Hoc queries play an important role.

Fiber Inner Ducts without cables

One of the queries giving insight in the data migration has the following requirements: Show all Fiber Ducts with a Fiber Inner Duct in it who don’t have a cable inside it. If the ‘CABLE_NAME’ attribute of these ducts had been set, then they should have a cable inside it and are not reserved stock aka. reserved for future use. In FOW Fiber Inner Ducts (fno=4100) have a ‘Contained By’ relation with Fiber Ducts (fno=4000), and Fiber Cables (fno=7200) also have a ‘Contained By’ relation with Fiber Inner Ducts. Using this relation, the following Ad-Hoc query lists all Fiber Ducts with Fiber Inner Ducts in it without a cable :

select  gc_fduct_l.g3e_fno
	,	gc_fduct_l.g3e_fid
	,	gc_fduct_l.g3e_cno
	,	gc_fduct_l.g3e_cid
	,	a.g3e_fid	fid_inner_duct
	,	gc_netelem.cable_name	
	from gc_contain a
		inner join gc_netelem on gc_netelem.g3e_fid = a.g3e_fid
		inner join gc_fduct_l on gc_fduct_l.g3e_fid = a.g3e_ownerfid
	where a.g3e_fno = 4100
		and a.g3e_ownerfno = 4000
		and gc_netelem.cable_name is not null
		and gc_netelem.g3e_fno = 4100
		and rownum < 100
	and not exists (select g3e_fid from gc_contain b where b.g3e_fno = 7200 and b.g3e_ownerfid = a.g3e_fid)

You can run this query using the ‘Ad Hoc’-query wizard (click here for a video) and after some scrolling the output may look like this :

Basic AdHoc Query

Basic AdHoc Query

The Query Name is added to the ‘Queries’-node in the Legend and the extends of the Netherlands is shown. The query has run successful, and there are many Fiber Inner Ducts without a Fiber Cable in it but we cannot really see them without turning off all the items in the legend and then zooming in into one of the items of the resultset.

Notes:

  • Before creating your first Ad-Hoc query, you need to define an ‘Area Of Interest’
  • We are limiting the output to 100 rows
  • Ad-Hoc queries always need to output at least the following attributes : G3E_FNO, g3E_FID, G3E_CNO & G3E_CID. These attributes enable selection of separate features from the result set and should point to a graphical components for selection in a Map window
  • AdHoc-queries are saved in a Workspace, when creating a new Workspace previous Ad-Hoc are not available
  • When creating AdHoc-queries the feature selected is just a placeholder, but the Wizard only continues to the second screen if you select a feature. Once you have done that you can query any feature
  • You can change the appearance of your result set even after running it using Display Control
  • Save and test your query outside G/Technology for easy development

Avoid joints

While the query used does its work, table GC_FDUCT_L is joined to get the Graphic Lines in the system representing Fiber Ducts. Since the Feature- & Component numbers for this feature don’t change and are required, non-repeating components we can rewrite the query as follows:

select  4000 g3e_fno
	, a.g3e_ownerfid g3e_fid
	, 4010 g3e_cno
	, 1 g3e_cid
	, a.g3e_fid	fid_inner_duct
	, gc_netelem.cable_name	
	from gc_contain a
		inner join gc_netelem on gc_netelem.g3e_fid = a.g3e_fid
	where a.g3e_fno = 4100
		and a.g3e_ownerfno = 4000
		and gc_netelem.cable_name is not null
		and gc_netelem.g3e_fno = 4100
		and rownum < 100
	and not exists (select g3e_fid from gc_contain b where b.g3e_fno = 7200 and b.g3e_ownerfid = a.g3e_fid)

The necessary keys g3e_fno, g3e_cno & g3e_cid are still present but are now created using hardcoded value instead of SQL-joins, only the value for g3e_fid is variable but is now fetched from GC_CONTAIN. In this approach, table GC_FDUCT_L is no longer required.

Output results to a DataTable

This first queries showed we have some problems with our data migration, but the results are not really in a readable format. If you have a query with multiple results like this one, it may be convenient to output the results to a Datatable.

Output to datatable

If you output results to a datatable you can easily do the following:

  • Fit a feature
  • Select a feature in Feature Explorer
  • Export results

Note: If your result set is very large, it is better to export your results using a dedicated SQL-tool.

Custom ‘Areas Of Interest’

When creating an AdHoc-query, you can limit the results to be contained in a predefined area, the so called ‘Areas Of Interest’ (AOI). If you do so, G/Technology will first run a dedicated query to fetch features from your active AOI and then add some dedicated code after your query. If we take our first query:

select  gc_fduct_l.g3e_fno
	,	gc_fduct_l.g3e_fid
	,	gc_fduct_l.g3e_cno
	,	gc_fduct_l.g3e_cid
	,	a.g3e_fid	fid_inner_duct
	,	gc_netelem.cable_name	
	from gc_contain a
		inner join gc_netelem on gc_netelem.g3e_fid = a.g3e_fid
		inner join gc_fduct_l on gc_fduct_l.g3e_fid = a.g3e_ownerfid
	where a.g3e_fno = 4100
		and a.g3e_ownerfno = 4000
		and gc_netelem.cable_name is not null
		and gc_netelem.g3e_fno = 4100
	and not exists (select g3e_fid from gc_contain b where b.g3e_fno = 7200 and b.g3e_ownerfid = a.g3e_fid)

will be changed to:

select  gc_fduct_l.g3e_fno
	,	gc_fduct_l.g3e_fid
	,	gc_fduct_l.g3e_cno
	,	gc_fduct_l.g3e_cid
	,	a.g3e_fid	fid_inner_duct
	,	gc_netelem.cable_name	
	from gc_contain a
		inner join gc_netelem on gc_netelem.g3e_fid = a.g3e_fid
		inner join gc_fduct_l on gc_fduct_l.g3e_fid = a.g3e_ownerfid
	where a.g3e_fno = 4100
		and a.g3e_ownerfno = 4000
		and gc_netelem.cable_name is not null
		and gc_netelem.g3e_fno = 4100
	and not exists (select g3e_fid from gc_contain b , AOIQUERYRESULT AQR WHERE (GC_FDUCT.G3E_FID = AQR.G3E_FID AND AQR.G3E_USERNAME='GTFIBER' AND AQR.G3E_QUERYNAME='aa') AND (b.g3e_fno = 7200 and b.g3e_ownerfid = a.g3e_fid))

This generates an Oracle error. Creating an Ad-Hoc query using an AOI can be hard to get working, but you can use Oracle Spatial to create your own ‘Areas Of Interest’. This feature is called ‘CLLI Boundary’ is used to create Areas of Interest, but we can also use it in Oracle Spatial to limit our query to only process a given area called ‘Hd-C’:

select  gc_fduct_l.g3e_fno
	,	gc_fduct_l.g3e_fid
	,	gc_fduct_l.g3e_cno
	,	gc_fduct_l.g3e_cid
	,	a.g3e_fid	fid_inner_duct
	,	gc_netelem.cable_name	
	from gc_contain a
		inner join gc_netelem on gc_netelem.g3e_fid = a.g3e_fid
		inner join gc_fduct_l on gc_fduct_l.g3e_fid = a.g3e_ownerfid
		, gc_bnd_p	
	where a.g3e_fno = 4100
		and a.g3e_ownerfno = 4000
		and gc_netelem.cable_name is not null
		and gc_netelem.g3e_fno = 4100
		and gc_bnd_p.feature_type = 'CLLI'		
		and gc_bnd_p.wc_clli = 'Hd-C'
		and not exists (select g3e_fid from gc_contain b where b.g3e_fno = 7200 and b.g3e_ownerfid = a.g3e_fid)
		and SDO_GEOM.RELATE( gc_bnd_p.g3e_geometry, 'ANYINTERACT', gc_fduct_l.g3e_geometry, 0.1) = 'TRUE'

The SDO_GEOM.RELATE Oracle Spatial operator is used in the where clause to get all the Fiber Ducts contained in area ‘Hd-C’.

Finding features with carriage returns in Attribute values

When users are entering attribute values using Feature Explorer they may be expecting G/Technology saving values when entering ‘Enter’. GTech will not do this, but will instead store a Carriage return/Line Feed with the attribute value. This will lead to pollution of attribute values in the database and give mismatches between tag-id’s from ducts coming from NIM and ducts in GTech. During the migration we might for example be looking for two ducts with Tag-id ‘B217982’, but what we find I ‘B217982’ and ‘B217982Chr(10)Chr(13)’. The following Ad-Hoc query will show all Fiber Duct features (fno=4000) who’s cable_name attribute has the ‘CHR(13)CHR(10)’ character combination (CHR(13)=Carriage Return, CHR(10)=Line Feed) :

select g3e_fno
     , g3e_fid
	 , g3e_cno
	 , g3e_cid
	 , cable_name
from gc_netelem	
where gc_netelem.g3e_fno = 4000
and regexp_like( trim(cable_name), CHR (13) || CHR(10) )

Click here for a Video of creating and running this Query.

Hope this Helps. Stephan

Intergraph issues database will be public available

Intergraph has an internal database for keeping track of all kinds of software issues, if you file a Trouble- or Change Request, it will end up in this database. This database will become public available in the 3rd quarter of 2013 according to Intergraph sources at Hexagon 2013. Also, if a problem gets fixed, the solution will be described so you know if applies to you. All this information will be public accessible and indexed by Google, so if you are facing a problem in G/Technology (or any other Intergraph product) google will find it and it will show up in the results of your search.