Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Semantic markup and ICS table generation #402

Open
wants to merge 51 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
83db68b
Added document for testing
PaulMartinsen Nov 24, 2024
db1901e
Initial go at processing requirement metadata.
PaulMartinsen Nov 24, 2024
b163244
Fix up paths and remove redundant references
PaulMartinsen Nov 24, 2024
e5d50d2
Added test documents
PaulMartinsen Nov 24, 2024
a5b2458
Added support for source specification oids through document attribut…
PaulMartinsen Nov 29, 2024
1cb5b33
Tidy
PaulMartinsen Nov 29, 2024
ad6b88e
Implemented tree-processor to extract semantic SDPI content.
PaulMartinsen Jan 1, 2025
2ad0ffc
Added examples for testing.
PaulMartinsen Jan 1, 2025
0579a69
Added basic support extract use case semantics
PaulMartinsen Jan 2, 2025
4ee331e
Added oid definitions.
PaulMartinsen Jan 4, 2025
c4c5b69
Added support to create implementation conformance statement tables.
PaulMartinsen Jan 9, 2025
6d4e5e3
Merge remote-tracking branch 'origin/master' into 2024-11-PJM-Require…
PaulMartinsen Jan 9, 2025
a13f5b6
Don't add global id when oid information isn't available.
PaulMartinsen Jan 9, 2025
aefff5a
Document DumpTreeInfo breaking macro replacement.
PaulMartinsen Jan 9, 2025
c0a4070
Support unfiltered requirements tables.
PaulMartinsen Jan 10, 2025
0c2b012
Applied Kotlin style and coding standards from IDE.
PaulMartinsen Feb 22, 2025
5de1231
Fixed image example path.
PaulMartinsen Feb 22, 2025
3f141d4
Started documenting semantic formatting of requirements and use-cases.
PaulMartinsen Feb 23, 2025
03cbd9e
Documenting more use-cases, and inserting tables.
PaulMartinsen Feb 28, 2025
22697fc
Fix test build error; tidy language in doc
PaulMartinsen Feb 28, 2025
8c65e4f
Added command line option to control structure diagnosis dump.
PaulMartinsen Feb 28, 2025
41cb9ce
Made bibliography optional (for tests).
PaulMartinsen Feb 28, 2025
8b881ec
Added tests for requirements, use-cases, ICS tables.
PaulMartinsen Mar 1, 2025
39aa426
Refactored numbering tests to isolate processor tested and simplify d…
PaulMartinsen Mar 1, 2025
d8d5cfe
Tidy converter options
PaulMartinsen Mar 1, 2025
36e057d
Fixes for PDF target
PaulMartinsen Mar 6, 2025
ef1957d
Expanded document generation docs to cover test tools.
PaulMartinsen Mar 6, 2025
552685e
Fixed PDF icons
PaulMartinsen Mar 6, 2025
fee61b6
Merge remote-tracking branch 'origin/master' into 2024-11-PJM-Require…
PaulMartinsen Jan 9, 2025
b2888fa
Don't add global id when oid information isn't available.
PaulMartinsen Jan 9, 2025
6266aca
Document DumpTreeInfo breaking macro replacement.
PaulMartinsen Jan 9, 2025
96af868
Support unfiltered requirements tables.
PaulMartinsen Jan 10, 2025
532bd3e
Applied Kotlin style and coding standards from IDE.
PaulMartinsen Feb 22, 2025
ff11cc9
Fixed image example path.
PaulMartinsen Feb 22, 2025
a589ef0
Started documenting semantic formatting of requirements and use-cases.
PaulMartinsen Feb 23, 2025
21c3a65
Documenting more use-cases, and inserting tables.
PaulMartinsen Feb 28, 2025
b0c576c
Fix test build error; tidy language in doc
PaulMartinsen Feb 28, 2025
518fd4f
Added command line option to control structure diagnosis dump.
PaulMartinsen Feb 28, 2025
0fc249e
Made bibliography optional (for tests).
PaulMartinsen Feb 28, 2025
8ddadff
Added tests for requirements, use-cases, ICS tables.
PaulMartinsen Mar 1, 2025
07c3fbf
Refactored numbering tests to isolate processor tested and simplify d…
PaulMartinsen Mar 1, 2025
92fc103
Tidy converter options
PaulMartinsen Mar 1, 2025
fa1c0a1
Fixes for PDF target
PaulMartinsen Mar 6, 2025
b007f17
Expanded document generation docs to cover test tools.
PaulMartinsen Mar 6, 2025
c324962
Fixed PDF icons
PaulMartinsen Mar 6, 2025
215a038
Merge remote-tracking branch 'origin/2024-11-PJM-RequirementMetadata'…
PaulMartinsen Mar 6, 2025
fb76947
Removed PDF referering to itself.
PaulMartinsen Mar 6, 2025
8a717d3
Removed testdoc used for development.
PaulMartinsen Mar 6, 2025
9632fa6
Added copy2clipboard back; fixed for asciidoc folder names.
PaulMartinsen Mar 6, 2025
ffa4e77
Minor change to trigger actions?
PaulMartinsen Mar 14, 2025
d526f4f
Support special value "skip" for SDPI_API_ACCESS_TOKEN_SECRET. Allows…
PaulMartinsen Mar 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Started documenting semantic formatting of requirements and use-cases.
Tidy
PaulMartinsen committed Feb 23, 2025
commit 3f141d42de47db55026b2166212b06f334ad98bb
Original file line number Diff line number Diff line change
@@ -7,15 +7,14 @@ enum class BlockAttribute(val key: String) {
ID("id"),
TITLE("title"),
ROLE("role"),
REQUIREMENT_LEVEL("sdpi_req_level"),
REQUIREMENT_TYPE("sdpi_req_type"),
REQUIREMENT_GROUPS("sdpi_req_group"),
REQUIREMENT_SPECIFICATION("sdpi_req_specification"),
MAX_OCCURRENCE("sdpi_max_occurrence"),
VOLUME_CAPTION("sdpi_volume_caption"),

}

