Skip to content

Commit

Permalink
writeSignalTable(..) further improved + new keyword arguments for get…
Browse files Browse the repository at this point in the history
…SignalNames(..)
  • Loading branch information
MartinOtter committed Aug 17, 2022
1 parent 4f3197d commit 502344d
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 79 deletions.
9 changes: 6 additions & 3 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
43 changes: 25 additions & 18 deletions src/AbstractSignalTableInterface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,44 @@ 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


"""
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

Expand All @@ -80,52 +87,52 @@ 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
signal = getSignal(signalTable,name)
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
try
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
end
end
if type_available
signal2[:_eltypeOrType] = _eltypeOrType
end
end
if size_available
signal2[:_size] = _size
end
Expand All @@ -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).
"""
Expand Down
43 changes: 28 additions & 15 deletions src/SignalTable.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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), ...`
Expand Down Expand Up @@ -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)
```
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -207,7 +220,7 @@ end

"""
toSignalTable(signalTable)::SignalTable
Returns a signalTable as instance of [`SignalTable`](@ref).
"""
function toSignalTable(sigTable)::SignalTable
Expand All @@ -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

83 changes: 40 additions & 43 deletions src/SignalTableFunctions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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

Expand Down

0 comments on commit 502344d

Please sign in to comment.