This documentation is intended to help designing applications for the Velobox' vmkstationd3
that include communication with Velometrik's controller for pressure mats.
Introduction
The new Velobox WS/HTTP-server supports "applications" that work largely independent from each other on top of a "kernel".
We could also talk about "application servers" but "application" is shorter.
You start an application with
ws://<velobox-name>.local:8080/apps/<application-name>
Once having created a websocket connection the client can send commands and
will asyncronously get responses or other useful information from the application.
Depending on the application there may be additional HTTP-URLs to deliver special data.
While commands are in a simple commandline mode the responses are JSON strings.
Although more than one client can connect to the same application,
they won't work independently from each other.
Generel commands
Info
- The number
0 stands for the live image, 1 for the left and 2 for the right integrated image.Number 3 stands for a retrieved image.
[something] denotes an optional input for "something"
a|b an alternative.
-
record {[-force] <duration> [1|2]} | cancel
- Start recording 1 or 2 rsp. for <duration> seconds
- Use the -force option to override unstored recordings. Without -force an unstored recording will cause an error.
- After recording the results will be sent. If no recording is specified 1 is the default.
- cancel running recording. This is the preferred method to avoid an "unstored" error.
-
set mask {nomask|standard|large|custom|<name>}|{[-store [<name>]] <pattern>}
- Select one of the predefined masks or specify a new mask
- <pattern> is an sequence with a length of 448 for each senorpoint. 1 is off and 0 is on
- The new mask will be stored with the name specified by the -store option where "custom" is the default
- The predefined masks 'nomask', 'standard' and 'large' cannot be overridden
- A mask name may only consist of alphanumeric characters and may not begin with a digit
-
set jpeg colorcontrast 0-7
- Set color contrast for all three images
- Changing the color contrast will affect the other Apps too
-
set jpeg bgcolor <#HEX value>
- Set background color for all three images
- Changing the color contrast will affect the other Apps too
-
set jpeg 0|1|2|3 grid no|dotted|solid
- Shows or doesn't show the grid that separates the measuring points
-
set jpeg 0|1|2|3 resolution 1-n
- Sets the number of pixels generated per measuring point
- Although there is no upper limit, a large number will probabely knock out the Velobox
-
set jpeg 0|1|2|3 frame 0-n
- Draws a black frame around the generated image of the width (0-n pixels) specified
-
set jpeg 0|1|2|3 quality 0-100
- Sets quality for JPEG compression
-
set jpeg 0|1|2|3 mask show|hide
-
set compare on|off
- Switch compare mode on or off
-
get mask <name>
- Fetch mask specified by <name>
- This will cause the wsevent 'mask' with the keys 'name' and 'values'
- The mask can be used a starting point for a mask editor
-
get appquestions [<lang>]1)
- Fetch questions whose answers will be recorded for a client during a fitting session
- They correspond to the questions ask by the Velometrik® product selection webservice
-
get appsettings
- Fetch all settings done by a set command (or config) as a single JSON Object
- Details are explained below
-
config n_update_live|n_update_int 1-n
- Configure application behavior
- Update every n_update_live-th live image
- Update every n_update_int-th integrated image
- Affects only JPEG-Generation
-
switch create_jpeg on|off
- Switch the creation of JPEG pressure images on or off rsp
-
switch create_norm on|off
- Switch the creation of normalized pressure value on or off rsp
-
status <what>
- Returns status information. <what> may be:
channel{1|2} |
recording status of that channel |
|
finished true|false |
|
recording_id (0 if not stored) |
imagespeed |
number of pressure images received during 1s 1) |
temp |
CPU temperature in degrees centigrade |
1)Images without any pressure will not be counted
-
version2)
- Query Version
- The versions of vmkstationd (key
vmkstationd) and of the application (key app) will be returned
Bluetooth commands
-
btconnect [<device_name>]
- Connect to the only device available or to the device specified
- If the device is not paired yet, it will be paired before
-
btdisconnect
- End current connection
- The device keeps paired
-
btremove [<device_name>]
- Remove the device given by <device_name> from the paired devices
- If <device_name> is not given, all devices whose name starts with "SmartCover" will be removed
-
The usage of this command is recommended in any case of connection failure with the following steps:
- Inform user about the connection failure (The reason usually is unimportant.)
- Ask user if he wants to retry
- Send btremove (If the user wants to retry)
- Send btconect again
-
btversion
- Query version of internal Bluetooth agent
- A version change could affect the
btevent's
Database commands
The Velobox has a database for storing the results of the saddle fittings.
It can be accessed by SQL. Extra commands ease the handling of images by creating JPEG images with the current JPEG setting from the stored pressure values and the mask.
Together they support a reasonable workflow.
-
sql <SQL command>
- Commands to handle sessions
- This command returns the wsevent "sqlresult" with the key "rows" whose value is an array of the rows returned by the SQL command
- Each row is an object with the column names as keys. In case of a NULL value the corresponding key is missing
-
session <subcommand> ...
- Execute the given SQL command
- Fittings are organized in sessions. Every session has a number of recordings
- A session must be started before a recording can be stored
-
The subcommands:
-
start <client_id>|<client_info>
- tart a new session for the client with <client_id>
- Start a new session for a new client specified by <client_info>
- <client_info> is a JSON object string with the keys 'forename', 'surname' and 'email'. Non of these keys is mandatory
- Usually it is not a good idea to store a client without any of these data because the only way to find him again will be the autmatically generated timestamp
- The client_id of the new client can be retrieved by
SELECT next_id-1 AS client_id FROM nextids WHERE table_name = 'clients'
- Both versions create a new row in the sessions table. The session_id can be retrieved by
SELECT next_id-1 AS session_id FROM nextids WHERE table_name = 'sessions'
-
finish
- Finish the current session
- Finishing a session means checking for unsaved recordings and deleting empty sessions. (sessions without any recordings)
- Attention! The application server cannot know about unsaved data on the client (product, product_label or notes)
-
restart <session_id>
- Restart an earlier session
- This can be useful if the fitter wants to resume an earier session that was not completely successful
-
retrieve <session_id>
- Fetch 1st and last recording of a session from the database and send images, results and notes
- The notes (product, product_label, notes) will be packed as additional keys to the result objects
-
recording <subcommand> ...
- Commands concerning a recording
-
The subcommands:
-
store 1|2 [<infos>]
- Store last recording results (pressure values and analysis results) from the given channel
- The optional <infos> is a JSON object string with the keys 'product', 'product_label' and 'notes'.
- If the recording was already stored only the infos will be updated
-
cancel [1|2]
- Cancels recording 1 or recording 2 respectively
- If no recording is specified both recordings will be cancelled
- This is useful if the user explicitly wants to cancel a recording before starting a new one
- To implicitly cancel an unstored recording use the -force option of the record command
-
retrieve <recording_id> [1|2|3]
- etch recording with <recording_id> from the database
- The notes (product, product_label, notes) will be packed as additional keys to the result object
- If a channel is given, the image and the results are for that channel
- If the destination channel holds an unstored recording an error message will be returned
- Use record cancel or record store to clear this problem.
- If no channel is given, the image will be created in "session compare mode", i.e. the (red) maximum shows the maxiumum pressure of the whole session.
-
analysis {<blocks> [<session_id>] | <analyses>}
- Storing and retrieving unique1) analysis results during a session
- These results may come from measurements or may be results of an inquiry.
- There are the following types (blocks) of results:
- 1) unique within a session
| block |
contains |
| sitbones |
Result of sitbone analysis |
|
(currently of visual analysis of sitbones pressure image) |
| anamnesis |
Result of anamnesis |
|
(see get appquestions) |
| x1 |
1st result set defined by client application |
| x2 |
2nd result set ... |
| ... |
(numbering 1 ...) |
- The complete syntax is as follows:
analysis {<blocks> [<session_id>] | <analyses>}
-
| <blocks>: |
JSON-array containing the block codes for retrieve |
| <session_id>: |
... the results belong to |
|
(Default is the active session.) |
| <analyses>: |
JSON-object containing all the results to be stored |
-
get mask ...
-
get appquestions ...
[The database tables]
| clients |
| client_id | INT NOT NULL | PK |
| email | VARCHAR(129) | |
| forename | VARCHAR(40) | |
| surname | VARCHAR(40) | |
| first_contact | CHAR(19) NOT NULL | YYYY-MM-DD hh:mm:ss |
| sessions |
| session_id | INT NOT NULL | PK |
| client_id | INT NOT NULL | FK to clients |
| date | CHAR(10) NOT NULL | |
| restart | CHAR(10) | YYYY-MM-DD |
| notes | TEXT | YYYY-MM-DD |
| recordings |
| recording_id | INT NOT NULL | PK |
| session_id | INT NOT NULL | FK to sessions |
| analysis | INT NOT NULL | 1 sitbones 1)
2 saddlepressure
3 anamnesis 1)
|
| finished | CHAR(19) NOT NULL | YYYY-MM-DD hh:mm:ss |
| product | VARCHAR(40) | |
| product_label | VARCHAR(40) | |
| n_pressure_rows | INT | |
| n_pressure_cols | INT | |
| pressure_values | TEXT | space char separated integers |
| max_pressure | INT | |
| mask | TEXT | space char separated 0 and 1 |
| results | TEXT | JSON object |
| notes | TEXT | |
1) for future version
| masks |
| mask_id | INT NOT NULL | PK |
| name | CHAR(20) NOT NULL | alphanumerical chars only |
| standard | INT NOT NULL | 0 or 1 |
| n_rows | INT NOT NULL | |
| n_cols | INT NOT NULL | |
| mask | TEXT NOT NULL | space char separated 0 and 1 |
| nextids |
| table_name | VARCHAR NOT NULL | PK |
| id_name | VARCHAR NOT NULL | |
| next_id | INT NOT NULL | 1) |
1)This value must be used for the next INSERT into this table.
After that is must be increased by 1.
It's a good idea to do this together with the INSERT inside a transaction.
The recording results
Recording ends with wsevent result1 or result2 rsp.
It may end with wsevent error (msg "Insufficient data")
if no pressure images have arrived during recording.
Remember that non of these values has overall meaning.
That means you can compare only values captured during the same bike fitting session
to find an improvement or a change for the worse.
The result values are:
| Key |
Value |
| pelvisrotation | area in which the center of pressure moved during recording (details below) |
| veloscore | Velometrik®s measure of pressure distribution (explanation below) |
| front_rear (front:rear) | pressure ratio between front and rear half in % relative to whole pressure |
| left_right (left:right) | pressure ratio between left and right rear quarter in % relative to whole pressure in the rear half |
| center_left (x,y) | center of pressure inside left half |
| center_right (x,y) | center of pressure inside right half |
| center (x,y) | overall center of pressure (situated on the tie line between center_left and center_right) |
Pelvis rotation
There are several ways how to graphically present the pelvet rotation area.
The simplest way is to draw a rectangle. The most advanced way would be to draw 4 splines.
The result object defines a kind of oval represented of 4 splines embedded inside a parallelogram.
The easiest way to present a numerical result is using "width" and "height" which describes the size of a surrounding rectangle.
v-Secondary axis
| c1 | front | c2
-+------------------+------------------------+- Front tangent
| | |
| | |
| | |
| left | center | right
-+------------------+------------------------+- <-Principal axis
| | |
| | |
| c4 | rear | c3
-+------------------+------------------------+- Rear tangent
| | |
Left tangent Right tangent
Veloscore
The "veloscore" is a measure of pressure distribution.
Values are from 0 to 100 where 0 means "All pressure is on a single point." and 100 means "All points have the same pressure.".
JSON responses
Every JSON response has the key "wsevent" which classifies the JSON message.
Depending on the message class there may be more keys specific for that message class.
Until now there are the following definitions:
| WSEvent |
Detail keys |
Notes |
| error | class | internal|client|user errors |
| nr | error number (user errors only) |
| source | error source |
| msg | text message |
| max_value | value | New pressure maximum |
| sda_live | | A new live image is available.s. Fetching JPEG-Images |
| sda_int1 | | An integrated image for the left side is available. s. Fetching JPEG-Images |
| sda_int2 | | An integrated image for the right side is available.s. Fetching JPEG-Images |
| sda_int3 | | A retrieved image in "session compare mode". |
| sattelnorm | | Normalized pressure values |
| imagetype | sda_live, sda_int1, sda_int2, sda_int3 corresponding to the "image available" WSEvents |
| n_rows | number of rows |
| n_cols | number of columns |
| values | pressure values 0...255 (The maximum (if any) is always 255.) |
| result1 | | Result for the left side. |
| pelvisrotation | see schema above |
| | Values in cm scale=1 |
| veloscore | Velometriks measure of the pressure distribution |
| | Values 0...100 scale=1 |
| front_rear | pressure distribution between front and rear in percent |
| | Format <left>:<right> |
| | Values 0...100 scale=0 |
| left_right | pressure distribution between left and right in percent |
| | Format <left>:<right> |
| | Values 0...100 scale=0 |
| center | center of pressure |
| | Object with keys x, y |
| | Values in cm scale=1 |
| center_left | center of pressure on left half |
| | Object with keys x, y |
| | Values in cm scale=1 |
| center_right | center of pressure on right half |
| | Object with keys x, y |
| | Values in cm scale=1 |
| result2 | | Result for the right side. |
| result3 | | Result from a retrieved recording. |
| pelvisrotation | (like record1) |
| veloscore | ... |
| ... | |
| ttychange | change | plugged|unplugged |
| start | started reading (ok|error if plugged) |
| type | mat type MT_SAT* |
| driver | (used as identifier) |
| ttystate | state | active|inactive |
| driver | (used as identifier) |
| overload | source | where overload comes from |
| total | total number of requests |
| rejected | number of requests rejected |
| temp | CPU temperature |
| freq_max | maximum CPU frequency |
| freq_cur | current CPU frequency |
| btevent | btevent | Bluetooth event (Detail keys see next table) |
Error classes and numbers:
| class |
number |
notes |
| internel | | Error in the application server
This should never happen and therefore must be reported to Velometrik |
| client | | Error in the client application
This must be fixed by the client (GUI) developer. |
| user | | Handling error by client application user
These errors should be reported to the application user. |
| | 1 | Insufficient data for analysis |
| | 2 | A recording started before is still running. |
| | 3 | There are unstored recording(s) that must be stored or cancelled before. |
| | 4 | There is no session. (It must be create before.) |
| | 5 | There is no recording (to store) or the recording is not finished yet. |
| BTEvent |
Detail key |
Class |
Number |
Meaning |
| disconnected | | | | Regular disconnect |
| ambiguity | devices | | | Array with all device names |
| data | | | | Data are available |
| error | class | disconnected | | Unexpected disconnect |
| | | fail | | Connection fails |
| | | | | Pair failed |
| | | | | Start notify failed |
| | | | | Device not found |
| | | | | No device found |
| | | | | Expected device not found |
| | | | | Connection timeout |
| | | comm | | Stop notify failed |
| | | client | | Command/syntax errors |
| | | | 1 | |
| | | | 2 | disconnect while trying connect (ignored) |
| | | internal | | Unexpected error, btagent restarted |
| | msg | | | The error message |
| debug | | | | Debug message |
Stored settings
A series of the
commands documented above apply to settings
that define the appearance of pressure images.
These settings will be stored in the Velobox when disconnecting from the application
and they will be retrieved at the next application start.
The client application developer may want to offer menu like graphical elements to the user to let him set everything according to his own flavor.
To initialize them the current settings
can be obtained using the command
get appsettings.
The application responds with a wsevent "
appsettings" with a settings object
as value of the key
appsettings.
This object has the keys listed as "Settings" in the following table.
If a setting applies to different displays (0 live image; 1, 2 integrated images; 3 other images) there are subkeys 0...3.
| Setting |
Command |
| Setting | Command |
| maskname | set mask ... |
| compare | set compare ... |
| contrast | set jpegcontrast ... |
| bgcolor | set jpeg {0|1|2|3} bgcolor ... |
| grid | set jpeg {0|1|2|3} grid ... |
| resolution | set jpeg {0|1|2|3} resolution ... |
| frame | set jpeg {0|1|2|3} frame ... |
| jpegquality | set jpeg {0|1|2|3} quality ... |
| showmask | set jpeg {0|1|2|3} mask ... |
| imageupdates | config n_update_... |
Fetching JPEG-Images
An image announced by a wsevent must be either fetched by a HTTP-request or it can be ignored.
The URL for fetching is
/sattelbild?image=<imagetype>
Hints to client application developers
Creating JPEG images is the most CPU power consuming part of the whole application.
Therefore you should choose your JPEG settings in a way that minimizes power consumption.
It is very easy to create an overload situation by setting 'set jpeg 0 resolution 50' for example.
You will see a delayed live image and after a few seconds an overload wsevent will arrive.
Among the additional information is the temperature. This is because the cpu slows down at high temperatures1).
If at overload the current frequency is lower than the maximum frequency a high temperature might be the reason.
It could a good idea to send a warning to your user in this case.
1)It also slows down at low load which would contradict the overload condition.
The pelvis rotation area in real life is very small. (The fitting goal is a minimized area.)
Showing the shape with four splines usually only makes sense in a magnified representation.
Bluetooth connection handling is managed by only two commands, btconnect and btdisconnect.
In case of an error it's sufficient to rety btconnect after giving an appropriate to the user.
In case of ambiguity a menu with all choices should be offered.
1)
Questions are subdivided in an array of sections. Each section object has a code, a text for display and an array of questions (named fragen).
Each question has a code (equal to the code in Velometrik®s product selection webservice), a response type (antworttyp),
a text for display and, depending on the response type, an array of defaults. The respond types are:
- numeric value
- yes/no answer
- numeric value from defaults
- string value from defaults
Questions with response type 3 or 4 have a non empty array with defaults (vorgaben).
Each default object has a code (which is the value to be stored, i.e. the answer), a boolean value standard and a text for display.
"standard is true" means, this value can be used as the proposed default value.
Moreover there is an empty string named antwort (answer) in every question to make GUI programmers live a little bit easier.
2)
Versioning follows the rule: <main>.<minor>.<revision>. A new revision contains corrections only.
A new minor version is compatible with the previous minor version.
A new main version may not be compatible with the previous one.
The version string may have one of the following appendices (seperated by underscore '_'):
| Appendix |
Meaning |
Remark |
| A | Alpha | Developers current (pre beta) version |
| X<date> | eXperimental | <date>: YYYYMMDD |
| B<nr>[U] | Beta <nr> | |
| P<nr>[U] | Prerelease <nr> | |
The character "U" marks the version as "unfinished".