/**
* Defines attribute keywords for sdpi_requirement blocks
*/
sealed class RequirementAttributes {
enum class Common(val key: String) {
// Type of requirement.
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import org.asciidoctor.ast.StructuralNode
import org.asciidoctor.extension.BlockMacroProcessor
import org.asciidoctor.extension.Name
import org.sdpi.asciidoc.BlockAttribute
import org.sdpi.asciidoc.RequirementAttributes

const val BLOCK_MACRO_NAME_SDPI_ICS_TABLE = "sdpi_ics_table"

@@ -41,9 +42,9 @@ class AddICSPlaceholder : BlockMacroProcessor(BLOCK_MACRO_NAME_SDPI_ICS_TABLE) {
placeholderTable.attributes["role"] = ICS_TABLE_ROLE

// Add filter attributes to the table for the tree processor to consume.
val strGroup = attributes[BlockAttribute.REQUIREMENT_GROUPS.key]
val strGroup = attributes[RequirementAttributes.Common.GROUPS.key]
if (strGroup != null) {
placeholderTable.attributes[BlockAttribute.REQUIREMENT_GROUPS.key] = strGroup
placeholderTable.attributes[RequirementAttributes.Common.GROUPS.key] = strGroup
}

return placeholderTable
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import org.asciidoctor.extension.BlockMacroProcessor
import org.asciidoctor.extension.Name
import org.apache.logging.log4j.kotlin.Logging
import org.sdpi.asciidoc.BlockAttribute
import org.sdpi.asciidoc.RequirementAttributes

const val BLOCK_MACRO_NAME_SDPI_REQUIREMENT_TABLE = "sdpi_requirement_table"

@@ -43,9 +44,9 @@ class AddRequirementQueryPlaceholder : BlockMacroProcessor(BLOCK_MACRO_NAME_SDPI
placeholderTable.attributes["role"] = REQUIREMENT_TABLE_ROLE

// Add filter attributes to the table for the tree processor to consume.
val strGroup = attributes[BlockAttribute.REQUIREMENT_GROUPS.key]
val strGroup = attributes[RequirementAttributes.Common.GROUPS.key]
if (strGroup != null) {
placeholderTable.attributes[BlockAttribute.REQUIREMENT_GROUPS.key] = strGroup
placeholderTable.attributes[RequirementAttributes.Common.GROUPS.key] = strGroup
}

return placeholderTable
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import org.asciidoctor.ast.Table
import org.asciidoctor.extension.Contexts
import org.asciidoctor.extension.Treeprocessor
import org.sdpi.asciidoc.BlockAttribute
import org.sdpi.asciidoc.RequirementAttributes
import org.sdpi.asciidoc.getRequirementGroups
import org.sdpi.asciidoc.model.SdpiRequirement2
import org.sdpi.asciidoc.plainContext
@@ -42,7 +43,7 @@ class PopulateTables(private val docInfo: SdpiInformationCollector) : Treeproces
private fun getSelectedRequirements(block: Table): Collection<SdpiRequirement2> {
val requirementsInDocument = docInfo.requirements()

val aGroups = getRequirementGroups(block.attributes[BlockAttribute.REQUIREMENT_GROUPS.key])
val aGroups = getRequirementGroups(block.attributes[RequirementAttributes.Common.GROUPS.key])
if (aGroups.isEmpty()) {
// unfiltered
return requirementsInDocument.values

This file was deleted.

Original file line number Diff line number Diff line change
@@ -164,9 +164,9 @@ class RequirementsBlockProcessor : BlockProcessor(BLOCK_NAME_SDPI_REQUIREMENT) {
* Retrieve the requirement level
*/
private fun getRequirementLevel(requirementNumber: Int, attributes: MutableMap<String, Any>): RequirementLevel {
val strLevel = attributes[BlockAttribute.REQUIREMENT_LEVEL.key]
val strLevel = attributes[RequirementAttributes.Common.LEVEL.key]
checkNotNull(strLevel) {
("Missing ${BlockAttribute.REQUIREMENT_LEVEL.key} attribute for SDPi requirement #$requirementNumber").also {
("Missing ${RequirementAttributes.Common.LEVEL.key} attribute for SDPi requirement #$requirementNumber").also {
logger.error { it }
}
}
@@ -184,9 +184,9 @@ class RequirementsBlockProcessor : BlockProcessor(BLOCK_NAME_SDPI_REQUIREMENT) {
* Retrieve the requirement type
*/
private fun getRequirementType(requirementNumber: Int, attributes: MutableMap<String, Any>): RequirementType {
val strType = attributes[BlockAttribute.REQUIREMENT_TYPE.key]
val strType = attributes[RequirementAttributes.Common.TYPE.key]
checkNotNull(strType) {
("Missing ${BlockAttribute.REQUIREMENT_TYPE.key} attribute for SDPi requirement #$requirementNumber").also {
("Missing ${RequirementAttributes.Common.TYPE.key} attribute for SDPi requirement #$requirementNumber").also {
logger.error { it }
}
}
@@ -222,7 +222,7 @@ class RequirementsBlockProcessor : BlockProcessor(BLOCK_NAME_SDPI_REQUIREMENT) {
* Retrieves the list of groups the requirement belongs to (if any).
*/
private fun getRequirementGroupMembership(mutableAttributes: MutableMap<String, Any>): List<String> {
return getRequirementGroups(mutableAttributes[BlockAttribute.REQUIREMENT_GROUPS.key])
return getRequirementGroups(mutableAttributes[RequirementAttributes.Common.GROUPS.key])
}

/**
@@ -283,7 +283,7 @@ class RequirementsBlockProcessor : BlockProcessor(BLOCK_NAME_SDPI_REQUIREMENT) {
requirementNumber: Int,
mutableAttributes: MutableMap<String, Any>
): String {
val strSourceSpecification = mutableAttributes[BlockAttribute.REQUIREMENT_SPECIFICATION.key]
val strSourceSpecification = mutableAttributes[RequirementAttributes.Common.SPECIFICATION.key]
checkNotNull(strSourceSpecification) {
("Missing requirement source id for SDPi requirement #$requirementNumber").also {
logger.error(it)
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.sdpi.asciidoc.extension

/**
* Defines keywords for block roles.
*/
sealed class RoleNames {
enum class UseCase(val key: String) {
// Use case section
FEATURE("use-case"),

// The block providing technical pre-conditions for the use case.
// Must be inside a feature section.
BACKGROUND("use-case-background"),

// Block describing one scenario in the use case. Must be
// inside a feature section.
SCENARIO("use-case-scenario"),

// Steps for the scenario. Must be inside a scenario section.
STEPS("use-case-steps"),
}
}
Original file line number Diff line number Diff line change
@@ -69,7 +69,7 @@ class SdpiInformationCollector(private val bibliography: BibliographyCollector)
processRequirement(block)
}

if (block.hasRole("use-case")) {
if (block.hasRole(RoleNames.UseCase.FEATURE.key)) {
processUseCase(block)
}

@@ -563,19 +563,19 @@ class SdpiInformationCollector(private val bibliography: BibliographyCollector)
var iBlock = 0
while (iBlock < specBlocks.count()) {
val useCaseBlock = specBlocks[iBlock]
if (useCaseBlock.hasRole("use-case-background")) {
if (useCaseBlock.hasRole(RoleNames.UseCase.BACKGROUND.key)) {
backgroundContent.addAll(getSteps(useCaseBlock))
}

if (useCaseBlock.hasRole("use-case-scenario")) {
if (useCaseBlock.hasRole(RoleNames.UseCase.SCENARIO.key)) {
val oTitle = useCaseBlock.attributes["sdpi_scenario"]
checkNotNull(oTitle)
{
"${getLocation(useCaseBlock)} missing required scenario title".also { logger.error { it } }
}

val iStepBlock = iBlock + 1
check(iStepBlock < specBlocks.count() && specBlocks[iStepBlock].hasRole("use-case-steps"))
check(iStepBlock < specBlocks.count() && specBlocks[iStepBlock].hasRole(RoleNames.UseCase.STEPS.key))
{
"${getLocation(useCaseBlock)} missing steps for scenario $oTitle".also { logger.error { it } }
}
@@ -596,13 +596,13 @@ class SdpiInformationCollector(private val bibliography: BibliographyCollector)
for (child in block.blocks) {
val strRole = child.role
when (strRole) {
"use-case-background" -> specBlocks.add(child)
"use-case-scenario" -> {
RoleNames.UseCase.BACKGROUND.key -> specBlocks.add(child)
RoleNames.UseCase.SCENARIO.key -> {
specBlocks.add(child)
gatherUseCaseBlocks(child, specBlocks)
}

"use-case-steps" -> specBlocks.add(child)
RoleNames.UseCase.STEPS.key-> specBlocks.add(child)
else -> gatherUseCaseBlocks(child, specBlocks)
}
}
225 changes: 225 additions & 0 deletions articles/sdpi-article-ihe-tf-asciidoc-cookbook.adoc
Original file line number Diff line number Diff line change
@@ -337,6 +337,231 @@ For a bulleted list of items, the final "full stop" character of each non-final
. Second bullet;
. Final Bullet.


== Semantic content
https://docs.asciidoctor.org/asciidoctorj/latest/[Asciidoctor], which converts these files into other formats (e.g., html, pdf), has been https://docs.asciidoctor.org/asciidoctorj/latest/extensions/extensions-introduction/[extended] to process domain specific content for the supplement. These extensions provide semantic markup of content that is used to automatically generate, for example, implementation conformance statement tables and export requirements and use cases in JSON format for external tooling (see https://profiles.ihe.net/DEV/SDPi/index.html#vol1_appendix_a_requirements_management_for_p_n_t_interperability[requirements management for plug-and-trust interoperability]).

Content encoded and exported, to `sdpi-supplement/referenced-artifacts/` in JSON format includes:

* `sdpi-requirements.json`: all requirements defined, as described below, in the source.
* `sdpi-use-cases.json`: all use cases defined, as described below, in the source.


NOTE: the extensions are written in Kotlin and may be found, along with the document processing tools, in `.ci/asciidoc-converter`. In Windows, run `build_document.bat` to process the Asciidoc source files.

=== Defining requirements

The example below illustrates the AsciiDoc source to define a simple requirement.

[source,asciidoc]
----
.R1021
[sdpi_requirement,sdpi_req_level=shall,sdpi_req_type=tech_feature,sdpi_req_group="provider,discovery-proxy",sdpi_req_specification=sdpi-p]
****
[NORMATIVE]
====
When the managed discovery option is enabled for a SOMDS provider, then it SHALL use the DEV-46 transaction to update the discovery proxy actor of its network presence and departure.
====
****
----

The block name, `sdpi_requirement`, triggers semantic processing for the requirement content while the https://docs.asciidoctor.org/asciidoc/latest/blocks/add-title/[block title] defines the requirement number. Various <<requirement-attributes,attributes>> define properties for the requirement. The requirement content is delimited by exactly four *'s. The only required content for a block is the normative statement, tagged with the block name `[NORMATIVE]` and delimited by exactly four ='s. With this information we can:

* perform basic requirement validation, such as checking level keywords in the normative text,
* generate a unique <<requirement-oids,ISO object identifier>> for each requirement,
* generate implementation conformance statement <<generate-table,tables>> with a list of requirements for selected groups,
* ensure all requirement numbers are unique,
* &hellip;

Although the normative statement is the only block needed to define a requirement, many requirements include explanatory notes, examples and related information. This additional information is captured in tagged content blocks as the example below illustrates. This semantic content is retained when the requirements are exported in JSON format.

[source,asciidoc]
----
.R1023
[sdpi_requirement,sdpi_req_level=shall,sdpi_req_type=tech_feature,sdpi_req_group="consumer,discovery-proxy",sdpi_req_specification=sdpi-p]
****
[NORMATIVE]
====
When the managed discovery option is enabled for a SOMDS consumer, then it shall use the DEV-47 transaction to retrieve SOMDS provider network presence metadata from the Discovery Proxy actor.
====
[NOTE]
====
. When retrieving network presence metadata from a discovery proxy actor, a discovery scope may be specified as a filter to identify a specific subset of SOMDS provider systems.
. A SOMDS consumer may optionally use the DEV-47 transaction to subscribe to all metadata updates from a set of SOMDS consumer systems, essentially using the discovery proxy as a pass-through for SOMDS provider DEV-23 and DEV-24 transactions.
====
[EXAMPLE]
====
This is an example of an example block.
====
[RELATED]
====
<<ref_oasis_ws_discovery_2009>>, section 2.2.2 Managed mode.
====
****
----

==== Requirement numbering and referencing

Requirement numbers are parsed from the title of the `sdpi_requirement` block. They must match the regular expression `^([A-Z])*?R(\\d+)$`. That is, a sequence of upper-case characters followed by `R` then a numeric value. Examples of valid requirement titles include: `.R1002`, `.TR1002`, `.ABCDEFR1002`. Invalid requirement titles include `.r1234` (an upper-case `R` is required), `.ABC R1234` (spaces are not permitted), `.tR1234` (lower-case letters are not permitted).

NOTE: The `.` character marks the line as an AsciiDoc https://docs.asciidoctor.org/asciidoc/latest/blocks/add-title/[title]; it is required to begin in the first column of the source file.

NOTE: Requirements are identified by number only. They are preserved but do not form part of the requirement identifier.

Requirements may be referenced using the `RefRequirement` in-line macro. For example: `RefRequirement:r1002[]`. Requirements are matched numerically only and must be prefixed by a lower-case `r`. References can be used in paragraph text. For example:

====
Check out RefRequirement:r1001[], it is super important!
====

NOTE: Previously anchors were needed, for cross-referencing, when defining a requirement (e.g., `[sdpi_requirement#r1022,sdpi_req_level=shall]`). These are now generated automatically.

IMPORTANT: Using the `RefRequirement` in-line macro helps ensure future compatibility if, for example, <<requirement-oids,ISO object identifiers>> are used to reference requirements.

[[requirement-attributes]]
==== Block attributes

Block attributes supply metadata for the requirement. Attributes are tabulated below; additional information may be found in https://profiles.ihe.net/DEV/SDPi/index.html#vol1_clause_sdpi_requirement_type_requirement_definition[SDPi requirement type definitions].

|===
|Attribute | Applies to | Type | Required |Description

|`sdpi_req_type` | all | {`use_case_feature` \| `tech_feature` \| `ihe_profile` \| `ref_ics` \| `use_case_feature` \| `risk_mitigation` } | yes | Defines the type of requirement, from the https://profiles.ihe.net/DEV/SDPi/index.|`sdpi_req_level` | {`shall` \| `should` \| `may` } |yes | Defines the behaviour expected for compatible implementations. html#vol1_clause_sdpi_requirements_core_model[SDPi requirements core model]
|`sdpi_req_group` | all | comma separated list | no | Defines membership of ad-hoc groups. Groups may be used to select related requirements when generating ICS tables.
|`sdpi_req_specification` | all | defined _specification id_| yes| Defines the root for the requirements <<requirement-oids,ISO object identifier>>.
|`sdpi_use_case_id` | `use_case_feature` | defined _use-case id_ | yes | Defines the use-case that the requirement applies to. Automatically populated from the enclosing <<use-case>> section.
|`sdpi_ref_id` | `ref_ics` | defined _reference id_ | yes | The bibliography anchor for the entry to the referenced requirement.
|`sdpi_ref_section` | `ref_ics` | section identifier | yes | The identifier for the section containing the referenced requirement.
|`sdpi_ref_req` | `ref_ics` | requirement identifier | yes | The identifier for the requirement in the referenced standard.
|`sdpi_ses_type` | `risk_mitigation` | {`general` | `safety` | `effectiveness` | `security` | `audit` } | yes | https://profiles.ihe.net/DEV/SDPi/index.html#vol1_clause_sdpi_requirement_type_ses[Type of risk] the requirement mitigates.
|`sdpi_ses_test` | `risk_mitigation` | {`inspect` | `wire` } | yes | Mechanism for testing the requirement.
|===

[[requirement-oids]]
==== ISO object identifiers

ISO object identifiers (OIDs) are assigned and rendered with each requirement in the output file generated. They are used as the primary requirement id in implementation conformance statement tables. ISO object identifiers combine a profile- or standard-specific root identifier with requirement number, that is: `profile_oid.2.requirement_number`. The profile/standard is identified, by name, using the `sdpi_req_specification` attribute. Relationships between profile/standard names and their ISO object identifier is established using document level `sdpi_oid` scoped attributes, typically in `asciidoc/std-oid-definitions.adoc`.

Named profile and standards include:

|===
| `sdpi_req_specification` value | Profile/standard

| `sdpi` | The https://profiles.ihe.net/DEV/SDPi/index.html[SDPi] profile
| `sdpi-p` | The https://profiles.ihe.net/DEV/SDPi/index.html#vol1_clause_sdpi_p_profile[SDPi plug-and-trust] profile
| `sdpi-a` | The https://profiles.ihe.net/DEV/SDPi/index.html#vol1_clause_sdpi_a_profile[SDPi alerting] profile
| `sdpi-r` | The https://profiles.ihe.net/DEV/SDPi/index.html#vol1_clause_sdpi_r_profile[SDPi reporting] profile
| `sdpi-xC` | The https://profiles.ihe.net/DEV/SDPi/index.html#vol1_clause_sdpi_xc_profile[SDPi external control] profile
|===

Refer to the document source for corresponding ISO object identifiers for each profile.

NOTE: specification names are case-sensitive.

==== Groups

There is currently (February 2025) no constraint on groups that may be assigned to requirements. However, to promote consistency and utility, the following groups are recommended.

|===
| Group id | Description

| consumer | Applies to https://profiles.ihe.net/DEV/SDPi/index.html#vol1_clause_sdpi_p_somds_consumer[SOMDS consumer] implementations.
| provider | Applies to https://profiles.ihe.net/DEV/SDPi/index.html#vol1_clause_sdpi_p_somds_provider[SOMDS provider] implementations.
| discovery-proxy | Applies to https://profiles.ihe.net/DEV/SDPi/index.html#_discovery_proxy[managed discovery proxy] implementations.
|===

NOTE: Group names are case sensitive.

[[use-case]]
=== Defining use cases

Use case blocks, and their parts, are identified in the source by AsciiDoc https://docs.asciidoctor.org/asciidoc/latest/attributes/role/[roles], with metadata provided in block attributes. The following example AsciiDoc source defines a use-case and one scenario.

[source,asciidoc]
----
[role="use-case",sdpi_use_case_id=stad]
[sdpi_feature="Synchronized Time Across Devices"]
=== Use Case Feature 1: Synchronized time across devices (STAD)
==== Narrative
Nurse Jean attaches a ventilator to the medical device network in the ICU. It automatically obtains the correct time.
==== Benefits
Automatically acquiring the time saves the user from spending time entering the time into the device. It also guarantees that the correct time will be entered.
It is also important for all devices to have a consistent time since the data being exported to consuming devices and systems will use the time stamps from the device to mark the time that the clinical data was acquired. Since this is part of the clinical record, accuracy is very important.
==== Technical Pre-Conditions
[role=use-case-background]
====
*Given* All devices communicate using a common acronym_md_lan protocol
*And* A Time Source (TS) Service is on the acronym_md_lan network
====
==== Scenarios
[role=use-case-scenario,sdpi_scenario="Device is connected to the MD LAN network with a Time Source service"]
===== Scenario: STAD 1.1 --- Device is connected to the MD LAN network with a Time Source service
[role=use-case-steps]
====
*Given* Device has detected at least one TS Service
*When* The TS Service is operational
*Then* The device will synchronize its time with the TS Service
====
----

[[use-case-roles]]
==== Block roles

AsciiDoc https://docs.asciidoctor.org/asciidoc/latest/attributes/role/[roles] assigned to content blocks provide semantic structure to use-case definitions. The roles available for use-cases are shown in the table below.

Along with roles, use-case blocks should be logically structured in document sections. That is, background and scenarios must be sub-sections of a use-case section; steps must be within a scenario section. This structure is used to associate each content block with the appropriate use-case/scenario.

Each use-case must have at most one background section and zero or more scenarios. Each scenario section must have at most one steps content block (which may contain several <<use-case-steps,steps>>).

|===
|Role | Parent | Content block

| `use-case` | &mdash; | Defines a use-case feature.
| `use-case-background` | `use-case` | Defines technical pre-requisites for a use-case.
| `use-case-scenario` | `use-case` | Defines one scenario in the use-case.
| `use-case-steps` | `use-case-scenario` | Defines steps for a use-case scenario.
|===

[[use-case-attributes]]
==== Block attributes

[[use-case-steps]]
==== Defining steps

Steps, in background and steps content blocks, are defined in paragraphs prefixed by https://cucumber.io/docs/gherkin/reference/[Gherkin] keywords. Keywords are parsed (e.g., for exporting rich JSON data). Supported steps are shown in the table below. Steps are not case-sensitive.

|===
|Keyword | Description

| *given* | Initial context of the system, typically something that happened in the past.
| *when* | An action or event that triggers the scenario.
| *then* | Expected outcome from the scenario.
| *and* | Conjunction, used with *when* or *then* for an additional trigger/outcome that is also expected.
| *or* | Conjunction, used with *when* or *then* for an alternate trigger/outcome.
|===

[[generate-table]]
=== Inserting summary tables


== Topics Parking Lot

Include:
29 changes: 28 additions & 1 deletion testdoc/MyDocument.adoc
Original file line number Diff line number Diff line change
@@ -68,7 +68,7 @@ sdpi_requirement_table::[sdpi_req_group="consumer,discovery-proxy"]
[NORMATIVE]
====
When the vol1_spec_sdpi_p_option_discovery_proxy is enabled for a vol1_spec_sdpi_p_actor_somds_provider Actor, then it shall use the vol2_clause_dev_46, DEV-46 transaction to update the vol1_spec_sdpi_p_actor_discovery_proxy Actor on its network presence and departure.
When the vol1_spec_sdpi_p_option_discovery_proxy is enabled for a vol1_spec_sdpi_p_actor_somds_provider Actor, then it shall use the vol2_clause_dev_46, DEV-46 transaction to update the vol1_spec_sdpi_p_actor_discovery_proxy Actor on its network presence and departure.
====
[NOTE]
@@ -93,6 +93,33 @@ When the vol1_spec_sdpi_p_option_discovery_proxy is enabled for a vol1_spec_sdpi
If a vol1_spec_sdpi_p_actor_somds_provider Actor is configured or provisioned for the vol1_spec_sdpi_p_option_discovery_proxy, but the proxy system is not available, then the vol1_spec_sdpi_p_actor_somds_provider shall revert back to "ad hoc" discovery mode.
****

.R1023
[sdpi_requirement,sdpi_req_level=shall,sdpi_req_type=tech_feature,sdpi_req_group="consumer,discovery-proxy",sdpi_req_specification=sdpi-p]
****
[NORMATIVE]
====
When the managed discovery option is enabled for a SOMDS consumer, then it shall use the DEV-47 transaction to retrieve SOMDS provider network presence metadata from the Discovery Proxy actor.
====
[NOTE]
====
. When retrieving network presence metadata from a discovery proxy actor, a discovery scope may be specified as a filter to identify a specific subset of SOMDS provider systems.
. A SOMDS consumer may optionally use the DEV-47 transaction to subscribe to all metadata updates from a set of SOMDS consumer systems, essentially using the discovery proxy as a pass-through for SOMDS provider DEV-23 and DEV-24 transactions.
====
[EXAMPLE]
====
This is an example of an example block.
====
[RELATED]
====
<<ref_oasis_ws_discovery_2009>>, section 2.2.2 Managed mode.
====
****

.R0500
[sdpi_requirement#r0500,sdpi_req_level=shall]
****
99 changes: 96 additions & 3 deletions testdoc/MyDocument.html
Original file line number Diff line number Diff line change
@@ -746,6 +746,17 @@ <h2 id="_requirements_list">3 Requirements list</h2>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top">
<div class="content">
<div class="paragraph global-id">
<p>1.3.6.1.4.1.19376.1.6.2.11.2.1023</p>
</div>
</div></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#1.3.6.1.4.1.19376.1.6.2.11.2.1023">R1023</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">shall</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Technical feature</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top">
<div class="content">
@@ -840,6 +851,17 @@ <h2 id="_requirements_list">3 Requirements list</h2>
<td class="tableblock halign-left valign-top"><p class="tableblock">shall</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Technical feature</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top">
<div class="content">
<div class="paragraph global-id">
<p>1.3.6.1.4.1.19376.1.6.2.11.2.1023</p>
</div>
</div></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#1.3.6.1.4.1.19376.1.6.2.11.2.1023">R1023</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">shall</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Technical feature</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top">
<div class="content">
@@ -924,8 +946,10 @@ <h2 id="_some_requirements">4 Some requirements</h2>
</div>
<div class="exampleblock">
<div class="content">
<div class="paragraph">
<p>When the vol1_spec_sdpi_p_option_discovery_proxy is enabled for a vol1_spec_sdpi_p_actor_somds_provider Actor, then it shall use the vol2_clause_dev_46, DEV-46 transaction to update the vol1_spec_sdpi_p_actor_discovery_proxy Actor on its network presence and departure.</p>
<div class="literalblock">
<div class="content">
<pre>When the vol1_spec_sdpi_p_option_discovery_proxy is enabled for a vol1_spec_sdpi_p_actor_somds_provider Actor, then it shall use the vol2_clause_dev_46, DEV-46 transaction to update the vol1_spec_sdpi_p_actor_discovery_proxy Actor on its network presence and departure.</pre>
</div>
</div>
</div>
</div>
@@ -968,6 +992,54 @@ <h2 id="_some_requirements">4 Some requirements</h2>
</div>
</div>
</div>
<div id="1.3.6.1.4.1.19376.1.6.2.11.2.1023" class="openblock">
<div class="content">
</div>
</div>
<div id="r1023" class="sidebarblock requirement">
<div class="content">
<div class="title">
R1023 <span class="global-id"><code>(1.3.6.1.4.1.19376.1.6.2.11.2.1023)</code></span>
</div>
<div class="exampleblock">
<div class="content">
<div class="paragraph">
<p>When the managed discovery option is enabled for a SOMDS consumer, then it shall use the DEV-47 transaction to retrieve SOMDS provider network presence metadata from the Discovery Proxy actor.</p>
</div>
</div>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"><i class="fa icon-note" title="Note"></i></td>
<td class="content">
<div class="olist arabic">
<ol class="arabic">
<li><p>When retrieving network presence metadata from a discovery proxy actor, a discovery scope may be specified as a filter to identify a specific subset of SOMDS provider systems.</p></li>
<li><p>A SOMDS consumer may optionally use the DEV-47 transaction to subscribe to all metadata updates from a set of SOMDS consumer systems, essentially using the discovery proxy as a pass-through for SOMDS provider DEV-23 and DEV-24 transactions.</p></li>
</ol>
</div></td>
</tr>
</tbody>
</table>
</div>
<details><summary class="title">Example</summary>
<div class="content">
<div class="paragraph">
<p>This is an example of an example block.</p>
</div>
</div>
</details>
<details><summary class="title">Related</summary>
<div class="content">
<div class="paragraph">
<p><a href="#ref_oasis_ws_discovery_2009">[OASIS WS-Discovery:2009]</a>, section 2.2.2 Managed mode.</p>
</div>
</div>
</details>
</div>
</div>
<div id="r0500" class="sidebarblock requirement">
<div class="content">
<div class="title">
@@ -1367,6 +1439,13 @@ <h4 id="_consumer">4.3.2 Consumer</h4>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ICS-1023</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#1.3.6.1.4.1.19376.1.6.2.11.2.1023">1.3.6.1.4.1.19376.1.6.2.11.2.1023</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">m</p></td>
<td class="tableblock halign-left valign-top"></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ICS-1001</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#1.3.6.1.4.1.19376.1.6.2.x.2.1001">1.3.6.1.4.1.19376.1.6.2.x.2.1001</a></p></td>
@@ -1437,6 +1516,13 @@ <h4 id="_discovery_proxy">4.3.3 Discovery proxy</h4>
<td class="tableblock halign-left valign-top"></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ICS-1023</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#1.3.6.1.4.1.19376.1.6.2.11.2.1023">1.3.6.1.4.1.19376.1.6.2.11.2.1023</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">m</p></td>
<td class="tableblock halign-left valign-top"></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
@@ -1472,6 +1558,13 @@ <h4 id="_unfiltered">4.3.4 Unfiltered</h4>
<td class="tableblock halign-left valign-top"></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ICS-1023</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#1.3.6.1.4.1.19376.1.6.2.11.2.1023">1.3.6.1.4.1.19376.1.6.2.11.2.1023</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">m</p></td>
<td class="tableblock halign-left valign-top"></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ICS-0500</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#r0500">R0500</a></p></td>
@@ -1556,7 +1649,7 @@ <h3 id="_referenced_standards">4.4 Referenced Standards</h3>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2025-01-10 17:35:58 +1300
Last updated 2025-02-23 12:12:13 +1300
</div>
</div>
</body>
54 changes: 54 additions & 0 deletions testdoc/referenced-artifacts/sdpi-requirements.json
Original file line number Diff line number Diff line change
@@ -81,6 +81,60 @@
"relatedContent": []
}
},
"1023": {
"type": "tech-feature",
"requirementNumber": 1023,
"localId": "R1023",
"globalId": "1.3.6.1.4.1.19376.1.6.2.11.2.1023",
"level": "SHALL",
"groups": [
"consumer",
"discovery-proxy"
],
"specification": {
"normativeContent": [
{
"type": "block",
"lines": [
"When the managed discovery option is enabled for a SOMDS consumer, then it shall use the DEV-47 transaction to retrieve SOMDS provider network presence metadata from the Discovery Proxy actor."
]
}
],
"noteContent": [
{
"type": "olist",
"items": [
{
"type": "item",
"strMarker": ".",
"strText": "When retrieving network presence metadata from a discovery proxy actor, a discovery scope may be specified as a filter to identify a specific subset of SOMDS provider systems."
},
{
"type": "item",
"strMarker": ".",
"strText": "A SOMDS consumer may optionally use the DEV-47 transaction to subscribe to all metadata updates from a set of SOMDS consumer systems, essentially using the discovery proxy as a pass-through for SOMDS provider DEV-23 and DEV-24 transactions."
}
]
}
],
"exampleContent": [
{
"type": "block",
"lines": [
"This is an example of an example block."
]
}
],
"relatedContent": [
{
"type": "block",
"lines": [
"<<ref_oasis_ws_discovery_2009>>, section 2.2.2 Managed mode."
]
}
]
}
},
"500": {
"type": "tech-feature",
"requirementNumber": 500,