a rather simple relay bridge between two communicating entities -- intended as a translator within the ChRIS ecosystem between a clinical service and a CUBE controlling service
pfbridge
was developed to "bridge" communication between one service to another. On one side, an origin point is a program or service that has some basic metadata that defines a function to apply to some data. The "data" in this case is typically some medical image defined by a set of DICOM tags, and the "function" to apply is the name of a set of operations that are ultimately managed by CUBE. Controlling CUBE and shepherding its progress is a controller service called pflink
. The pflink
API requires more verbose data that is not really relevant to the original service -- for example a typical pflink
payload contains information about several other services that are of no concern or interest to the originator.
Thus, pfbridge
was conceived as a intermediary to buffer the originator from implementation details of concern. It accepts a much reduced http
POST
body, and repackages this body into the more detailed pflink
body. Then, pfbridge
transmits (or relays) this to pflink
and captures the pflink
response. Before returning this response to the original caller, pfbridge
simplifies the response to return only success and status (and possible error) information.
A client calls the appropriate pfbridge
API endpont (/api/v1/analyze/
) with a POST
modeled by
{
"imageMeta": {
"AccessionNumber": "",
"PatientID": "",
"PatientName": "",
"PatientBirthDate": "",
"PatientAge": "",
"PatientSex": "",
"StudyDate": "",
"StudyDescription": "",
"StudyInstanceUID": "",
"Modality": "",
"ModalitiesInStudy": "",
"PerformedStationAETitle": "",
"NumberOfSeriesRelatedInstances": "",
"InstanceNumber": "",
"SeriesDate": "",
"SeriesDescription": "",
"SeriesInstanceUID": "",
"ProtocolName": "",
"AcquisitionProtocolDescription": "",
"AcquisitionProtocolName": ""
},
"analyzeFunction": ""
}
The imageMeta
contains fields corresponding to DICOM tag keys that are used to query a PACS for an image (or image set) to analyze with the named analyzeFunction
. Only imageMeta
fields that relevant to a particular image need be explicitly specified, so for example:
{
"imageMeta": {
"StudyInstanceUID": "123456789",
"SeriesInstanceUID": "123456789",
},
"analyzeFunction": "dylld"
}
Will apply the dylld
analyzeFunction
to the image with the specified StudyInstanceUID
and SeriesInstanceUID
.
After pfbridge
relays this JSON to pflink
, it returns to the caller:
{
"Status": true|false,
"State": "",
"Progress": "%",
"ErrorWorkflow": "",
"ModelViolation": null,
"ErrorComms": {
"error": "",
"URL": "",
"help": ""
}
}
The Status
is a boolean
on the status of the workflow. If it has failed, i.e. false
, then the client should examine the ErrorWorkflow
and ErrorComms
. If is operational, i.e. true
, then the client can read the current state of analysis in State
with a Progress
showing the progress within that state as a percentage, for example
{
"Status": true,
"State": "Registering image to ChRIS",
"Progress": "50%",
"ErrorWorkflow": "",
"ModelViolation": null,
"ErrorComms": {
"error": "",
"URL": "",
"help": ""
}
}
Build the latest docker image
# Pull repo...
gh repo clone FNNDSC/pfbridge
# Enter the repo...
cd pfbridge
# Set some vars
set UID (id -u) # THIS IF FOR FISH SHELLs
# export UID=$(id -u) # THIS IS FOR BASH SHELLs
export PROXY="http://10.41.13.4:3128"
# Here we build an image called local/pfbridge
# Using --no-cache is a good idea to force the image to build all from scratch
docker build --no-cache --build-arg http_proxy=$PROXY --build-arg UID=$UID -t local/pfbridge .
# If you're not behind a proxy, then
docker build --no-cache --build-arg UID=$UID -t local/pfbridge .
Several pfbridge
runtime variables can be set at start time, as defined by these models:
class Pflink(BaseSettings):
prodURL:str = 'http://localhost:8050/workflow/'
testURL:str = 'http://localhost:8050/testing/'
class DylldAnalysis(Pflink):
pluginName:str = 'pl-dylld'
pluginArgs:str = ''
clinicalUser:str = 'radstar'
feedName:str = 'dylld-%SeriesInstanceUID'
These can be set at start time by passing them in the environment to docker
. Note the settings class reads environment variables in a case insensitive manner.
# Set the workflow and testing URLs of the pflink instance
# to which we are bridging
export PRODURL=http://localhost:8050/workflow
export TESTURL=http://localhost:8050/testing
# For daemon, or background mode:
docker run --env PRODURL=$PRODURL --env TESTURL=$TESTURL \
--name pfbridge --rm -it -d \
-p 33333:33333 \
local/pfbridge /start-reload.sh
Using httpie, let's ask pfbridge
about itself
http :33333/api/v1/about/
and say hello
with some info about the system on which pfbridge
is running:
http :33333/api/v1/hello/ echoBack=="Hello, World!"
For full exemplar documented examples, see pfbridge/workflow.sh
in this repository as well as HOWTORUN
. Also consult the pfbridge/pfbridge.sh
script for more details.
Full API swagger is available. Once you have started pfbridge
, and assuming that the machine hosting the container is localhost
, navigate to http://localhost:33333/docs .
To debug code within pfbridge
from a containerized instance, perform volume mappings in an interactive session:
# Set the workflow and testing URLs of the pflink instance
# to which we are bridging
export PRODURL=http://localhost:8050/workflow
export TESTURL=http://localhost:8050/testing
# Run with support for source debugging
docker run --name pfbridge --rm -it \
-p 33333:33333 \
-v $PWD/pfbridge:/app:ro \
local/pfbridge /start-reload.sh
The workflow.sh
script can be sourced in bash
/zsh
to provide full CLI helper functions for complete access to the API.
cd pfbridge/pfbridge
# Assuming bash/zsh:
source workflow.sh
The following commands are defined:
pflinkURLs_get
: Get thepflink
links.testURL_set <URL>
: Set thepflink
test URL.prodURL_set <URL>
: Set thepflink
production URL.analysis_get
: Get theanalysis
relevant data.analysis_set <key> <value>
: Setanalysis
relevant data.relay <type> <StudyInstanceUID> <SeriesInstanceUID>
: Relay an analysis to perform.
Proper tests coming soon. For now you can use the workflow.sh
script to do some rudimentary testing. Successive calls to relay test <study> <series>
will return to the caller all the major states through which pflink
transits. Assuming you have fired up an instance of pfbridge
:
# You can check how the `pflink` URLs are currently configured with:
❯ pflinkURLs_get
{
"productionURL": "http://localhost:8050/workflow/",
"testingURL": "http://localhost:8050/testing/"
}
# This assumes of course that you have a `pflink` instance running on `localhost:8050`.
# Let's assume not and try and hit the `testing` URL:
# Here we use two numeric arguments that correspond to the
# StudyInstanceUID and SeriesInstanceUID of a dummy test:
❯ relay test 1234567 1234567
{
"Status": "Comms failure",
"Progress": "n/a",
"ErrorWorkflow": "n/a",
"ErrorComms": {
"error": "All connection attempts failed",
"URL": "http://localhost:8050/testing/",
"help": "Please check that the pflink URL is correct"
}
}
# If we in fact get `pflink` properly up, we can test the testing URL:
❯ relay test 1234567 1234567
{
"Status": "Initializing workflow",
"Progress": "0%",
"ErrorWorkflow": "",
"ErrorComms": {
"error": "",
"URL": "",
"help": ""
}
}
❯ relay test 1234567 1234567
{
"Status": "Pulling image for analysis",
"Progress": "25%",
"ErrorWorkflow": "",
"ErrorComms": {
"error": "",
"URL": "",
"help": ""
}
}
❯ relay test 1234567 1234567
{
"Status": "Pulling image for analysis",
"Progress": "50%",
"ErrorWorkflow": "",
"ErrorComms": {
"error": "",
"URL": "",
"help": ""
}
}
-30-