Running BlueSky through Web Services
How to run BlueSky on our servers using web services calls.
Overview
Since version 3.1, the BlueSky Framework now has built-in support for remote access using web services. The framework provides a consistent interface to the models distributed with BlueSky. Currently, this interface is accessible using the XML-RPC protocol. Other protocols may be supported in the future.
Steps of a BlueSky web services call
Essentially, calling a BlueSky model using web services is a request-response process. Your computer (known as the client) constructs an XML-RPC message, called a request. The request contains the name of the model you want to call, and information about a fire or fires of interest. The client sends the request to the BlueSky server, using the HTTP protocol (the same network protocol that web browsers use to communicate with web servers). At the server, the BlueSky Framework unpacks the request and builds a FireInformation data structure. This data structure is a software object that contains everything BlueSky knows about a given fire or set of fires. Next, the framework uses the FireInformation structure to initialize a BlueSky module for the model you have selected. The BlueSky module uses information from the FireInformation structure to set up a model run (e.g. by creating model configuration files, constructing data arrays, etc.) and then runs the model. Then the BlueSky module takes the output from the model and uses it to update the FireInformation structure. The BlueSky server repacks the FireInformation data structure into an XML-RPC message, called the response. BlueSky returns this response back to the client. The client (your computer) then unpacks this response and uses the results.
A program or script that uses a BlueSky web service will generally need to follow these steps:
- Collect information about the fire(s)
- Build a local data structure that resembles the FireInformation object
- Use an XML-RPC library to translate your local data structure into an XML-RPC request
- Use an XML-RPC or HTTP library to send your XML-RPC request to the BlueSky server and get a response back
- Use an XML-RPC library to translate the XML-RPC response back into a local data structure
- Read the results from the local data structure
The format of the call
The actual "wire protocol" or format of data passed between the client and server are described by the XML-RPC specification (BlueSky also uses the <nil/> value extension).
The format of the data structure is actually determined by the wrapper module, not by the Framework itself. However, nearly all web service calls will take a single FireInformation object as their only parameter. For nodes that require more than one input structure (that is, for nodes that have more than one input "plug" when connected in a BlueSky graph), you must pass a structure for each of its inputs.
The structure of the FireInformation object and its sub-objects is described by the $BS_DIR/base/etc/types.ini file. The BlueSky Framework will attempt to infer the correct structure if it is simply given a flat mapping of field names to values. For example, if the input is a structure that just contains
{"latitude": 45.46, "longitude": -114.961, "date_time": "20090728", "area": 100}
then the framework will infer the structure:
/*FireInformation*/ {
"fire_locations": /*array*/ [
/*FireLocationData*/ {
"id": "UNKNOWN",
"owner": null,
"latitude": 45.46,
"longitude": -114.961,
"fips": null,
"elevation": null,
"slope": null,
"state": null,
"county": null,
"country": null,
"date_time": "200907280000+00:00",
"duration": null,
"snow_month": null,
"rain_days": null,
"wind": null,
"fuel_moisture_10hr": null,
"fuel_moisture_1khr": null,
"type": null,
"scc": null,
"area": 100.0,
"time_profile": null,
"fuels": null,
"consumption": null,
"emissions": null,
"plume_rise": null,
"metadata": /*struct*/ { }
}
],
"fire_events": /*array*/ [ ],
"dispersion": null,
"start_date": "200907280000+00:00",
"hours_to_run": 24,
"emissions_offset": 0,
"dispersion_offset": 0,
"emissions_start": "200907280000+00:00",
"emissions_end": "200907290000+00:00",
"dispersion_start": "200907280000+00:00",
"dispersion_end": "200907290000+00:00",
"metadata": /*struct*/ { }
}
Note that the structure has been expanded into a FireInformation object, containing a "fire_locations" list, which contains a FireLocationData object, which in turn contains the values passed in. A few other values (mostly date/time data) are given reasonable default values, but most values not explicitly provided in the input are inferred to be null.
Expected output
Since BlueSky Framework nodes can have more than one output structure, the return value from the XML-RPC call will always be a <struct> element. This <struct> will contain an entry for each output from the node, indexed by name.
For example, if the above structure is passed into the "FCCS" fuel-loading model, using the BlueSky web service, the response structure will contain a single element named "fires". The "fires" structure will look like this:
/*FireInformation*/ {
"fire_locations": /*array*/ [
/*FireLocationData*/ {
"id": "UNKNOWN",
"owner": null,
"latitude": 45.46,
"longitude": -114.961,
"fips": null,
"elevation": null,
"slope": null,
"state": null,
"county": null,
"country": null,
"date_time": "200907280000+00:00",
"duration": null,
"snow_month": null,
"rain_days": null,
"wind": null,
"fuel_moisture_10hr": null,
"fuel_moisture_1khr": null,
"type": null,
"scc": null,
"area": 100.0,
"time_profile": null,
"fuels": /*FuelsData*/ {
"fuel_1hr": 0.4,
"fuel_10hr": 2.2,
"fuel_100hr": 2.8,
"fuel_1khr": 8.3,
"fuel_10khr": 0.7,
"fuel_gt10khr": 0.5,
"shrub": 0.0,
"grass": 0.14,
"rot": 1.0,
"duff": 1.92,
"litter": 1.0,
"canopy": 4.16,
"metadata": /*struct*/ {
"VEG": "Lodgepole pine forest",
"fccs_number": 22
}
},
"consumption": null,
"emissions": null,
"plume_rise": null,
"metadata": /*struct*/ { }
},
],
"fire_events": /*array*/ [ ],
"dispersion": null,
"start_date": "200907280000+00:00",
"hours_to_run": 24,
"emissions_offset": 0,
"dispersion_offset": 0,
"emissions_start": "200907280000+00:00",
"emissions_end": "200907290000+00:00",
"dispersion_start": "200907280000+00:00",
"dispersion_end": "200907290000+00:00",
"metadata": /*struct*/ { }
}
Example scripts
Here is an example XML-RPC client written in Python. This is a fully-functional utility script that takes latitude and longitude on the command line, queries the FCCS fuel loading module, and writes the output fuelbed information to the screen:
#!/usr/bin/env python
import os
import sys
import xmlrpclib
import datetime
WEB_SERVICE_URL = "http://blueskyweb.sonomatech.com/xml-rpc.py"
def main():
# Parse command line arguments
try:
latitude, longitude = sys.argv[1:]
except ValueError:
print "Usage: %s <latitude> <longitude>" % os.path.basename(sys.argv[0])
sys.exit(1)
# Construct request structure
requestData = {
"latitude": float(latitude),
"longitude": float(longitude),
"area": 100.0,
"date_time": datetime.date.today().strftime("%Y%m%d")
}
# Construct XML-RPC proxy
bluesky = xmlrpclib.ServerProxy(WEB_SERVICE_URL, allow_none=True)
# Send request, and retrive response
print "Sending request to %s..." % WEB_SERVICE_URL
responseData = bluesky.FCCS(requestData)
# Extract FireInformation from response
fires = responseData["fires"]
# Write output
print "Got response, %d fires returned:" % len(fires["fire_locations"])
for i, fireLoc in enumerate(fires["fire_locations"]):
print
print "Fire #%d: %s" % (i + 1, fireLoc["id"])
print " Latitude: %1.5f" % fireLoc["latitude"]
print " Longitude: %1.5f" % fireLoc["longitude"]
if fireLoc["fuels"]:
print
if "VEG" in fireLoc["fuels"]["metadata"]:
print " Vegetation type:", fireLoc["fuels"]["metadata"]["VEG"]
print " 1-hr fuels: %1.2f" % fireLoc["fuels"]["fuel_1hr"]
print " 10-hr fuels: %1.2f" % fireLoc["fuels"]["fuel_10hr"]
print " 100-hr fuels: %1.2f" % fireLoc["fuels"]["fuel_100hr"]
print " 1,000-hr fuels: %1.2f" % fireLoc["fuels"]["fuel_1khr"]
print " 10,000-hr fuels: %1.2f" % fireLoc["fuels"]["fuel_10khr"]
print " >10,000-hr fuels: %1.2f" % fireLoc["fuels"]["fuel_gt10khr"]
print " Duff: %1.2f" % fireLoc["fuels"]["duff"]
print " Grass: %1.2f" % fireLoc["fuels"]["grass"]
print " Litter: %1.2f" % fireLoc["fuels"]["litter"]
print " Rotten fuels: %1.2f" % fireLoc["fuels"]["rot"]
print " Canopy: %1.2f" % fireLoc["fuels"]["canopy"]
if __name__ == '__main__':
main()
Creating a web page tool that uses web-services
An example JavaScript implementation is available at http://blueskyweb.sonomatech.com/webservicetest.html.
This uses the JavaScript XML-RPC library from http://phpxmlrpc.sourceforge.net/jsxmlrpc/. (I made a small change to the library to add support for the <nil/> value extension to the XML-RPC spec.) A copy of this library, with the patch applied, is also distributed with the BlueSky Framework, under $BS_DIR/base/wwwroot/xmlrpc_lib.js.
BlueSky Playground
The BlueSky Playground is a tool for exploring the effects of different combinations of models. It uses XML-RPC web services to communicate with the BlueSky Framework, enabling users to run models on demand, as well as compare results from different models.



