--- title: "Getting started with qupflowR" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Getting started with qupflowR} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} resource_files: - figures/logo.svg - figures/logo-dark.svg --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` qupflowR is being built to connect R to [QuPath](https://qupath.github.io/) 0.7.0 through a local Java bridge, with a Shiny app on top of the same R functions and shareable analysis workflows. This vignette describes what the current development version can and cannot do. It is updated whenever a new part becomes usable. ## What this version contains This version is the foundation of the package. It has no functions for QuPath projects, images or objects yet. | Part | State in this version | |---|---| | Package skeleton, tests and quality checks | first version | | Classified error conditions and the error code registry | first version | | Message catalogue in German and English | first version | | JSON helpers and JSON Schema validation for the bridge protocol | first version, building blocks for the bridge client | | Pinned execution of an explicitly trusted external method | implemented for Linux; separate from the live bridge | | Java bridge (QuPath extension and headless worker) | in development, not reachable from R | | Connecting to QuPath, reading projects, objects and measurements | planned | | Shiny app | planned | | Shareable workflows | planned | Loading the package is safe on any machine: `library(qupflowR)` starts no Java, QuPath, Python or browser process, downloads nothing and does not load optional packages such as shiny, sf or hyperannotatR. ```{r} library(qupflowR) ``` ## Errors carry stable codes Every error that qupflowR raises has a code from one registry. The Java bridge reads the same registry, so an error reported by the bridge and the matching R condition agree on the code, the R condition class, the HTTP status and whether a retry is safe. ```{r} codes <- qp_error_codes() codes[codes$code %in% c("HOST_BUSY", "REVISION_CONFLICT"), c("code", "http_status", "r_class", "retryable")] ``` Code that calls qupflowR can catch a class of problems, or look at the code: ```{r} cnd <- tryCatch( qp_abort("REVISION_CONFLICT"), qp_conflict_error = function(e) e ) class(cnd) qp_condition_code(cnd) ``` ## Messages in German or English User-facing messages come from a catalogue. German is the default; the option `qupflowR.language` selects English. ```{r} qp_msg("error.host_busy", .lang = "de") qp_msg("error.host_busy", .lang = "en") ``` ## Optional packages The Shiny app, geometry conversion with sf and the hyperannotatR interoperability will need packages that are listed in `Suggests`. They are checked only where a feature uses them. A missing package then produces a `qp_capability_error` with the code `CAPABILITY_UNAVAILABLE` that names the packages to install. ## JSON values and schema validation Protocol IDs are character values. Mark a scalar explicitly; unmarked vectors remain arrays, even when they contain only one element. ```{r json-values} payload <- list( image_id = qp_scalar("synthetic-image"), object_ids = c("1", "2"), metadata = qp_empty_object() ) encoded <- qp_json_encode(payload) encoded qp_json_decode(encoded)$object_ids qp_value_state(c(1.5, NA_real_, NaN, Inf, -Inf)) ``` The schema validator checks protocol structure, not the existence of a running bridge or the correctness of image measurements. An incomplete health document therefore produces a report with errors: ```{r schema-validation} report <- qp_schema_validate(qp_as_json("{}"), "health") report$valid head(report$errors) ``` ## Requirements - R (>= 4.5.0), as declared in DESCRIPTION. Local checks and the hosted R/OS matrix provide separate evidence; neither qualifies the native bridge. - The bridge will need QuPath 0.7.0 and a Java 25 runtime. Neither is needed for the functions shown here. Nothing has been released yet. Planned functions such as connecting to a running QuPath or opening a project do not exist in this version, so this vignette does not show them. ## Reproduce an existing external method `qp_run_external()` is a separate compatibility boundary for an explicitly trusted, hash-pinned executable and argument vector. It leaves analytical scripts and runtime choices unchanged. Declare all script, runtime, model and input pins; use a new workspace and explicit relative output files. The `{{workspace}}` argument token expands without a shell. A bounded private process environment prevents accidental use of the user's desktop/preferences. The method itself must validate its analytical outputs. The returned `execution_complete` status records process success, unchanged declared inputs and nonempty output artifacts; it does not imply scientific equivalence or a successful project save. Keep study configuration and data outside the public package. This additive API is not the future queued workflow engine and does not qualify the developing live bridge for other QuPath versions. The initial execution profile is Linux-only. Its receipt records the complete explicit child environment, and Java property values retain paths containing spaces or quotes. Other platforms remain unavailable until separately qualified. The following Linux example creates a synthetic method in a temporary directory, pins the executable and script, and checks the output against a known value. It does not read study files, start QuPath or access the network. `digest` and `processx` are optional dependencies required by this API. The example is evaluated on Linux when both are installed; elsewhere it is shown as code only. Real methods must also pin their data, runtime and model dependencies. ```{r external-method, eval=identical(Sys.info()[["sysname"]], "Linux") && requireNamespace("digest", quietly=TRUE) && requireNamespace("processx", quietly=TRUE)} local({ example_dir <- tempfile("qupflowr-example-") dir.create(example_dir) on.exit(unlink(example_dir, recursive = TRUE), add = TRUE) script <- file.path(example_dir, "synthetic-method.R") writeLines(c( "out <- commandArgs(trailingOnly = TRUE)[1]", "writeLines('42', file.path(out, 'result.txt'))" ), script) program <- normalizePath(file.path(R.home("bin"), "Rscript")) paths <- c(program, script) pins <- data.frame( path = paths, sha256 = vapply(paths, function(path) { digest::digest(file = path, algo = "sha256") }, character(1), USE.NAMES = FALSE) ) result <- qp_run_external( program = program, args = c("--vanilla", script, "{{workspace}}"), pins = pins, outputs = "result.txt", workspace = file.path(example_dir, "run"), timeout = 30, trust = TRUE ) observed <- readLines(file.path(result$workspace, "result.txt")) if (!identical(observed, "42")) { cli::cli_abort("Unexpected synthetic output.", class = "qp_example_error") } result$status }) ``` An existing workspace, a changed pinned file, a nonzero process exit, or a missing/empty declared output fails the run. Inspect the retained `run.json` and process logs for real failures. Keep those receipts private because they record paths and the explicit child environment. ## Further help Use `help(package = "qupflowR")` for the function reference and `citation("qupflowR")` for citation details. The package is GPL (>= 3). Report reproducible issues at .