diff --git a/docs/src/index.md b/docs/src/index.md index 89f4037..7dcfffa 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -190,9 +190,12 @@ are different to the Python 2.x version. ### Version 0.4.1 -- writeSignalTable(..., log=false): - - Info messages about not written elements only printed if log=true. - - Some issues corrected when writing signals. +- getSignalNames(signalTable; getVar=true, getPar=true, getMap=true): New keyword arguments + getVar, getPar, getMap to only return names of the specified signal categories. + +- writeSignalTable(...): Some issues corrected. + +- @error replaced by error(..). ### Version 0.4.0 diff --git a/src/AbstractSignalTableInterface.jl b/src/AbstractSignalTableInterface.jl index 93b8c58..256559a 100644 --- a/src/AbstractSignalTableInterface.jl +++ b/src/AbstractSignalTableInterface.jl @@ -23,22 +23,29 @@ function getIndependentSignalNames(obj) if Tables.istable(obj) && Tables.columnaccess(obj) return [string(Tables.columnnames(obj)[1])] else - @error "getIndependentSignalNames(obj) is not supported for typeof(obj) = " * string(typeof(obj)) + error("getIndependentSignalNames(obj) is not supported for typeof(obj) = " * string(typeof(obj))) end end """ - getSignalNames(signalTable)::Vector{String} + getSignalNames(signalTable; getVar=true, getPar=true, getMap=true)::Vector{String} Returns a string vector of the signal names that are present in signalTable (including independent signal names). +- If getVar=true, Var(..) variables are included. +- If getPar=true, Par(..) variables are included. +- If getMap=true, Map(..) variables are included. """ -function getSignalNames(obj) +function getSignalNames(obj; getVar=true, getPar=true, getMap=true) if Tables.istable(obj) && Tables.columnaccess(obj) - return string.(Tables.columnnames(obj)) + if !getVar + return String[] + else + return string.(Tables.columnnames(obj)) + end else - @error "getSignalNames(obj) is not supported for typeof(obj) = " * string(typeof(obj)) + error("getSignalNames(obj) is not supported for typeof(obj) = " * string(typeof(obj))) end end @@ -46,14 +53,14 @@ end """ getSignal(signalTable, name::String) -Returns signal `name` from `signalTable` (that is a [`Var`](@ref) or a [`Par`](@ref)). +Returns signal `name` from `signalTable` (that is a [`Var`](@ref), a [`Par`](@ref) or a [`Map`](@ref)). If `name` does not exist, an error is raised. """ function getSignal(obj, name::String) if Tables.istable(obj) && Tables.columnaccess(obj) return Var(values= Tables.getcolumn(obj, Symbol(name))) else - @error "getSignal(obj, \"$name\") is not supported for typeof(obj) = " * string(typeof(obj)) + error("getSignal(obj, \"$name\") is not supported for typeof(obj) = " * string(typeof(obj))) end end @@ -80,13 +87,13 @@ end getSignalInfo(signalTable, name::String) Returns signal, but [`Var`](@ref) or [`Par`](@ref)) without :values or :value -but instead with :_eltypeOrType (eltype of the values if AbstractArray, otherwise typeof the values) +but instead with :_eltypeOrType (eltype of the values if AbstractArray, otherwise typeof the values) and :_size (if defined on the values) - + If `name` does not exist, an error is raised. This function is useful if only the attributes of a signal are needed, but not their values -(returning the attributes might be a *cheap* operation, whereas returning the values +(returning the attributes might be a *cheap* operation, whereas returning the values might be an *expensive* operation). """ function getSignalInfo(signalTable, name::String)::SymbolDictType @@ -94,10 +101,10 @@ function getSignalInfo(signalTable, name::String)::SymbolDictType signal2 = copy(signal) delete!(signal2, :values) delete!(signal2, :value) - _eltypeOrType = nothing + _eltypeOrType = nothing _size = nothing type_available = false - size_available = false + size_available = false if isVar(signal) if haskey(signal, :values) type_available = true @@ -105,19 +112,19 @@ function getSignalInfo(signalTable, name::String)::SymbolDictType sig = signal[:values] _eltypeOrType = eltypeOrType(sig) _size = size(sig) - size_available = true + size_available = true catch size_available = false end end else if haskey(signal, :value) - type_available = true + type_available = true try sig = signal[:value] - _eltypeOrType = eltypeOrType(sig) + _eltypeOrType = eltypeOrType(sig) _size = size(sig) - size_available = true + size_available = true catch size_available = false end @@ -125,7 +132,7 @@ function getSignalInfo(signalTable, name::String)::SymbolDictType end if type_available signal2[:_eltypeOrType] = _eltypeOrType - end + end if size_available signal2[:_size] = _size end @@ -136,7 +143,7 @@ end """ getIndependentSignalsSize(signalTable)::Dims -Returns the lengths of the independent signals as Dims. +Returns the lengths of the independent signals as Dims. E.g. for one independent signal of length 5 return (5,), or for two independent signals of length 5 and 7 return (5,7). """ diff --git a/src/SignalTable.jl b/src/SignalTable.jl index ee2b7ab..f620e0c 100644 --- a/src/SignalTable.jl +++ b/src/SignalTable.jl @@ -5,7 +5,7 @@ Returns a new SignalTable dictionary. -Arguments `args...` are dictionary pairs where `values` must be [`Var`](@ref)(...) and/or +Arguments `args...` are dictionary pairs where `values` must be [`Var`](@ref)(...) and/or [`Par`](@ref)(...) and/or [`Map`](@ref)(...). Example: The *first* argument must define the *independent* signal, that is, `Var(values=..., independent=true), ...` @@ -48,15 +48,15 @@ This results in the following output: name unit size eltypeOrType kind attributes ───────────────────────────────────────────────────────────────────────────────────────────────────────── time "s" [6] Float64 Var independent=true -load.r "m" [6,3] Float64 Var +load.r "m" [6,3] Float64 Var motor.angle "rad" [6] Float64 Var state=true, der="motor.w" -motor.w "rad/s" [6] Float64 Var +motor.w "rad/s" [6] Float64 Var motor.w_ref ["rad", "1/s"] [6,2] Float64 Var info="Reference angle and speed" wm "rad/s" [6] Float64 Var alias="motor.w" ref.clock [6] Union{Missing,Bool} Var variability="clock" motor.w_c [6] Union{Missing,Float64} Var variability="clocked", clock="ref.clock" -motor.inertia "kg*m/s^2" Float32 Par -motor.data String Par +motor.inertia "kg*m/s^2" Float32 Par +motor.data String Par attributes Map experiment=Map(stoptime=0.5, interval=0.01) ``` @@ -98,7 +98,7 @@ struct SignalTable <: AbstractDict{String,Any} sig_values = sig[:values] if !(typeof(sig_values) <: AbstractArray) error("SignalTable(\"$key\" => signal, ...): typeof(signal[:values]) = $(typeof(sig_values)) but must be an `<: AbstractArray`!") - end + end if get(sig, :independent, false) k += 1 ndims_sig = ndims(sig_values) @@ -113,11 +113,11 @@ struct SignalTable <: AbstractDict{String,Any} error("SignalTable(\"$key\" => signal, ...): size(signal[:values],$i) = $(size(sig_values,i)) but must be $val (= length of independent signal)!") end end - end + end else # Needs not have :values, e.g. alias # error("SignalTable(\"$key\" => signal, ...) is a Var(..) and has no key :values which is required!") - end + end if haskey(sig, :alias) aliasName = sig[:alias] if haskey(sig,:values) @@ -126,7 +126,7 @@ struct SignalTable <: AbstractDict{String,Any} error("SignalTable(\"$key\" => Var(alias=\"$aliasName\"...): referenced signal does not exist.") end sigAlias = dict[aliasName] - sig = merge(sigAlias,sig) + sig = merge(sigAlias,sig) end elseif isPar(sig) if haskey(sig, :alias) @@ -190,13 +190,26 @@ end isSignalTable( sigTable::SignalTable) = true getIndependentSignalNames(sigTable::SignalTable) = sigTable.independendentSignalNames getIndependentSignalsSize(sigTable::SignalTable) = sigTable.independentSignalsSize -getSignalNames(sigTable::SignalTable) = setdiff(String.(keys(sigTable)), ["_class"]) getSignal( sigTable::SignalTable, name::String) = sigTable[name] hasSignal( sigTable::SignalTable, name::String) = haskey(sigTable, name) +function getSignalNames(sigTable::SignalTable; getVar=true, getPar=true, getMap=true) + if getVar && getPar && getMap + sigNames = setdiff(String.(keys(sigTable)), ["_class"]) + else + sigNames = String[] + for (key,value) in sigTable.dict + if getVar && isVar(value) || + getPar && isPar(value) || + getMap && isMap(value) + push!(sigNames, key) + end + end + end + return sigNames +end - -function getDefaultHeading(sigTable::SignalTable)::String - attr = get(sigTable, "attributes", "") +function getDefaultHeading(sigTable::SignalTable)::String + attr = get(sigTable, "attributes", "") if attr == "" return "" else @@ -207,7 +220,7 @@ end """ toSignalTable(signalTable)::SignalTable - + Returns a signalTable as instance of [`SignalTable`](@ref). """ function toSignalTable(sigTable)::SignalTable @@ -217,7 +230,7 @@ function toSignalTable(sigTable)::SignalTable sigTable2 = SignalTable() for name in getSignalNames(sigTable) sigTable2[name] = getSignal(sigTable,name) - end + end return sigTable2 end diff --git a/src/SignalTableFunctions.jl b/src/SignalTableFunctions.jl index 4648f6f..55dbcf3 100644 --- a/src/SignalTableFunctions.jl +++ b/src/SignalTableFunctions.jl @@ -189,7 +189,7 @@ function showInfo(io::IO, signalTable; kind2 = String[] attr2 = String[] - sigNames = getSignalNames(signalTable) + sigNames = getSignalNames(signalTable, getVar=showVar, getPar=showPar, getMap=showMap) if sorted sigNames = sort(sigNames) end @@ -672,7 +672,7 @@ getHeading(signalTable, heading::AbstractString) = heading != "" ? heading : get import JSON -const TypesWithoutEncoding = Set(["Float64", "Int64", "Bool", "String", "Symbol"]) +const TypesWithoutEncoding = Set([Float64, Int64, Bool, String, Symbol]) appendNames(name1, name2) = name1 == "" ? name2 : name1 * "." * string(name2) @@ -710,6 +710,11 @@ function encodeSignalTable(signalTable; signalNames=nothing, log=false) end end +arrayElementBaseType(::Type{T}) where {T} = T +arrayElementBaseType(::Type{Array{T,N}}) where {T,N} = arrayElementBaseType(T) +arrayElementBaseType(::Type{Union{Missing,T}}) where {T} = T +arrayElementBaseType(A::Type{<:AbstractArray}) = arrayElementBaseType(eltype(A)) + """ jsigDict = encodeSignalTableElement(path, signalTableElement; log=false) @@ -727,7 +732,7 @@ function encodeSignalTableElement(path, element; log=false) end available = false for (key,val) in element - if key != ":_class" + if key != :_class encodedSignal = encodeSignalTableElement(appendNames(path,key),val, log=log) if !isnothing(encodedSignal) available = true @@ -741,61 +746,53 @@ function encodeSignalTableElement(path, element; log=false) return nothing end -# elseif typeof(element) <: AbstractArray && (elementBaseType(eltype(element)) <: Number || elementBaseType(eltype(element)) <: String) - elseif typeof(element) <: AbstractArray - if ndims(element) == 1 - eltypeElement = eltype(element) - if string(eltypeElement) in TypesWithoutEncoding || eltypeElement <: AbstractArray && ndims(eltypeElement) == 1 + else + elementType = typeof(element) + if elementType <: AbstractArray && (arrayElementBaseType(elementType) <: Number || arrayElementBaseType(elementType) <: String) + if ndims(element) == 1 && arrayElementBaseType(elementType) in TypesWithoutEncoding return element else - if log - @info "$path::$(typeof(element)) is ignored, because mapping to JSON not known" + elunit = unitAsParseableString(element) + if elunit == "" + jdict = OrderedDict{String,Any}("_class" => "Array", + "eltype" => replace(string(eltype(element)), " " => ""), + "size" => Int[i for i in size(element)], + "layout" => "column-major", + "values" => reshape(element, length(element))) + else + element = ustrip.(element) + jdict = OrderedDict{String,Any}("_class" => "Array", + "unit" => elunit, + "eltype" => replace(string(eltype(element)), " " => ""), + "size" => Int[i for i in size(element)], + "layout" => "column-major", + "values" => reshape(element, length(element))) end - return nothing + return jdict end - else + + elseif elementType in TypesWithoutEncoding + return element + + elseif elementType <: Number elunit = unitAsParseableString(element) if elunit == "" - jdict = OrderedDict{String,Any}("_class" => "Array", - "eltype" => string(eltype(element)), - "size" => Int[i for i in size(element)], - "layout" => "column-major", - "values" => reshape(element, length(element))) + jdict = OrderedDict{String,Any}("_class" => "Number", + "type" => replace(string(typeof(element)), " " => ""), + "value" => element) else element = ustrip.(element) - jdict = OrderedDict{String,Any}("_class" => "Array", + jdict = OrderedDict{String,Any}("_class" => "Number", "unit" => elunit, - "eltype" => string(eltype(element)), - "size" => Int[i for i in size(element)], - "layout" => "column-major", - "values" => reshape(element, length(element))) + "type" => replace(string(typeof(element)), " " => ""), + "value" => element) end return jdict - end - - elseif string(typeof(element)) in TypesWithoutEncoding - return element - elseif typeof(element) <: Number - elunit = unitAsParseableString(element) - if elunit == "" - jdict = OrderedDict{String,Any}("_class" => "Number", - "type" => typeof(element), - "value" => element) else - element = ustrip.(element) - jdict = OrderedDict{String,Any}("_class" => "Number", - "unit" => elunit, - "type" => typeof(element), - "value" => element) - end - return jdict - - else - if log @info "$path::$(typeof(element)) is ignored, because mapping to JSON not known" + return nothing end - return nothing end end