Sample Python Script¶
Overview¶
The goal of this sample python script is to demonstrate how to use the Machine Analytics API to create a capture and upload a log file. The flow of the script is as follows:
- Authenticate user to gain access to APIs.
- Obtain a modeler type and capture type.
- Create a modeler of type obtained above
- Create a capture and upload log file to backend
- List your builds
- Do some cleanup
Global variables¶
Base url where our data endpoints live. The {} is a place holder
DATA_URL = 'https://data.authentise.com/{}/'
URL for authentication
USERS_URL = 'https://users.authentise.com/sessions/'
Path pointing to file to be uploaded
CAPTURE_PATH = 'modeler_status_1.log'
HTTP requests headers
HEADERS = {
'Content-Type' : 'application/json',
'Accept' : 'application/json',
}
Authentise username and password for authentication
user_data = {
'username' : 'engineering@authentise.com',
'password' : 'password',
}
The attributes of the modeler
MODELER_DATA = {
'ip_address' : '10.0.0.10',
'name' : 'My printer name',
'description' : 'Blue Steel',
'serial_number' : 'ABBA',
}
Function calls¶
- Authenticate
_make_request('POST', USER_URL, user_data)
Send the request to the endpoint so we can authenticate the session and be granted permissions.
- List modeler types to create a modeler
modeler_type, capture_type = _get_capture_type_status_uri(_list_modeler_types())
Get a modeler type and its associated capture_type.
- Create modeler
modeler = _create_modeler(modeler_type)
Create modeler and assigns it to the modeler variable.
- Create a capture and upload log file.
_create_capture(modeler, capture_type)
Use the modeler and capture_type we got above to create a capture and upload the file to the backend.
- List your builds.
builds = _list_builds()
Call the build endpoint so we can see the progress of our build based on the capture upload.
- Clean up.
_delete_modeler(modeler['location'])
Because this is a sample script I used a fake modeler and didn’t want to clutter up the database.
Considerations¶
This script is a sample of how to call the endpoints to create a capture in Python. It is intended as an example for you to build a script that would be suited to your environment.
We’ll be using the python requests
library. It’s a 3rd-party library, but the API should be clear enough to understand well how it works without being familiar with the library specifics
Sample code¶
Python sample script
#!/usr/bin/env python3
import gzip
import pprint
import requests
"""change these global variables for your environment"""
DATA_URL = 'https://data.authentise.com/{}/'
USERS_URL = 'https://users.authentise.com/sessions/'
CAPTURE_PATH = 'modeler_status_1.log'
HEADERS = {
'Content-Type' : 'application/json',
'Accept' : 'application/json',
}
USER_DATA = {
'username' : 'test@authentise.com',
'password' : '*******',
}
session = requests.Session()
def _make_request(method, url, payload=None, headers=HEADERS):
response = session.request(method, url, json=payload, headers=HEADERS)
if not response.ok:
raise Exception("Bad response from {}: {}".format(url, response.json()))
if method == 'POST':
return response.headers
elif method == 'PUT':
return None
elif method == 'DELETE':
return None
else:
return response.json()
The sample code above forms the basis for working with the variou APIs of 3DIAX. All requests we make will include the correct Content-Type
and Accept
header to tell the API we are sending JSON and expect to receive JSON.
The first thing we’ll need to do in our script is to decide which kind of modeler we are going to imitate. To do that we will list the different capture types.
# ...snip...
def _list_capture_types():
return _make_request('GET', DATA_URL.format('capture-type'))
def main():
# authenticate
_make_request('POST', USERS_URL, USER_DATA)
# show the different capture types
pprint.pprint(_list_capture_types())
return
if __name__ == '__main__':
main()
If we run the above script as-is we’ll get back a list of capture-type resources. It looks something like this
{
"resources": [
{
"capture_type": "catex.modeler.status",
"interval": 300,
"uri": "https://data.authentise.com/capture-type/f9fa10dc-3bdb-463f-a6a6-7e6934aeae97/",
"modeler_type": "https://data.authentise.com/modeler-type/1f124472-efd7-441d-b659-c480e5f55e14/"
},
{
"capture_type": "catex.modeler.jobs",
"interval": null,
"uri": "https://data.authentise.com/capture-type/dfa7a7b6-fc30-453e-b516-76b0a9bfe768/",
"modeler_type": "https://data.authentise.com/modeler-type/1f124472-efd7-441d-b659-c480e5f55e14/"
},
{
"capture_type": "connex.modeler.log",
"interval": 300,
"uri": "https://data.authentise.com/capture-type/e428be7e-c42c-4b16-8972-c819ba5d4611/",
"modeler_type": "https://data.authentise.com/modeler-type/85be369e-83a2-4f39-ba7c-90887abc3898/"
}
]
}
For this tutorial we will use catex.modeler.status
- that’s the log file that certain Stratasys FDM machines create at regular intervals during normal operation that tell us the material present, the job they are printing, temperature, etc.
In order to actually send up this log information we need to create a modeler
resource. This represents a printer in the system. In order to create a modeler we need to know what types the system supports
# ...snip...
def _list_modeler_types():
return _make_request('GET', DATA_URL.format('modeler-type'))
def main():
# authenticate
_make_request('POST', USERS_URL, USER_DATA)
# show the different capture types
pprint.pprint(_list_modeler_types())
return
if __name__ == '__main__':
main()
When we run the code above we get the data on the modeler types in the system. It looks something like this
{
"resources": [
{
"description": null,
"name": "Fortus 250",
"uri": "https://data.authentise.com/modeler-type/728d2c92-75b6-4c67-a1e4-3e587c32e375/",
"technology": "FDM",
"manufacturer": "Stratasys",
"capture_types": [
{
"capture_type": "catex.modeler.jobs",
"interval": null,
"uri": "https://data.authentise.com/capture-type/bc53aced-b309-4bdf-8ece-69947f4a9db7/"
},
{
"capture_type": "catex.modeler.status",
"interval": 300,
"uri": "https://data.authentise.com/capture-type/98f40df0-318f-4ac0-87f5-765430d1d127/"
}
]
},
{
"description": null,
"name": "Fortus 400",
"uri": "https://data.authentise.com/modeler-type/1df33a4c-1b96-4098-a44a-5c98c92b018b/",
"technology": "FDM",
"manufacturer": "Stratasys",
"capture_types": [
{
"capture_type": "catex.modeler.jobs",
"interval": null,
"uri": "https://data.authentise.com/capture-type/64251c64-3b2c-4265-a8e8-48599494a902/"
},
{
"capture_type": "catex.modeler.status",
"interval": 300,
"uri": "https://data.authentise.com/capture-type/103691f1-8188-4aad-b43e-a29ef3b726d7/"
}
]
},
{
"description": null,
"name": "Objet 350",
"uri": "https://data.authentise.com/modeler-type/85be369e-83a2-4f39-ba7c-90887abc3898/",
"technology": "Binder Jet",
"manufacturer": "Stratasys",
"capture_types": [
{
"capture_type": "connex.modeler.log",
"interval": 300,
"uri": "https://data.authentise.com/capture-type/e428be7e-c42c-4b16-8972-c819ba5d4611/"
}
]
},
{
"description": null,
"name": "Uprint",
"uri": "https://data.authentise.com/modeler-type/1f124472-efd7-441d-b659-c480e5f55e14/",
"technology": "FDM",
"manufacturer": "Stratasys",
"capture_types": [
{
"capture_type": "catex.modeler.jobs",
"interval": null,
"uri": "https://data.authentise.com/capture-type/dfa7a7b6-fc30-453e-b516-76b0a9bfe768/"
},
{
"capture_type": "catex.modeler.status",
"interval": 300,
"uri": "https://data.authentise.com/capture-type/f9fa10dc-3bdb-463f-a6a6-7e6934aeae97/"
}
]
}
]
}
We can see here that the Fortus 250
supports the capture type we’re interested in. Each of these resources are referenced by URI. We’re going to use the modeler https://data.authentise.com/modeler-type/728d2c92-75b6-4c67-a1e4-3e587c32e375/
and the capture type https://data.authentise.com/capture-type/98f40df0-318f-4ac0-87f5-765430d1d127/
Now we need to create a modeler and a capture for the modeler that uses those URIs
# ... snip ...
def _create_modeler(modeler_type_uri):
MODELER_DATA['type'] = modeler_type_uri
return _make_request('POST', DATA_URL.format('modeler'), MODELER_DATA)
def _delete_modeler(modeler):
_make_request('DELETE', modeler)
def _list_builds():
return _make_request('GET', DATA_URL.format('build'))
def _create_capture(modeler, capture_type):
payload = {
'modeler' : modeler['location'],
'capture_type': capture_type
}
headers = _make_request('POST', DATA_URL.format('capture'), payload)
capture_upload_url = headers["x-upload-location"]
capture_uri = headers["Location"]
return capture_uri, capture_upload_url
def main():
# authenticate
_make_request('POST', USERS_URL, USER_DATA)
modeler_type = 'https://data.authentise.com/modeler-type/728d2c92-75b6-4c67-a1e4-3e587c32e375/'
capture_type = 'https://data.authentise.com/capture-type/98f40df0-318f-4ac0-87f5-765430d1d127/'
# create the mode1ler
modeler = _create_modeler(modeler_type)
pprint.pprint('Created modeler uri:{}'.format(modeler['location']))
# create a capture
capture, upload_url = _create_capture(modeler, capture_type)
print("Created capture {}".format(capture)
We have now created a modeler and created a capture for our modeler. Normally Authentise Echo will send a new capture for each modeler it listens to once every 5 minutes. This is how the system knows that the printer is healthy and keeps track of what the printer is doing. In our example script we have created the capture but we have not uploaded any data. Creating the capture merely creates a location where the data could be uploaded
The capture that gets uploaded has a format that is specific to each printer type. The catex.modeler.status
capture type expects a file that looks like this
set machineStatus(general) {
-modelerStatus "Building"
-modelerExplanation ": "
-startTime "1457952053"
-elapsedBuildTime 12721
-modCount 143
-modelerType "vt2l"
-currentModelingLayer 34
-partCurrentTemp 355
-supportCurrentTemp 241
-envelopeCurrentTemp 100
-partTip "T12"
-supportTip "T12SR100"
-partSetTemp 355
-supportSetTemp 240
-envelopeSetTemp 100
-serverTime 1457964774
-serverTimeZoneOffset 21600
-serverTimeDSTValue 1
-controllerVersion " 3.17.2516.0 "
-compatibleCMBVersion 9.3
};
set machineStatus(cassette) {
{
-spool 0
-name "NYL12"
-status "ACTIVE"
-remainingMatl 25.209132
-mfgDate "05122015"
-mfgLot "6621"
-initialMatl 92.300000
-serialNumber 278929307
}
};
set machineStatus(currentJob) {
-jobName "my-job"
-jobid 123
-owner "Eli Ribble"
-submitTime "1457951706"
-buildTime 12721
-estimatedBuildTime 27474
-startTime "1457952053"
-endTime "1457952053"
-completionStatus "In Progress"
-partTotalMatl 3.1626620
-supportTotalMatl 0.0954441
-partConsumed 1.5615234
-supportConsumed 0.0926666
-partTip "T12"
-supportTip "T12"
-partMatlName "NYL12"
-supportMatlName "SR-110"
-totalLayers 69
-currentLayer 34
-partMinX 11.465531
-partMinY 0.058877
-partMinZ 0.014056
-partMaxX 16.191124
-partMaxY 14.199600
-partMaxZ 0.541973
-comment "Packed file written by Control Center!!!!!Model color: black. "
-producer "10.6 (4739)"
-buildMode ""
-pack "{ sacTower,1140 CLIP, CAMERA FLEX ARM FLEX CLIP BODY*51 }"
};
set machineStatus(queue) {
};
set machineStatus(previousJob) {
-position {-1}
-jobName {}
-jobId 0
-owner {}
-submitTime 0
-estimatedBuildTime 0
-startTime 0
-endTime 0
-buildTime 0
-completionStatus {}
-partMatlName {}
-supportMatlName {}
-partTotalMatl 0
-supportTotalMatl 0
-partConsumed 0
-supportConsumed 0
-partTip T16
-supportTip T16
-totalLayers 0
-currentLayer 0
-partMinX 0
-partMinY 0
-partMinZ 0
-partMaxX 0
-partMaxY 0
-partMaxZ 0
-comment {}
-producer {}
-pack {}
};
You can manipulate that file to set things like the jobName (current set to my-job
) or jobid (currently set to 123
). The system will parse this file and then update information about the printer, including creating build
resources that represent what is being built
Here we’ll show the code for how to upload the file
# ... snip ...
def _create_modeler(modeler_type_uri):
MODELER_DATA['type'] = modeler_type_uri
return _make_request('POST', DATA_URL.format('modeler'), MODELER_DATA)
def _delete_modeler(modeler):
_make_request('DELETE', modeler)
def _list_builds():
return _make_request('GET', DATA_URL.format('build'))
def _create_capture(modeler, capture_type):
payload = {
'modeler' : modeler['location'],
'capture_type': capture_type
}
headers = _make_request('POST', DATA_URL.format('capture'), payload)
capture_upload_url = headers["x-upload-location"]
capture_uri = headers["Location"]
return capture_uri, capture_upload_url
def _upload_capture(capture, upload_url, path):
with open(path, 'rb') as f:
content = gzip.compress(f.read())
response = session.put(
upload_url,
data = content,
headers = {'Content-Type': 'application/x-gzip'},
)
assert response.ok, response.text
def main():
# authenticate
_make_request('POST', USERS_URL, USER_DATA)
modeler_type = 'https://data.authentise.com/modeler-type/728d2c92-75b6-4c67-a1e4-3e587c32e375/'
capture_type = 'https://data.authentise.com/capture-type/98f40df0-318f-4ac0-87f5-765430d1d127/'
# create the mode1ler
modeler = _create_modeler(modeler_type)['location']
pprint.pprint('Created modeler uri:{}'.format(modeler))
# create a capture
capture, upload_url = _create_capture(modeler, capture_type)
# upload the status file
_upload_capture(capture, upload_url, CAPTURE_PATH)
# list builds while we wait for the processing
try:
while True:
builds = _list_builds()
if len(builds['resources']) > 0:
pprint.pprint(builds)
else:
pprint.pprint('no builds')
time.sleep(5)
except KeyboardInterrupt:
pass
# optional clean up
_delete_modeler(modeler['location'])
if __name__ == '__main__':
main()
This will look for a file at CAPTURE_PATH
to upload, read it, gzip it and then upload it to the url given when the capture was created. The capture file will then be processed. While we wait for the processing we loop and list the builds. When the capture is successfully processed we should see a new build in our list. This indicates that the file was processed successfully and 3DIAX has detected the build in our status file
The complete source code for our script is below
#!/usr/bin/env python3
import gzip
import pprint
import json
import requests
import time
"""change these global variables for your environment"""
DATA_URL = 'https://data.authentise.com/{}/'
USERS_URL = 'https://users.authentise.com/sessions/'
CAPTURE_PATH = 'modeler_status_1.log'
HEADERS = {
'Content-Type' : 'application/json',
'Accept' : 'application/json',
}
USER_DATA = {
'username' : 'test@authentise.com',
'password' : '******',
}
session = requests.Session()
def _make_request(method, url, payload=None, headers=HEADERS):
response = session.request(method, url, json=payload, headers=HEADERS)
if not response.ok:
raise Exception("Bad response from {}: {}".format(url, response.json()))
if method == 'POST':
return response.headers
elif method == 'PUT':
return None
elif method == 'DELETE':
return None
else:
return response.json()
def _list_capture_types():
return _make_request('GET', DATA_URL.format('capture-type'))
def _list_modeler_types():
return _make_request('GET', DATA_URL.format('modeler-type'))
def _create_modeler(modeler_type_uri):
payload = {
'description' : 'some fake modeler',
'ip_address' : '127.0.0.1',
'name' : 'test modeler',
'serial_number' : '123',
'type' : modeler_type_uri,
}
return _make_request('POST', DATA_URL.format('modeler'), payload)
def _delete_modeler(modeler):
_make_request('DELETE', modeler)
def _list_builds():
return _make_request('GET', DATA_URL.format('build'))
def _get_capture_type_status_uri(modeler_types):
for capture_types in modeler_types['resources']:
for capture_type in capture_types['capture_types']:
if capture_type['capture_type'] == 'catex.modeler.status':
return capture_types['uri'], capture_type['uri']
def _create_capture(modeler, capture_type):
payload = {
'modeler' : modeler,
'capture_type': capture_type
}
headers = _make_request('POST', DATA_URL.format('capture'), payload)
capture_upload_url = headers["x-upload-location"]
capture_uri = headers["Location"]
return capture_uri, capture_upload_url
def _upload_capture(capture, upload_url, path):
with open(path, 'rb') as f:
content = gzip.compress(f.read())
response = requests.put(
upload_url,
data=content,
headers={"content-type": "application/x-gzip"}
)
assert response.ok, response.text
def main():
# authenticate
_make_request('POST', USERS_URL, USER_DATA)
# show the different capture types
#pprint.pprint(_list_capture_types())
# show the different capture types
#pprint.pprint(_list_modeler_types())
modeler_type = 'https://data.authentise.com/modeler-type/728d2c92-75b6-4c67-a1e4-3e587c32e375/'
capture_type = 'https://data.authentise.com/capture-type/98f40df0-318f-4ac0-87f5-765430d1d127/'
# create the mode1ler
modeler = _create_modeler(modeler_type)['location']
pprint.pprint('Created modeler uri:{}'.format(modeler))
# create a capture
capture, upload_url = _create_capture(modeler, capture_type)
pprint.pprint('Created capture: {} with upload URL {}'.format(capture, upload_url))
# upload the status file
_upload_capture(capture, upload_url, CAPTURE_PATH)
# list builds while we wait for the processing
try:
while True:
builds = _list_builds()
if len(builds['resources']) > 0:
pprint.pprint(builds)
else:
pprint.pprint('no builds')
time.sleep(5)
except KeyboardInterrupt:
pass
# optional clean up
_delete_modeler(modeler)
print('Deleted modeler {}'.format(modeler))
if __name__ == '__main__':
main()