japc-rest is a RESTful service that allows accessing
FESA or
DeviceAccess devices, platform and programming language independant, over JAPC.
The base URL
The base URL to access the resources in the production environment is
https://asl157.acc.gsi.de/japc-client/v1/
API documentation is available via
https://asl157.acc.gsi.de/japc-client/v1/_api
Note that the sections below are at least partially outdated
The WADL describes the service
With
https://restpro00a.acc.gsi.de/japc/client/v1/application.wadl we get the list of available resources under the base URL.
WADL is for
REST what
WSDL for
SOAP is: It describe the resource, its path parameters with their respective names and types, its methods with their respective types (GET, PUT, ..) among other things.
The following code-snippet shows the resource
https://restpro00a.acc.gsi.de/japc/client/v1//{device}/{property}.
...
<resource path="/{device}/{property}">
<param name="property" style="template" type="xs:string"/>
<param name="device" style="template" type="xs:string"/>
<method id="setParameterValue" name="PUT">
<request>
<param name="selector" style="query" type="xs:string"/>
<representation mediaType="application/json"/>
</request>
<response>
<representation mediaType="application/json"/>
</response>
</method>
<method id="getParameterValue" name="GET">
<request>
<param name="selector" style="query" type="xs:string"/>
</request>
<response>
<representation mediaType="application/json"/>
</response>
</method>
</resource>
It shows the path of the resource, which must be added to the basic URL of the service, in this case it is composed of the device name followed by the property name.
The device and properies name are the 2 path parameters of the resource. Both path parameters are of type string.
It's abvious that the resource has 2 methods, one for PUT and another for GET operartions. Furthermore the GET request as well as the PUT request may have a query parameter with the name selector of type string.
The response element shows that both request produce and send back a
json object to the client.
The
json object the PUT operation returns to the client has the following simple structure:
{
"code": number,
"message": string
}
The value for code represents the
HTTP status code and the optional message tries to explain the result of the request.
The response of the GET operation returns a complex JSON object: To keep it simple, a property with a unique field is shown in the following example.
{
"deviceName": "GSITemplateTestvmla017",
"propertyName": "Power",
"fieldName":"power"
"valueMap": {
"power: {
"type": "int";
"value": 1
}
}
}
Synchronous and Asynchronous Resources
For GET as well as for PUT operations japc-rest provides synchronous resources and there conterpart ashynchronous ones.
Synchronous resources
A synchronous resource is a resource that processes a request synchronously. How the client procedes is another story.
When a request is received, an I/O thread from a pool of such threads, available at server side, is dedicated to accept and process the request. The thread blocks until the request is finished and returned. As the number of available I/O threads is limited, the situation in which no more threads are available to accept and process any new request may take place when the number of requests is high and processing is time consuming.
Asynchronous Resources
An asynchronous resource processes a request quite differently: a first thread (acceptor) is dedicated to accept a request, the processing is done by another thread (worker). The accepor-thread accepts a request hands it over to the worker and is immediately released and returned to the pool, so that it can accept another potential connection. It's of course the worker-thread that sends back the response to the client.
Reading the Fields' Values of a Property of FESA and DeviceAccess devices
To read the values of a property's fields or the value of a single field of
FESA and DeviceAccess devices, japc-rest provides resources.
Reading the Fields' Values of a Device's Property
A
FESA or
DeviceAccess device has one or more properties and a property has one or more fields. To get the values of the fields of a device's property we use the resource:
/{device}/{property}.
For the device
GSITemplateTestvmla017 and its property
Power , the GET request will be:
https://restpro00a.acc.gsi.de/japc/client/v1/GSITemplateTestvmla017/Power
The returned Json-object is:
{
"deviceName":"GSITemplateTestvmla017",
"propertyName":"Power",
"valueMap":{
"power":{
"type":"int",
"value":1
}
}
}
Reading the Value of a single Field of a Device's Paramter
To get just the value of a unique field japc-rest provides the resource
/{device}/{property}/{field}.
To keep things simple, let's use the same device
GSITemplateTestvmla017 and the same property
Power. This property has a unique field
power, but even if it had many more fields, we would use the same resource. The GET request is:
https://restpro00a.acc.gsi.de/japc/client/v1/GSITemplateTestvmla017/Power/power
The returned
json object is:
{
"deviceName":"GSITemplateTestvmla017",
"propertyName":"Power",
"fieldName":"power",
"valueMap":{
"power":{
"type":"int",
"value":1
}
}
}
Setting the Fields' Values of Property of FESA and DeviceAccess devices
japc-rest provides also resources to set the values of property's fields.
The Requirement of the Correct Value of a Query Property
Setting the values of a property must be done with caucious, that is why the requester must provide the correct value of a query parameter named
token to filfull this operation.
For this type of operations the client-IP, the device name, the property name and the field name are always logged.
Setting the Value of a single Field of a Device's Property
To set the value of a sole field japc-rest provides the resource
/{device}/{property}/{field}.
Again to keep things simple, let's use the same device
GSITemplateTestvmla017 and the same property
Power. The unique field
power, requies an integer value and is defined in
FESA as:
<enum name="DEVICE_POWER" id="xxxxxxxxxxx">
<!--An enumeration Type used to control the operational mode of the device. -->
<!--Its values are a subset of those in the DEVICE_POWER_STATE type -->
<item access="RW" meaning="ON" symbol="ON" value="1" id="_181009120017_3" />
<!--The device is in fully operational state -->
<item access="RW" meaning="OFF" symbol="OFF" value="2" id="_181009120018_0" />
<!--The device is turned off -->
</enum>
The correct values for this field are thus 1 for "ON" or 2 for "OFF".
The PUT request is:
https://restpro00a.acc.gsi.de/japc/client/v1/GSITemplateTestvmla017/Power/power
and the Json-Object to provide as payload in the body of the request using
POSTMAN or any other REST Client Browser-addon like
RESTClient is:
{
"deviceName":"GSITemplateTestvmla017",
"propertyName": "Power",
"valueMap":{
"power":{
"type":"int",
"value":2
}
}
}
For this request without the famous
token query parameter, the returned
json object is:
{
"code": 400,
"message": "No value provided for token."
}
For the same PUT request with the correct Value for token:
https://restpro00a.acc.gsi.de/japc/client/v1/GSITemplateTestvmla017/Power/power?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx we get a response with an empty body, the Status 204 and the message that "The server successfully processed the request, but is not returning any content".
Setting the Fields' Values of a Device's Property
For a device's property with 2 or more fields, the same PUT-request with the
token query parameter can be use to set the values of these fields:
{
"deviceName":"a-device-name",
"propertyName": "a-property-name",
"valueMap":{
"field-1":{
"type":"type-of-field-1",
"value":"any-value-of-the-type-above"
},
"field-2":{
"type":"type-of-field-2",
"value":"any-value-of-the-type-above"
},
.....
"field-N":{
"type":"type-of-field-N",
"value":"any-value-of-the-type-above"
}
}
}
The element "type" can be:
- one of the primitive types: byte, boolean, char, decimal, double, float, int/integer, long, short, String.
- A simple array of one of the primitive types: byte[], boolean[], char[], decimal[], double[], float[], int[], long[], short[], String[].
- A 2-D array: byte[][], boolean[][], char[][], decimal[][], double[][], float[][], int[][], long[][], short[][], String[][].
A Java Client to Read and Set the Fields' Values of a Device's Property
A Java client desktop application japc-rest-client-api has been written to access and use japc-rest.
Using Python to Read and Set the Fields' Values of a Device's Property
It's of course possible to access and use japc-rest from any program, or script written in any programming-language.
As Python is an importing langauge, we will present simple scripts to read and to write the fields' values of a device's property.
A Python-Script to Read the Fields' Values of a Device's Property
With the simple python script listed below we get the value of the unique field
power of property
Power of the device
GSITemplateTestvmla017.
#!/bin/python
# tested with python version 3.6.6 on Windows 7 Entreprise
import requests
import json
deviceName='GSITemplateTestvmla017'
propertyName='Power'
headers = {'content-type': 'application/json'}
# the url for request
url = 'https://restpro00a.acc.gsi.de/japc/client/v1/' + deviceName + '/' + propertyName
# send a get request
response = requests.get(url, headers=headers)
print ("returned status-code: " + str(response.status_code))
if response.status_code == 200:
print("The GET request was successful")
else:
# Something went wrong
print("The GET request was unsuccessful")
# just print the returned Json-Object
print(response.text)
The script has been written and tested with the 3.6.6 version of WPython. To use this script for another device, the values for
deviceName,
propertyName must be of course set accordingly.
It must be clear for the script user which properties a certain device has and which fields a certain property has. Furthermore it must be clear for him/her which type the value of each field has and therefore he/she must know in advance what for a structure the returned
json object has.
.. (to be continued)
A Python-Script for a PUT
The python script below shows how to set the fields of the property
Power of the device
GSITemplateTestvmla017. In this case again the property has a the unique field
power but the same script is suitable to set the values of a property's fields,
#!/bin/python
# tested with python version 3.6.6 on Windows 7 Entreprise
import requests
import json
deviceName='GSITemplateTestvmla017'
propertyName='Power'
headers = {'content-type': 'application/json'}
# the url for request
url1 = 'https://restpro00a.acc.gsi.de/japc/client/v1/'+devicename+'/'+propertyName + '?token=Zi5sYWZyaXRlOmdzaS1mYWlyLWdlaGVpbQ=='
payload = {
"deviceName":"GSITemplateTestvmla017",
"propertyName": "Power",
"valueMap":{
"power":{
"type":"int",
"value":2
}
}
}
print("Trying to set the property '" + propertyName + "' (url " + str(url1) + ")")
# a put request must be sent to set the parameter's Fileds' values.
response = requests.put(url1, json=payload, headers=headers)
if response.status_code != 204:
# Something went wrong
print("The PUT request was unsuccessful")
print ("returned status-code: " + str(response.status_code))
print(response.text)
# raise requests.HTTPError(response.text)
else:
print("The PUT request was successful")
print ("returned status-code: " + str(response.status_code))
.. (to be continued)
Returned Messages By Errors
If we execute the following script, where the propertyName in the payload json object is other than the one of the path-parameter:
#!/bin/python
# tested with python version 3.6.6 on Windows 7 Entreprise
import requests
import json
deviceName='GSITemplateTestvmla017'
propertyName='Setting'
headers = {'content-type': 'application/json'}
# the url for request
url1 = 'https://restpro00a.acc.gsi.de/japc/client/v1/'+devicename+'/'+propertyName + '?token=Zi5sYWZyaXRlOmdzaS1mYWlyLWdlaGVpbQ=='
payload = {
"deviceName":"GSITemplateTestvmla017",
"propertyName": "Power",
"valueMap":{
"power":{
"type":"int",
"value":2
}
}
}
....
We get the following error:
The PUT request was unsuccessful
returned status-code: 400
{
"code":400,
"message":"Invalid Request: The value 'Power' provided for property-name in the
json object and the value 'Setting' of the path-parameter 'property' must be identical."
}
The property
Power of
GSITemplateTestvmla017 has a sole field
power that requies an integer value: "1" for "ON" and "2" for "OFF".
Trying to set its value to "3" for example, we get the following error-message:
The PUT request was unsuccessful
returned status-code: 404
{
"code":404,"message":"FESA_12005 Value item 'power': invalid data; nested error message: FESA_10009 Invalid value: '3' is invalid regarding type '::GSITemplate::DEVICE_POWER::DEVICE_POWER'..
generated/cpp/GSITemplate/GeneratedCode/TypeDefinition.h:433. /opt/fesa/fesa-fwk/5.0.1/include/fesa-core/Server/ServerData.h:435"
}
Trying to set the value of the field
power to a boolean
payload = {
"deviceName":"GSITemplateTestvmla017",
"propertyName": "Power",
"valueMap":{
"power":{
"type":"boolean",
"value":True
}
}
....
The PUT request was unsuccessful
returned status-code: 404
{"code":404,"message":"FESA_12007 Value item 'power': invalid type 'bool', 'int32_t' is expected. src/fesa-core/Server/ServerData.cpp:259"}
Feedbacks and Questions
If you have any question, please don't hesitate to contact
me,
Your feedback is highly appreciated: it will be considered for further enhancement.