Using the synchronous RDDMS Client
The ETP protocol is designed to be used for long running connections using
websockets.
However, in many cases it is not practical nor necessary to keep a connection
lying around.
Instead we might do a limited set of tasks, e.g., downloading data, and then we
no longer need the connection.
To ease these shorter workflows, we have set up a synchronous RDDMS client —
called RDDMSClientSync — that lets
the user call the main functionality from
RDDMSClient without having to manually set up
and close the connection.
Furthermore, this also avoids the need for using async and await.
Info
Even though RDDMSClientSync is made synchronous, the methods wrap the
concurrent RDDMSClient.
As such the "heavy lifting" of the methods are still executed concurrently.
In this tutorial we will repeat what is done in the previous tutorial "Using the RDDMS Client" to make it easier to compare the two clients.
See "Accessing the ETP
server" for instructions
on how to use the local open-etp-server or get an access token to a published
instance.
We will in this tutorial also work towards the local server.
Connection towards the server is handled in the background by the
RDDMSClientSync, and there is no need
to use a context manager or similar as for the concurrent
RDDMSClient.
Token expiration
The RDDMSClientSync stores the
access parameters towards the ETP server in the class, and only use them
when one of its methods is called to establish a connection.
If the client is used in a long running program, e.g., in a notebook where
it is set up once and then called much later, the token might expire and
the client will then be unable to connect to the server.
In this case you should fetch a new token, and then re-create the client
using the non-expired token.
Uploading a regular surface to the ETP server using the synchronous client
This tutorial is more or less a copy of the tutorial "Uploading a regular surface to the ETP server". We will therefore make it more brief and avoid any terminal output.
The regular surface is set up in exactly the same way as in the tutorial or the concurrent client:
import numpy as np
from rddms_io import RDDMSClientSync
import resqml_objects.v201 as ro
z = np.random.random((101, 103))
origin = np.array([10.0, 11.0])
spacing = np.array([1.0, 0.9])
u1 = np.array([np.sqrt(3.0) / 2.0, 0.5])
u2 = np.array([-0.5, np.sqrt(3.0) / 2.0])
originator = "<name/username/email>"
epc = ro.obj_EpcExternalPartReference(
citation=ro.Citation(title="Demo epc", originator=originator)
)
crs = ro.obj_LocalDepth3dCrs(
citation=ro.Citation(
title="Demo crs",
originator=originator,
),
vertical_crs=ro.VerticalCrsEpsgCode(epsg_code=6230),
projected_crs=ro.ProjectedCrsEpsgCode(epsg_code=23031),
)
gri = ro.obj_Grid2dRepresentation.from_regular_surface(
citation=ro.Citation(
title="Demo grid",
originator=originator,
),
crs=crs,
epc_external_part_reference=epc,
shape=z.shape,
origin=origin,
spacing=spacing,
unit_vec_1=u1,
unit_vec_2=u2,
)
rddms_connect from rddms_io we instead
import RDDMSClientSync from
rddms_io directly.
Notably, we also do not import asyncio.
As we do not need use async and await for the RDDMSClientSync we
therefore avoid wrapping the script in an asynchronous function.
Setting up the client
The connection parameters are given by:
uri = "ws://localhost:9100"
data_partition_id = None
access_token = None
dataspace_path = "rddms_io/sync-demo"
rddms_client = RDDMSClientSync(
uri=uri, data_partition_id=data_partition_id, authorization=access_token
)
rddms_connect, but no connection is
established yet.
Connecting and creating a dataspace
We can now call
RDDMSClientSync.create_dataspace
to set up a new dataspace.
The parameters are identical to the ones in the concurrent counterpart,
RDDMSClient.create_dataspace.
rddms_client.create_dataspace(
dataspace_path,
legal_tags=["legal-tag-1", "legal-tag-2"],
other_relevant_data_countries=["country-code"],
owners=["owners"],
viewers=["viewers-1", "viewers-2"],
ignore_if_exists=True,
)
Inner workings of RDDMSClientSync
When a method is called on RDDMSClientSync will set up a context manager
via rddms_connect to establish a
connection, then call the equally named method on
RDDMSClient, and finally close the
connection when leaving the context manager.
The pattern goes like this:
Uploading the surface
We upload the surface using
RDDMSClientSync.upload_model
similarly to how it is done in the previous
tutorial.
rddms_client.upload_model(
dataspace_path,
ml_objects=[epc, crs, gri],
data_arrays={
gri.grid2d_patch.geometry.points.zvalues.values.path_in_hdf_file: z,
},
)
Searching on the ETP server
We can search using RDDMSClientSync
in the same way we did the concurrent client.
dataspaces = rddms_client.list_dataspaces()
gri_resources = rddms_client.list_objects_under_dataspace(
dataspace_path, data_object_types=[ro.obj_Grid2dRepresentation]
)
gri_lo = rddms_client.list_linked_objects(start_uri=gri_resources[0].uri)
Downloading the surface
We download the surface using
RDDMSClientSync.download_models.
ret_models = rddms_client.download_models(
ml_uris=[gri_lo.start_uri],
download_arrays=True,
download_linked_objects=True,
)
gri_lo.start_uri) in the
RDDMSClientSync.download_models-call, we get a list containing a single
RDDMSModel in return.
The obj_Grid2dRepresentation-object is then found via:
Since we used the flags download_arrays=True and
download_linked_objects=True, the fields RDDMSModel.arrays and
RDDMSModel.linked_models (respectively) will also be populated (if there are
any linked objects and arrays).
Our uploaded obj_Grid2dRepresentation only links to a
obj_LocalDepth3dCrs-object (the obj_EpcExternalPartReference-objects are
excluded from being added to the RDDMSModel.linked_models), and to get it we
run:
The array is found using the path_in_hdf_file from the grid-object:
Delete objects and dataspaces
Here as in the previous tutorial we end by deleting all the objects and then delete the dataspace.
all_resources = rddms_client.list_objects_under_dataspace(dataspace_path)
rddms_client.delete_model(ml_uris=[a.uri for a in all_resources])
rddms_client.delete_dataspace(dataspace_path)
Full script
Finally, we list the full script used throughout this tutorial.
Here as well there are a few extra assert-statements that were not
included in the examples above, but are kept to ensure that the tutorial
example is kept up to date.
import numpy as np
from rddms_io import RDDMSClientSync
import resqml_objects.v201 as ro
z = np.random.random((101, 103))
origin = np.array([10.0, 11.0])
spacing = np.array([1.0, 0.9])
u1 = np.array([np.sqrt(3.0) / 2.0, 0.5])
u2 = np.array([-0.5, np.sqrt(3.0) / 2.0])
originator = "<name/username/email>"
epc = ro.obj_EpcExternalPartReference(
citation=ro.Citation(title="Demo epc", originator=originator)
)
crs = ro.obj_LocalDepth3dCrs(
citation=ro.Citation(
title="Demo crs",
originator=originator,
),
vertical_crs=ro.VerticalCrsEpsgCode(epsg_code=6230),
projected_crs=ro.ProjectedCrsEpsgCode(epsg_code=23031),
)
gri = ro.obj_Grid2dRepresentation.from_regular_surface(
citation=ro.Citation(
title="Demo grid",
originator=originator,
),
crs=crs,
epc_external_part_reference=epc,
shape=z.shape,
origin=origin,
spacing=spacing,
unit_vec_1=u1,
unit_vec_2=u2,
)
uri = "ws://localhost:9100"
data_partition_id = None
access_token = None
dataspace_path = "rddms_io/sync-demo"
rddms_client = RDDMSClientSync(
uri=uri, data_partition_id=data_partition_id, authorization=access_token
)
rddms_client.create_dataspace(
dataspace_path,
legal_tags=["legal-tag-1", "legal-tag-2"],
other_relevant_data_countries=["country-code"],
owners=["owners"],
viewers=["viewers-1", "viewers-2"],
ignore_if_exists=True,
)
rddms_client.upload_model(
dataspace_path,
ml_objects=[epc, crs, gri],
data_arrays={
gri.grid2d_patch.geometry.points.zvalues.values.path_in_hdf_file: z,
},
)
dataspaces = rddms_client.list_dataspaces()
gri_resources = rddms_client.list_objects_under_dataspace(
dataspace_path, data_object_types=[ro.obj_Grid2dRepresentation]
)
gri_lo = rddms_client.list_linked_objects(start_uri=gri_resources[0].uri)
ret_models = rddms_client.download_models(
ml_uris=[gri_lo.start_uri],
download_arrays=True,
download_linked_objects=True,
)
assert len(ret_models) == 1
ret_model = ret_models[0]
ret_gri = ret_model.obj
assert len(ret_model.linked_models) == 1
ret_crs = ret_model.linked_models[0].obj
ret_z = ret_model.arrays[
ret_gri.grid2d_patch.geometry.points.zvalues.values.path_in_hdf_file
]
assert ret_gri == gri
assert ret_crs == crs
np.testing.assert_equal(z, ret_z)
all_resources = rddms_client.list_objects_under_dataspace(dataspace_path)
rddms_client.delete_model(ml_uris=[a.uri for a in all_resources])
rddms_client.delete_dataspace(dataspace_path)