QBOID Confidential and Proprietary Information
The QBOID equipment that is generating the sizing record. Can be a Perceptor M2 or S1.
The machine that is receiving and consuming the sizing record.
The Device will send a POST
request with a JSON
body to the HTTP
endpoint defined in its configuration expecting a 200-299
HTTP response code.
The typical JSON
body is like the following:
{ "timestamp": "2021\/02\/14 07:19:38", "l": 195, "w": 165, "h": 72, "weight": 7709, "barcode": "845973030506", "shape": "Cuboidal", "device": "639b707f", "note": "", "attributes": { "hazmat": "true" } }
field | format | Description |
---|---|---|
timestamp |
string yyyy/MM/dd HH:mm:ss |
UTC timestamp using the Device time. Hour format is (0-23). |
l |
int |
Length of the item in mm. Length is defined as the biggest among the 2 measurements parallel to the ground plane. |
w |
int |
Width of the item in mm. Width is defined as the smallest among the 2 measurements parallel to the ground plane. |
h |
int |
Height of the item in mm. Height is defined as the measurement orthogonal to the ground plane. |
weight |
int (Optional) |
Weight of the item in g (grams). |
barcode |
string |
|
shape |
string |
Currently supported values are Irregular and Cuboidal . Additional shapes will be supported in the future. |
device |
string |
Device serial number. Same as shown in Settings->System->About Phone->Status->Serial number . |
note |
string |
Generic alphanumeric note screen as added by the user. |
attributes |
map(string->string) {} when empty |
Contains the values assigned using the attributes buttons. Attribute buttons are fully customizable any arbitrary combination of key/value may be present. |
image |
string PNG/JPEG base64 image (optional) |
Full resolution color image with bounding box drawn on top. Can be enabled/disabled in settings. The default image format is PNG , while JPEG can be selected in settings. |
imagecolor |
string PNG/JPEG base64 image (optional) |
Full resolution color image without bounding box drawn on top. Can be enabled/disabled in settings. The default image format is PNG , while JPEG can be selected in settings. |
imageseg |
string PNG base64 image (optional) |
Object Image with background subtracted. Pixel that belong to the background have the alpha channel set to 0 .JPEG format is not supported because of the alpha channel requirement. |
Optional fields, image format and endpoint can be configured in the M2
app in the section: Settings > Data Settings > WiFi API
.
Entry | Description |
---|---|
API endpoint |
Define the URL for the HTTP/HTTPS endpoint. |
Upload result image |
Enable/Disable the image field. |
Upload color image |
Enable/Disable the imagecolor field. |
Upload segmented image |
Enable/Disable the imageseg field. |
Use JPEG format |
Select JPEG format for image and imagecolor fields. |
String fields would be escaped as per JSON standard, more specifically:
input | output |
---|---|
New Line |
\n |
\ |
\\ |
/ |
\/ |
Carriage return |
\r |
Tab |
\t |
Images are encoded in the JSON request as a Data URI scheme with a base64 encoding adhering RFC-2045.
Following is an example of an encoded image:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAHklEQVR42mP8z8AARNQDjKMGjho4auCogaMGjlQDAUwCJ+0NBcXlAAAAAElFTkSuQmCC Note: Previous string can be copied and pasted to browser address bar to visualize the image.
HTTP request is configured as following
Property | Value |
---|---|
Request Method |
POST |
Content-Type |
application/json; utf-8 |
Accept |
application/json |
A response code in the range 200-299
is expected as a response.
POST /newEntry HTTP/1.1 Content-Type: application/json; utf-8 Accept: application/json User-Agent: Dalvik/2.1.0 (Linux; U; Android 8.1.0; Scorpio Build/V008R001) Host: 192.168.7.103:5000 Connection: Keep-Alive Accept-Encoding: gzip Content-Length: 199 {"timestamp":"2021\/02\/25 02:16:25","l":266,"w":203,"h":72,"weight":6807,"barcode":"X001SS387V","shape":"Irregular","device":"639b707f","note":"QWERTY","attributes":{"batt":"false","hazmat":"true"}}
Added in 2.1.0
HTTP over TLS
can be used simply defining https
as protocol in the URL of the endpoint.
If the server's SSL
certificate was issued by a well known CA
, no other operation is required.
A self-signed certificate is one that is signed with the servers own private key instead of being signed by a CA.
If the server is using a self-signed certificate, adding the certificate to the Perceptor M2 (see link) will be necessary. Failure to do so will throw the following error when the M2 is attempting to communicate with the server:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
Following is an example on how to create a self-signed certificate using OpenSSL command line tool
openssl req -x509 -newkey rsa:4096 -keyout self_key.pem -out self_cert.pem -sha256 -days 365 -nodes \ -subj "/C=US/ST=California/L=San Jose/O=Qboid/OU=TestingWebService/CN=testing.qboid.ai/emailAddress=info@qboid.ai" \ -addext "subjectAltName=DNS:testing.qboid.ai"
self_cert.pem
) file to M2 using the M2's Web Browser app, or from a PC over the M2's USB-C port.Settings->Security->Encryption & Credential->Install from storage
Certificate Name
VPN and apps
is selected as Credential use
QBOID provides a testing www server that can be used for experimenting with the different API configurations/protocols.
The test server can be reached through a reverse proxy that's exposing the same service with different combinations of protocols and certificates. All endpoints refers to the same service, so data pushed over https
will be visible on the http
page and vice versa.
Before using the server, please review the following:
Protocol | Web Visualization URL | M2 Endpoint |
---|---|---|
HTTP | http://testing.qboid.ai:5001/ | http://testing.qboid.ai:5001/newEntry |
HTTPS (self signed) | https://testing.qboid.ai:5002/ | https://testing.qboid.ai:5002/newEntry |
HTTPS (public CA) | https://testing.qboid.ai:5003/ | https://testing.qboid.ai:5003/newEntry |
Web Visualization URLs
can be opened with any PC or Phone browser. The page will wait for 120s for incoming data sent by any M2 device that's currently using a Testing Server Endpoint.
Testing server endpoints can be configured on the M2 settings in Data Settings->API Endpoint
.
Click on Use Testing Server
and select the desired combination of Protocol and certificate.
For using the self signed certificate:
from flask import Flask from flask import request import json app = Flask(__name__) @app.route("/newEntry", methods=['POST']) def newEntry(): data = json.loads(request.data) print("Barcode: %s Size: %ix%ix%i" % (data["barcode"], data["l"], data["w"], data["h"])) return "" if __name__ == "__main__": app.run(host = '0.0.0.0')
Barcode: X001SS387V Size: 266x201x72
192.168.7.108 - - [24/Feb/2021 19:34:15] "POST /newEntry HTTP/1.1" 200 -
Barcode: X001SS387V Size: 267x200x72
192.168.7.108 - - [24/Feb/2021 19:36:00] "POST /newEntry HTTP/1.1" 200 -
For testing the previous code set the device end point to:
http://[HOST-IP]:5000/newEntry
where [HOST-IP] is the IP address of the PC running the code. E.g.: `192.168.7.100`
import requests import json import random import string from datetime import datetime # To be changed according to the setup ENDPOINT = 'http://127.0.0.1:5000/newEntry' def rndString(minsize, maxSize): return ''.join(random.choices(string.ascii_uppercase + string.digits, k = random.randint(minsize, maxSize))) def createJsonEntry(): data = {} data['timestamp'] = datetime.utcnow().strftime('%Y/%m/%d %H:%M:%S') data['l'] = random.randint(10,500) data['w'] = random.randint(10,500) data['h'] = random.randint(10,500) data['weight'] = random.randint(10,50000) data['barcode'] = rndString(2,20) data['shape'] = 'Irregular' data['device'] = rndString(8,8) data['note'] = rndString(0,20) attributes = {} for i in range(random.randint(0, 4)): attributes[rndString(3,5)] = rndString(3,5) data['attributes'] = attributes strjson = json.dumps(data) # Explicitly escaping "/" since python json library is not doing it by default strjson = strjson.replace('/', '\/') return strjson if __name__ == "__main__": jsonMsg = createJsonEntry() print(jsonMsg) headers = {'content-type': 'application/json; utf-8', 'Accept' : 'application/json'} try: response = requests.post(ENDPOINT, data=jsonMsg, headers=headers) if (response.ok): print('Success') else: print('Error. Endpoint %s returned code: %i' % (ENDPOINT, response.status_code)) except Exception as e: print('Error while sending request to endpoint: %s' % ENDPOINT)
{"timestamp": "2021\/02\/25 05:02:08", "l": 56, "w": 419, "h": 68, "weight": 46405, "barcode": "KPGVETAS", "shape": "Irregular", "device": "Y3S9JH1D", "note": "3RI9CKE2N", "attributes": {"FJ4": "U1HC6"}}
Success
{"timestamp": "2021\/02\/25 05:04:52", "l": 494, "w": 430, "h": 290, "weight": 31464, "barcode": "GE7WJ83B3PNJBN", "shape": "Irregular", "device": "DO1KF5ZI", "note": "A3KYJ", "attributes": {"SVRAJ": "HMXK"}}
Error. Endpoint http://127.0.0.1:5000/newEntry returned code: 405
{"timestamp": "2021\/02\/25 05:06:08", "l": 449, "w": 129, "h": 432, "weight": 2339, "barcode": "UUYXJ9Y447M", "shape": "Irregular", "device": "BO2X6TD2", "note": "B71U", "attributes": {"AF7Y": "BX44"}}
Error while sending request to endpoint: http://127.0.0.1:5000/newEntry