This project has moved and is read-only. For the latest updates, please go here.

WaterOneFlow Client

Nov 19, 2009 at 7:31 PM

I have a few questions here:

 

I've noticed two different WaterOneFlow clients.  There's a WaterMLClient class in the MetadataFetcher plugin, and a WaterOneFlowClient class in the Search plugin.  Has there been any thought into creating a single package of classes for working with WaterOneFlow?  If so, how would plugins reference the package, since plugins are supposed to be fairly standalone?  Would the WaterOneFlow client class become part of the standard HydroDesktop installation, after which plugins could reference it?  I've started working on the MetadataFetcher plugin, and I would hate to reproduce effort in developing a WaterOneFlow client.

 

The local metadata cache stores WaterOneFlow service URLs.  Does it need to store just the URL (e.g., http://his.crwr.utexas.edu/twdb_wind/cuahsi_1_0.asmx), or does it need to store the argument to view the WSDL (e.g., http://his.crwr.utexas.edu/twdb_wind/cuahsi_1_0.asmx?WSDL)?  I guess it comes down to, what does the WaterOneFlow client class need?

 

I see that it has been hard coded to always use "WaterOneFlow" as the service name.  This works for anyone who has downloaded the generic ODM WaterOneFlow web service, but if someone has written their own service, then it could be named whatever they want.  I recommend finding a way to figure out the service name on the fly. 

Nov 19, 2009 at 7:43 PM
Edited Nov 19, 2009 at 7:45 PM

When storing URLs in the database, should we store the escaped URL (Uri.AbsoluteUri) (http://hello%20world.com) or the canonical form (Uri.ToString()) (http://hello world.com)?  My suggestion is the canonical form.

 

Nov 19, 2009 at 8:01 PM

Thank you for examining the code or the Metadata Fetcher. I acknowledge that there are some serious problems in the current MetadataFetcher source code.

Regarding your questions:

1. This is a very good suggestion.  I have started working on moving all web service access code to a single HydroDesktop.WebServices.dll package. This package will be a part of the standard installation. Plug-ins will be able to use its functions by adding a reference to HydroDesktop.WebServices.dll. Please see the source code in hydrodesktop\libraries\WebService. So far only the code to parse the GetValues xml has been included in the general purpose package. However, I'd like to have all code working with WaterOneFlow and HIS Central web services moded to that location.

2. The WaterOneFlow client class just needs the URL without the ?WSDL suffix.

3. This is a good suggestion. When the user adds a web service URL, the correct web service name should also be stored. Tim, do you know any easy way to get the web service name when we know the web service URL?

4. I agree with storing the canonical form.

Thanks again for working on this,

Jiri

Nov 19, 2009 at 9:48 PM

1. Sounds great!  Please keep us posted on progress with this.  I'd like to just plug in the code when it's finished, rather than revise/write my own.

2. Ok.  I've modified the metadata fetcher so that it strips off any URL query parameters (the part after and including the "?"). 

3. In HydroExcel, I sort of hack my way there with the following function.  I basically look for text that says "service name=" and then read the name after the text.

Public Function GetServiceName(wsdlUrl As String) As String
  Dim pCaller As New HydroObjects.WebServiceWrapper
  Dim txt As String
  txt = pCaller.DownloadWebASCII(wsdlUrl)
  Dim i As Long, j As Long
  i = InStr(txt, "service name=")
  If i > 0 Then
    j = InStr(i, txt, ">")
    GetServiceName = Mid(txt, i + 14, j - i - 15)
  End If
End Function

There's gotta be a better way of parsing the WSDL than that.  I haven't tried implementing this in code, but I think a good workflow would be to get a list of all service names at the WSDL.  If a service is named WaterOneFlow, then use that.  Otherwise, pick the first service name in the list.  I suppose an even more robust solution is to compare each service to the method signatures we expect from WaterOneFlow, and if the signatures match, then we use that service name.  But that's going to be 500 lines of code just to get the name of the WaterOneFlow service.  Hmmm, then another solution is just to ask the user for the name of the service each time.  Anyways, just brainstorming here...

4. I've modified the metadata fetcher to make sure it stores the canonical form.

Ok, that's all for now.  I anxiously await NHibernate and WaterOneFlowClient updates!!

Nov 25, 2009 at 3:53 PM

Here is the status of the WaterOneFlow client class:

A new class library 'HydroDesktop.WebServices' has been created. It can be found under Source/Libraries/WebService. Right now there are methods for connecting to a WaterOneFlow web service, connecting to HIS Central, calling the GetValues method and calling the HIS Central search methods.

There is also a 'WaterOneFlowParser' class which handles converting a WaterML TimeSeriesResponse xml file to a list of DataSeries with data values. It can handle the case when a single WaterML file contains values with different sources, methods or quality control levels.

The search plug-in now uses the HISCentralClient, WaterOneFlowClient and WaterOneFlow parser from the HydroDesktop.WebService library.

Following tasks still need to be completed:

1. Add method to call theGetSites  web method and convert result to a list of HydroDesktop.DataModel.Site objects

2. Add method to call the GetVariable web method and convert result to a list of HydroDesktop.DataModel.Variable objects

3. Add method to call the GetSiteInfo web method and convert result to a list of HydroDesktop.DataModel.SeriesRecord objects

4. Test if the classes work correctly with different versions of WaterML and with web services returning incomplete WaterML.

 

Dec 4, 2009 at 4:22 PM
Edited Dec 4, 2009 at 4:23 PM

Is the source code for the wateroneflow client in source control?  I couldn't find it.

 

This might be overworking the constructor, but I was wondering if the constructor could do some checkng to see if the URL is actually for a WaterOneFlow service.  Or, a "IsWaterOneFlowService" static method could be added.  I think the method should do the following:

1. Check that the URL is actually for a web service (try to compile an assembly from the ?WSDL). 

2. For all services that are present (I think it's possible that more than one service lives at a given WSDL), check the methods.  If the methods match the WaterOneFlow signatures, then assume this is the WaterOneFlow service at the given URL.  Dave Valentine may have better ideas for checking that this is a WaterOneFlow service.

3. Set a private property of the WaterOneFlow client to be the name of the service that was the WaterOneFlow service.  This way, calling applications don't have to try to figure out the service name, and don't have to assume that the name is "WaterOneFlow".  Currently, I believe our applications are hard-coding "WaterOneFlow" as the service name.

Again, this could either go in the constructor, or as a static method.

I found code at http://www.crowsprogramming.com/archives/66 that can be tweaked to give us service names and also method names and parameters.  I created a Windows form that shows a quick example of how to loop through service names.  I don't see a place to upload files here, so I saved the zip file at ftp://ftp.crwr.utexas.edu/pub/outgoing/Whiteaker/getServiceNameExample.zip.  You can click the link directly to download the file. 

Dec 7, 2009 at 3:39 PM

What's the philosophy for saving WaterML files to disk when they are returned from a web service?  Should any application that calls WaterOneFlow, always save the files to disk?  The all files go in the same place, or should each plugin have its own folder for storing WaterML files?

 

Dec 7, 2009 at 4:58 PM

Tim,

The WaterOneFlow client class is complete. In the constructor I have incorporated checking whether the URI is a valid WaterOneFlow service. Also the service name is now automatically extracted from the WSDL. Thank you for posting the example code.

The source code can be found under: Source/Libraries/DataAccess/HydroDesktop.Data/WebServices

The resulting binary file is HydroDesktop.Data.dll. The assembly HydroDesktop.data dll contains the object model, the data abstraction layer as well as the web service access code.

I'm going to post documentation of the WaterOneFlow client th the HydroDesktop website today. Main functions are:

string GetSitesXML()

string GetSiteInfoXML(string siteCode)

GetValuesXML(string siteCode, string variableCode)

IList<Site> GetSites()

IList<Series> GetSiteInfo(string siteCode)

iList<Series> GetValues(string siteCode, string variableCode)

The functions GetSitesXML, GetSiteInfoXML, GetValuesXML save the downloaded results to a xml file and return the xml file name.

The WaterOneFlow client also has a DownloadDirectory property. This property specifies the directory where downloaded xml files should be saved. By default, this directory is in: [TEMP]/HydroDesktop where [TEMP] is the system temporary files folder. The recommended saving policy for plug-ins is to use the default folder.

 

Dec 7, 2009 at 10:42 PM

Jiri,

Looking good.  I would like to READ the following properties from the service.  Can these be added?

ServiceName

ServiceProtocol

ServiceType

ServiceVersion

 

These seem like things that the object should be able to figure out once it has a asmxUrl and has been constructed.  I need these later on when saving a DataServiceInfo item to the database, and I'd rather let the WaterOneFlow client class tell me what these things are rather than try to figure them out myself.

 

Dec 7, 2009 at 11:00 PM

This is a good suggestion.

I will add a new property ServiceInfo of type DataServiceInfo to the WaterOneFlow client. In the constructor of WaterOneFlow client when the ServiceName is checked, the ServiceProtocol, ServiceType and ServiceVersion will be set.