diff --git a/src/sequence_io/pulseq_io/components.jl b/src/sequence_io/pulseq_io/components.jl index cd5b38b750af18bffb5b1dc82b926ea80e5c197a..fd189e2d95ee60e73fb621d3a4bdd2655c3a32e5 100644 --- a/src/sequence_io/pulseq_io/components.jl +++ b/src/sequence_io/pulseq_io/components.jl @@ -15,13 +15,13 @@ struct PulseqComponents pulses:: Dict{Int, PulseqRFPulse} grads:: Dict{Int, AnyPulseqGradient} adc:: Dict{Int, PulseqADC} - extensions:: Dict{Int, PulseqExtension} + extensions:: Dict{Int, Any} PulseqComponents(shapes, pulses, grads, adc, extensions) = new( _convert_to_dict(shapes, PulseqShape), _convert_to_dict(pulses, PulseqRFPulse), _convert_to_dict(grads, AnyPulseqGradient), _convert_to_dict(adc, PulseqADC), - _convert_to_dict(extensions, PulseqExtension), + _convert_to_dict(extensions, Any), ) end diff --git a/src/sequence_io/pulseq_io/extensions.jl b/src/sequence_io/pulseq_io/extensions.jl new file mode 100644 index 0000000000000000000000000000000000000000..2984c877da57c2f48f6c287826f36f109087b48a --- /dev/null +++ b/src/sequence_io/pulseq_io/extensions.jl @@ -0,0 +1,55 @@ +""" +Defines the Pulseq IO extension interface with default implementations +""" +module Extensions +import ..Types: PulseqExtension, PulseqExtensionDefinition + +struct UnknownExtensionMapper + ext::PulseqExtensionDefinition +end + +Base.getindex(mapper::UnknownExtensionMapper, i::Int) = PulseqExtension(mapper.ext, i) + +""" + parse_extension(ext::PulseqExtensionDefinition{name}) + +Parse a Pulseq extension definition into a dictionary-like object +that maps integer reference IDs to any object describing the extension. + +This can be overriden to support the reading of a specific extension type. +For example, to define a parser for an extension with the name "LABELSET": +``` +function PulseqIO.parse_extension(ext::PulseqExtensionDefinition{:LABELSET}) + ... +end +``` +""" +function parse_extension(ext::PulseqExtensionDefinition{N}) where {N} + @warn "Parsing unknown extension: {N}" + return UnknownExtensionMapper(ext) +end + + +""" + get_extension_name(obj) + +Get the name under which the given `obj` should be stored in a Pulseq extension. + +To write an object to a Pulseq file extension, +one needs to define both this function and [`add_extension_definition`](@ref). +""" +get_extension_name(ext::PulseqExtension{N}) where {N} = N + +""" + add_extension_definition!(content::Vector{String}, obj) + +Add the object to the extension definition and returns the reference index. + +The extension definition is passed on as a vector of strings. +This vector can be appended to, when adding a new object. + +If the object is already in the `definition` the reference index of the already existing object should be returned instead. +""" +add_extension_definition!(content::Vector{String}, ext::PulseqExtension) = ext.index + +end \ No newline at end of file diff --git a/src/sequence_io/pulseq_io/parsers/extensions.jl b/src/sequence_io/pulseq_io/parsers/extensions.jl index e75298c096e09f624f27efa03b59e417f2d8d9b1..fac6d32595def5f32e8b0e38b8f3f14624666b40 100644 --- a/src/sequence_io/pulseq_io/parsers/extensions.jl +++ b/src/sequence_io/pulseq_io/parsers/extensions.jl @@ -1,14 +1,16 @@ +import ..Extensions: parse_extension, get_extension_name, add_extension_definition! + function parse_section(section::PulseqSection{:extensions}; kwargs...) current_extension = -1 pre_amble = true linked_list = Dict{Int, NTuple{3, Int}}() - extensions = Dict{Int, PulseqExtension}() + extensions = Dict{Int, PulseqExtensionDefinition}() for line in section.content if startswith(line, "extension ") pre_amble = false (_, name, str_id) = split(line) current_extension = int(str_id) - extensions[current_extension] = PulseqExtension(name, String[]) + extensions[current_extension] = PulseqExtensionDefinition{name}(String[]) elseif pre_ample (id, type, ref, next) = int.(split(line)) linked_list[id] = (type=type, ref=ref, next=next) @@ -17,15 +19,69 @@ function parse_section(section::PulseqSection{:extensions}; kwargs...) end end + extension_mappers = Dict(key => parse_extension(ext) for (key, ext) in extensions) + function get_extension_list(key::Int) if iszero(key) return Tuple{PulseqExtension, Int}[] else base = get_extension_list(linked_list[key].next) - pushfirst!(base, (extensions[linked_list[key].type], linked_list[key].ref)) + pushfirst!(base, extension_mappers[linked_list[key].type][linked_list[key].ref]) return base end end return Dict(key => get_extension_list(key) for key in keys(linked_list)) +end + +function gen_section(comp:: PulseqComponents, ::Val{:extensions}) + definitions = Dict{Symbol, PulseqExtensionDefinition}() + extensions_ref_id = Dict{Any, Int}() + for ext_vec in comp.extensions + for ext in ext_vec + name = get_extension_name(ext) + if !(name in keys(definitions)) + definitions[name] = PulseqExtensionDefinition{name}(String[]) + end + extensions_ref_id[ext] = add_extension_definition!(definitions[name].content, ext) + end + end + + definitions_order = [keys(definitions)...] + + extension_vec_id = Dict{Vector, Int}() + + labels = Tuple{Int, Int, Int}[] + function add_extension_vec!(ext_vec::Vector) + if length(ext_vec) == 0 + return 0 + end + next_id = add_extension_vec!(ext_vec[2:end]) + ext = ext_vec[1] + type_id = findfirst(get_extension_name(ext), definitions_order) + ref_id = extensions_ref_id[ext] + all_ids = (type_id, ref_id, next_id) + if all_ids in labels + extension_vec_id[ext_vec] = findfirst(lables, all_ids) + else + push!(labels, all_ids) + extension_vec_id[ext_vec] = length(lables) + end + end + add_extension_definition!.(values(comp.extensions)) + + content = String[] + for (id0, (id1, id2, id3)) in enumerate(labels) + push!(content, "$id0 $id1 $id2 $id3") + end + push!(content, "") + + for (index, name) in enumerate(definitions_order) + def = definitions[name] + push!(content, "extension $(string(name)) $(string(index))") + append!(content, def.content) + push!(content, "") + end + + return PulseqSection{:extensions}(content) end \ No newline at end of file diff --git a/src/sequence_io/pulseq_io/pulseq_io.jl b/src/sequence_io/pulseq_io/pulseq_io.jl index 278e20775d3f9439eb7fe5ac9e4ad1d7ab2e2224..e2170991e9b5d1627aa7733dcfc1ccb8f0a12e9f 100644 --- a/src/sequence_io/pulseq_io/pulseq_io.jl +++ b/src/sequence_io/pulseq_io/pulseq_io.jl @@ -6,6 +6,7 @@ The translation of these types into MRIBuilder types is defined in "../pulseq.jl """ module PulseqIO include("types.jl") +include("extensions.jl") include("basic_parsers.jl") include("sections_io.jl") include("components.jl") @@ -13,6 +14,7 @@ include("parsers/parsers.jl") include("parse_sections.jl") import .Types: PulseqSequence +import .Extensions: parse_extension, get_extension_name, add_extension_definition! """ diff --git a/src/sequence_io/pulseq_io/types.jl b/src/sequence_io/pulseq_io/types.jl index 3aa40897171687fde9ed213eed36e51a161d7fca..d093917b74f544fd98ed3317c4ce9bc568b1bc83 100644 --- a/src/sequence_io/pulseq_io/types.jl +++ b/src/sequence_io/pulseq_io/types.jl @@ -94,18 +94,17 @@ end Abstract definition of an unknown Pulseq extension. """ -struct PulseqExtensionDefinition - name :: String +struct PulseqExtensionDefinition{N} content :: Vector{String} end """ PulseqExtension(definition::PulseqExtensionDefinition, id::Int) -Reference to a specific implementation of the [`PulseqExtensionDefinition`](@ref). +Reference to a specific implementation of a [`PulseqExtensionDefinition`](@ref). """ -struct PulseqExtension <: AnyPulseqComponent - definition::PulseqExtensionDefinition +struct PulseqExtension{N} <: AnyPulseqComponent + definition::PulseqExtensionDefinition{N} id :: Int end