From 59b7dd679b63d1a39549b187adc404a0154ead0d Mon Sep 17 00:00:00 2001 From: "Martin.Otter@dlr.de" Date: Thu, 4 Aug 2022 23:34:05 +0200 Subject: [PATCH 1/2] New signal-type Map added + output of showInfo(..) improved. --- Project.toml | 2 +- README.md | 42 +++---- .../examples/fileIO/VariousTypes_compact.json | 2 +- .../fileIO/VariousTypes_prettyPrint.json | 10 +- docs/src/Functions/OverviewOfFunctions.md | 7 +- docs/src/Functions/SignalTables.md | 2 +- docs/src/Functions/Signals.md | 10 +- docs/src/index.md | 76 ++++++------ src/AbstractSignalTableInterface.jl | 2 +- src/ExampleSignalTables.jl | 2 +- src/SignalTable.jl | 65 ++++------- src/SignalTableFunctions.jl | 108 ++++++++++-------- src/SignalTables.jl | 6 +- src/Signals.jl | 48 +++++++- test/SignalTableFunctions/test_SignalTable.jl | 3 +- test/SignalTableFunctions/test_json.jl | 2 +- 16 files changed, 220 insertions(+), 167 deletions(-) diff --git a/Project.toml b/Project.toml index b1dc6e2..a33e418 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SignalTables" uuid = "3201582d-3078-4276-ba5d-0a1254d79d7c" authors = ["Martin.Otter@dlr.de "] -version = "0.3.5" +version = "0.4.0" [deps] DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" diff --git a/README.md b/README.md index ef3bc90..3f5b7ca 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,18 @@ Package [SignalTables](https://github.com/ModiaSim/SignalTables.jl) (see [docu](https://modiasim.github.io/SignalTables.jl/stable/index.html)) provides abstract and concrete types and functions for *signal tables*. +A *signal table* is basically a table where the table columns can be multi-dimensional arrays with attributes. Typically, simulation results, reference signals, table-based input signals, measurement data, look-up tables can be represented by a signal table. -A *signal table* is an *ordered dictionary* of *signals* with string keys that supports the -[Abstract Signal Table Interface](https://modiasim.github.io/SignalTables.jl/stable/Internal/AbstractSignalTableInterface.html). -A *signal* can be defined in two forms: +A *signal table* is an *ordered dictionary* of *signals* with string keys. A *signal* can be defined in the following forms: -- As [Var](https://modiasim.github.io/SignalTables.jl/stable/Functions/Signals.html#SignalTables.Var) *dictionary* that has a required *values* key representing a *signal array* of any element type as function of the independent signal(s) (or is the k-th independent signal). A *signal array* is a *multi-dimensional array* with indices `[i1,i2,...,j1,j2,...]` to hold variable elements `[j1,j2,...]` at the `[i1,i2,...]` independent signal(s). If an element of a signal array is *not defined*, it has a value of *missing*. -- As [Par](https://modiasim.github.io/SignalTables.jl/stable/Functions/Signals.html#SignalTables.Par) *dictionary* that has an optional *value* key representing a constant of any type. +- As [Var](https://modiasim.github.io/SignalTables.jl/stable/Functions/Signals.html#SignalTables.Var) *dictionary* that has a required *values* key +(or an *alias* key) representing a *signal array* of any element type as function of the independent signal(s) (or is the k-th independent signal). A *signal array* is a *multi-dimensional array* with indices `[i1,i2,...,j1,j2,...]` to hold variable elements `[j1,j2,...]` at the `[i1,i2,...]` independent signal(s). If an element of a signal array is *not defined*, it has a value of *missing*. +- As [Par](https://modiasim.github.io/SignalTables.jl/stable/Functions/Signals.html#SignalTables.Par) *dictionary* that has a required *value* key (or and *alias* key) representing a constant of any type. +- As [Map](https://modiasim.github.io/SignalTables.jl/stable/Functions/Signals.html#SignalTables.Map) *dictionary* that has no required keys and collects attributes/meta-data that are associated with a Var, Par, Map, or signal table dictionary. -In both dictionaries, additional attributes can be stored, for example *unit*, *info*, *variability* (continuous, clocked, ...), *alias*, *interpolation*, +In all these dictionaries, additional attributes can be stored, for example *unit*, *info*, *variability* (continuous, clocked, ...), *interpolation*, *extrapolation*, and user-defined attributes. This logical view is directly mapped to Julia data structures, but can be also mapped to data structures in other @@ -33,8 +34,8 @@ t = 0.0:0.1:0.5 sigTable = SignalTable( "time" => Var(values= t, unit="s", independent=true), "load.r" => Var(values= [sin.(t) cos.(t) sin.(t)], unit="m"), - "motor.angle" => Var(values= sin.(t), unit="rad", state=true), - "motor.w" => Var(values= cos.(t), unit="rad/s", integral="motor.angle"), + "motor.angle" => Var(values= sin.(t), unit="rad", state=true, der="motor.w"), + "motor.w" => Var(values= cos.(t), unit="rad/s"), "motor.w_ref" => Var(values= 0.9*[sin.(t) cos.(t)], unit = ["rad", "1/s"], info="Reference angle and speed"), "wm" => Var(alias = "motor.w"), @@ -42,10 +43,9 @@ sigTable = SignalTable( variability="clock"), "motor.w_c" => Var(values= [0.8, missing, missing, 1.5, missing, missing], variability="clocked", clock="ref.clock"), - "motor.inertia"=> Par(value = 0.02f0, unit="kg*m/s^2"), "motor.data" => Par(value = "resources/motorMap.json"), - "attributes" => Par(info = "This is a test signal table") + "attributes" => Map(experiment=Map(stoptime=0.5, interval=0.01)) ) phi_m_sig = getSignal( sigTable, "motor.angle") # = Var(values=..., unit=..., ...) @@ -62,17 +62,17 @@ Command `showInfo` generates the following output: ```julia name unit size eltypeOrType kind attributes ─────────────────────────────────────────────────────────────────────────────────────────────────────── -time "s" (6,) Float64 Var independent=true -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_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 -attributes Par info="This is a test signal table" +time "s" [6] Float64 Var independent=true +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_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 +attributes Map experiment=Map(stoptime=0.5, interval=0.01) ``` The various Julia FileIO functions can be directly used to save a signal table diff --git a/docs/resources/examples/fileIO/VariousTypes_compact.json b/docs/resources/examples/fileIO/VariousTypes_compact.json index ae8f014..0dded51 100644 --- a/docs/resources/examples/fileIO/VariousTypes_compact.json +++ b/docs/resources/examples/fileIO/VariousTypes_compact.json @@ -1 +1 @@ -{"_class":"SignalTable","_classVersion":"0.3.5","time":{"_class":"Var","values":[0.0,0.1,0.2,0.3,0.4,0.5],"unit":"s","independent":true},"load.r":{"_class":"Var","values":{"_class":"Array","eltype":"Float64","size":[6,3],"layout":"column-major","values":[0.0,0.09983341664682815,0.19866933079506122,0.29552020666133955,0.3894183423086505,0.479425538604203,1.0,0.9950041652780258,0.9800665778412416,0.955336489125606,0.9210609940028851,0.8775825618903728,0.0,0.09983341664682815,0.19866933079506122,0.29552020666133955,0.3894183423086505,0.479425538604203]},"unit":"m"},"motor.angle":{"_class":"Var","values":[0.0,0.09983341664682815,0.19866933079506122,0.29552020666133955,0.3894183423086505,0.479425538604203],"unit":"rad","state":true,"der":"motor.w"},"motor.w":{"_class":"Var","values":[1.0,0.9950041652780258,0.9800665778412416,0.955336489125606,0.9210609940028851,0.8775825618903728],"unit":"rad/s","state":true,"start":{"_class":"Number","unit":"rad*s^-1","type":"Float64","value":1.0}},"motor.w_ref":{"_class":"Var","values":{"_class":"Array","eltype":"Float64","size":[6,2],"layout":"column-major","values":[0.0,0.08985007498214534,0.1788023977155551,0.2659681859952056,0.35047650807778546,0.4314829847437827,0.9,0.8955037487502232,0.8820599200571175,0.8598028402130454,0.8289548946025966,0.7898243057013355]},"unit":["rad","1/s"],"info":"Reference angle and speed"},"wm":{"_class":"Var","unit":"rad/s","state":true,"start":{"_class":"Number","unit":"rad*s^-1","type":"Float64","value":1.0},"alias":"motor.w"},"ref.clock":{"_class":"Var","values":{"_class":"Array","eltype":"Union{Missing, Bool}","size":[6],"layout":"column-major","values":[true,null,null,true,null,null]},"variability":"clock"},"motor.w_c":{"_class":"Var","values":{"_class":"Array","eltype":"Union{Missing, Float64}","size":[6],"layout":"column-major","values":[0.6,null,null,0.8,null,null]},"variability":"clocked","clock":"ref.clock"},"motor.inertia":{"_class":"Par","value":{"_class":"Number","type":"Float32","value":0.02},"unit":"kg*m/s^2"},"motor.data":{"_class":"Par","value":"resources/motorMap.json"},"attributes":{"_class":"Par","info":"This is a test signal table"}} \ No newline at end of file +{"_class":"SignalTable","_classVersion":"0.3.6","time":{"_class":"Var","values":[0.0,0.1,0.2,0.3,0.4,0.5],"unit":"s","independent":true},"load.r":{"_class":"Var","values":{"_class":"Array","eltype":"Float64","size":[6,3],"layout":"column-major","values":[0.0,0.09983341664682815,0.19866933079506122,0.29552020666133955,0.3894183423086505,0.479425538604203,1.0,0.9950041652780258,0.9800665778412416,0.955336489125606,0.9210609940028851,0.8775825618903728,0.0,0.09983341664682815,0.19866933079506122,0.29552020666133955,0.3894183423086505,0.479425538604203]},"unit":"m"},"motor.angle":{"_class":"Var","values":[0.0,0.09983341664682815,0.19866933079506122,0.29552020666133955,0.3894183423086505,0.479425538604203],"unit":"rad","state":true,"der":"motor.w"},"motor.w":{"_class":"Var","values":[1.0,0.9950041652780258,0.9800665778412416,0.955336489125606,0.9210609940028851,0.8775825618903728],"unit":"rad/s","state":true,"start":{"_class":"Number","unit":"rad*s^-1","type":"Float64","value":1.0}},"motor.w_ref":{"_class":"Var","values":{"_class":"Array","eltype":"Float64","size":[6,2],"layout":"column-major","values":[0.0,0.08985007498214534,0.1788023977155551,0.2659681859952056,0.35047650807778546,0.4314829847437827,0.9,0.8955037487502232,0.8820599200571175,0.8598028402130454,0.8289548946025966,0.7898243057013355]},"unit":["rad","1/s"],"info":"Reference angle and speed"},"wm":{"_class":"Var","unit":"rad/s","state":true,"start":{"_class":"Number","unit":"rad*s^-1","type":"Float64","value":1.0},"alias":"motor.w"},"ref.clock":{"_class":"Var","values":{"_class":"Array","eltype":"Union{Missing, Bool}","size":[6],"layout":"column-major","values":[true,null,null,true,null,null]},"variability":"clock"},"motor.w_c":{"_class":"Var","values":{"_class":"Array","eltype":"Union{Missing, Float64}","size":[6],"layout":"column-major","values":[0.6,null,null,0.8,null,null]},"variability":"clocked","clock":"ref.clock"},"motor.inertia":{"_class":"Par","value":{"_class":"Number","type":"Float32","value":0.02},"unit":"kg*m/s^2"},"motor.data":{"_class":"Par","value":"resources/motorMap.json"},"attributes":{"_class":"Map","experiment":{"_class":"Map","stoptime":0.5,"interval":0.01}}} \ No newline at end of file diff --git a/docs/resources/examples/fileIO/VariousTypes_prettyPrint.json b/docs/resources/examples/fileIO/VariousTypes_prettyPrint.json index 6ca9979..0c449e8 100644 --- a/docs/resources/examples/fileIO/VariousTypes_prettyPrint.json +++ b/docs/resources/examples/fileIO/VariousTypes_prettyPrint.json @@ -1,6 +1,6 @@ { "_class": "SignalTable", - "_classVersion": "0.3.5", + "_classVersion": "0.3.6", "time": { "_class": "Var", "values": [ @@ -178,7 +178,11 @@ "value": "resources/motorMap.json" }, "attributes": { - "_class": "Par", - "info": "This is a test signal table" + "_class": "Map", + "experiment": { + "_class": "Map", + "stoptime": 0.5, + "interval": 0.01 + } } } diff --git a/docs/src/Functions/OverviewOfFunctions.md b/docs/src/Functions/OverviewOfFunctions.md index e2e7150..b047893 100644 --- a/docs/src/Functions/OverviewOfFunctions.md +++ b/docs/src/Functions/OverviewOfFunctions.md @@ -28,8 +28,9 @@ Note, *FileIO* functions (e.g. JSON, HDF5) can be directly used, see [FileIO Exa | [`Par`](@ref) | Returns a parameter signal definition in form of a dictionary. | | [`isVar`](@ref) | Returns true, if signal is a [`Var`](@ref). | | [`isPar`](@ref) | Returns true, if signal is a [`Par`](@ref). | -| [`isSignal`](@ref) | Returns true, if signal is a [`Var`](@ref) or a [`Par`](@ref). | -| [`showSignal`](@ref) | Prints a [`Var`](@ref)(...) or [`Par`](@ref)(...) signal to io. | +| [`isMap`](@ref) | Returns true, if signal is a [`Map`](@ref). | +| [`isSignal`](@ref) | Returns true, if signal is a [`Var`](@ref), [`Par`](@ref) or [`Map`](@ref). | +| [`showSignal`](@ref) | Prints a [`Var`](@ref), [`Par`](@ref) or [`Map`](@ref) signal to io. | | [`eltypeOrType`](@ref) | Returns eltype(..) of AbstractArray or otherwise typeof(..). | | [`quantity`](@ref) | Returns `Unitful.Quantity` from numberType and numberUnit, e.g. `quantity(Float64,u"m/s")` | | [`unitAsParseableString`](@ref) | Returns the unit as a String that can be parsed with `Unitful.uparse`, e.g. "m*s^-1" | @@ -42,7 +43,7 @@ Note, *FileIO* functions (e.g. JSON, HDF5) can be directly used, see [FileIO Exa | [`getIndependentSignalNames`](@ref) | Returns the names of the independent signals. | | [`getSignalNames`](@ref) | Returns a string vector of the signal names that are present in a signal table. | | [`hasSignal`](@ref) | Returns `true` if a signal is present in a signal table. | -| [`getSignal`](@ref) | Returns signal from a signal table as [`Var`](@ref) or as [`Par`](@ref). | +| [`getSignal`](@ref) | Returns signal from a signal table as [`Var`](@ref), [`Par`](@ref) or [`Map`](@ref). | | [`getSignalInfo`](@ref) | Returns signal with :\_typeof, :\_size keys instead of :values/:value keys. | | [`getIndependentSignalsSize`](@ref) | Returns the lengths of the independent signals as Dims. | | [`getValues`](@ref) | Returns the *values* of a [`Var`](@ref) signal from a signal table. | diff --git a/docs/src/Functions/SignalTables.md b/docs/src/Functions/SignalTables.md index 32f041b..c0593dc 100644 --- a/docs/src/Functions/SignalTables.md +++ b/docs/src/Functions/SignalTables.md @@ -18,7 +18,7 @@ The functions below operate on a *signal table* that implements the [Abstract Si | [`getIndependentSignalNames`](@ref)| Returns the names of the independent signals. | | [`getSignalNames`](@ref) | Returns a string vector of the signal names that are present in a signal table. | | [`hasSignal`](@ref) | Returns `true` if a signal is present in a signal table. | -| [`getSignal`](@ref) | Returns signal from a signal table as [`Var`](@ref) or as [`Par`](@ref). | +| [`getSignal`](@ref) | Returns signal from a signal table as [`Var`](@ref), [`Par`](@ref) or [`Map`](@ref). | | [`getSignalInfo`](@ref) | Returns signal with :\_typeof, :\_size keys instead of :values/:value keys. | | [`getIndependentSignalsSize`](@ref)| Returns the lengths of the independent signals as Dims. | | [`getValues`](@ref) | Returns the *values* of a [`Var`](@ref) signal from a signal table. | diff --git a/docs/src/Functions/Signals.md b/docs/src/Functions/Signals.md index 073bfce..b278f6f 100644 --- a/docs/src/Functions/Signals.md +++ b/docs/src/Functions/Signals.md @@ -14,19 +14,23 @@ The functions below operate on *signals*. |:--------------------------------|:-------------------------------------------------------------------------------------------| | [`Var`](@ref) | Returns a variable signal definition in form of a dictionary. | | [`Par`](@ref) | Returns a parameter signal definition in form of a dictionary. | +| [`Map`](@ref) | Returns an attribute signal definition in form of a dictionary. | | [`isVar`](@ref) | Returns true, if signal is a [`Var`](@ref). | | [`isPar`](@ref) | Returns true, if signal is a [`Par`](@ref). | -| [`isSignal`](@ref) | Returns true, if signal is a [`Var`](@ref) or a [`Par`](@ref). | -| [`showSignal`](@ref) | Prints a [`Var`](@ref)(...) or [`Par`](@ref)(...) signal to io. | -| [`eltypeOrType`](@ref) | Returns eltype of an array (but without Missing) and otherwise returns typeof. | | +| [`isMap`](@ref) | Returns true, if signal is a [`Map`](@ref). | +| [`isSignal`](@ref) | Returns true, if signal is a [`Var`](@ref), [`Par`](@ref) or [`Map`](@ref). | +| [`showSignal`](@ref) | Prints a [`Var`](@ref), [`Par`](@ref) or [`Map`](@ref) signal to io. | +| [`eltypeOrType`](@ref) | Returns eltype of an array (but without Missing) and otherwise returns typeof. | | [`quantity`](@ref) | Returns `Unitful.Quantity` from numberType and numberUnit, e.g. `quantity(Float64,u"m/s")` | | [`unitAsParseableString`](@ref) | Returns the unit as a String that can be parsed with `Unitful.uparse`, e.g. "m*s^-1" | ```@docs Var Par +Map isVar isPar +isMap isSignal showSignal eltypeOrType diff --git a/docs/src/index.md b/docs/src/index.md index 2036ab7..880a7a2 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -8,17 +8,19 @@ CurrentModule = SignalTables Package [SignalTables](https://github.com/ModiaSim/SignalTables.jl) provides abstract and concrete types and functions for *signal tables*. +A *signal table* is basically a table where the table columns can be multi-dimensional arrays with attributes. Typically, simulation results, reference signals, table-based input signals, measurement data, look-up tables can be represented by a signal table. -A *signal table* is an *ordered dictionary* of *signals* with string keys that supports the -[Abstract Signal Table Interface](https://modiasim.github.io/SignalTables.jl/stable/Internal/AbstractSignalTableInterface.html). -A *signal* can be defined in two forms: +A *signal table* is an *ordered dictionary* of *signals* with string keys. A *signal* can be defined in the following forms: -- As [Var](https://modiasim.github.io/SignalTables.jl/stable/Functions/Signals.html#SignalTables.Var) *dictionary* that has a required *values* key representing a *signal array* of any element type as function of the independent signal(s) (or is the k-th independent signal). A *signal array* is a *multi-dimensional array* with indices `[i1,i2,...,j1,j2,...]` to hold variable elements `[j1,j2,...]` at the `[i1,i2,...]` independent signal(s). If an element of a signal array is *not defined*, it has a value of *missing*. -- As [Par](https://modiasim.github.io/SignalTables.jl/stable/Functions/Signals.html#SignalTables.Par) *dictionary* that has an optional *value* key representing a constant of any type. +- As [Var](https://modiasim.github.io/SignalTables.jl/stable/Functions/Signals.html#SignalTables.Var) *dictionary* that has a required *values* key (or an *alias* key) representing a *signal array* of any element type as function of the independent signal(s), or is the k-th independent signal. A *signal array* is a *multi-dimensional array* with indices `[i1,i2,...,j1,j2,...]` to hold variable elements `[j1,j2,...]` at the `[i1,i2,...]` independent signal(s). If an element of a signal array is *not defined*, it has a value of *missing*. +- As [Par](https://modiasim.github.io/SignalTables.jl/stable/Functions/Signals.html#SignalTables.Par) *dictionary* that has a required *value* key (or and *alias* key) representing a constant of any type. +- As [Map](https://modiasim.github.io/SignalTables.jl/stable/Functions/Signals.html#SignalTables.Map) *dictionary* that has no required keys and collects attributes/meta-data that are associated with a Var, Par, Map, or signal table dictionary. + +In all these dictionaries, additional attributes can be stored, for example *unit*, *info*, *variability* (continuous, clocked, ...), *interpolation*, +*extrapolation*, and user-defined attributes. -In both dictionaries, additional attributes can be stored, for example *unit*, *info*, *variability* (continuous, clocked, ...), *alias*, *interpolation*, *extrapolation*, and user-defined attributes. ## Examples @@ -27,22 +29,20 @@ using SignalTables t = 0.0:0.1:0.5 sigTable = SignalTable( - "time" => Var(values= t, unit="s", independent=true), - "load.r" => Var(values= [sin.(t) cos.(t) sin.(t)], unit="m"), - "motor.angle" => Var(values= sin.(t), unit="rad", state=true), - "motor.w" => Var(values= cos.(t), unit="rad/s", integral="motor.angle"), - "motor.w_ref" => Var(values= 0.9*[sin.(t) cos.(t)], unit = ["rad", "1/s"], - info="Reference angle and speed"), - "wm" => Var(alias = "motor.w"), - "ref.clock" => Var(values= [true, missing, missing, true, missing, missing], - variability="clock"), - - "motor.w_c" => Var(values= [0.8, missing, missing, 1.5, missing, missing], - variability="clocked", clock="ref.clock"), - - "motor.inertia" => Par(value = 0.02f0, unit="kg*m/s^2"), - "motor.data" => Par(value = "resources/motorMap.json"), - "attributes" => Par(info = "This is a test signal table") + "time" => Var(values= t, unit="s", independent=true), + "load.r" => Var(values= [sin.(t) cos.(t) sin.(t)], unit="m"), + "motor.angle" => Var(values= sin.(t), unit="rad", state=true, der="motor.w"), + "motor.w" => Var(values= cos.(t), unit="rad/s"), + "motor.w_ref" => Var(values= 0.9*[sin.(t) cos.(t)], unit = ["rad", "1/s"], + info="Reference angle and speed"), + "wm" => Var(alias = "motor.w"), + "ref.clock" => Var(values= [true, missing, missing, true, missing, missing], + variability="clock"), + "motor.w_c" => Var(values= [0.8, missing, missing, 1.5, missing, missing], + variability="clocked", clock="ref.clock"), + "motor.inertia"=> Par(value = 0.02f0, unit="kg*m/s^2"), + "motor.data" => Par(value = "resources/motorMap.json"), + "attributes" => Map(experiment=Map(stoptime=0.5, interval=0.01)) ) phi_m_sig = getSignal( sigTable, "motor.angle") # = Var(values=..., unit=..., ...) @@ -59,19 +59,20 @@ Command `showInfo` generates the following output: ```julia name unit size eltypeOrType kind attributes ─────────────────────────────────────────────────────────────────────────────────────────────────────── -time "s" (6,) Float64 Var independent=true -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_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 -attributes Par info="This is a test signal table" +time "s" [6] Float64 Var independent=true +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_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 +attributes Map experiment=Map(stoptime=0.5, interval=0.01) ``` + The various Julia FileIO functions can be directly used to save a signal table in various formats, e.g. JSON or HDF5 (see [FileIO Examples](@ref), [json file of sigTable above](../resources/examples/fileIO/VariousTypes_prettyPrint.json) ). @@ -187,6 +188,13 @@ are different to the Python 2.x version. ## Release Notes +### Version 0.4.0 + +- New signal-type Map added (additionally to Var und Par signals) with two new functions Map(..), isMap(..). +- Output of showInfo(..) improved. +- Non-backwards compatible changes: Function showInfo(..) has optional keywords arguments + showVar, showPar, showMap, showAttributes, instead of the previous Var, Par, attributes. + ### Version 0.3.5 - @usingPlotPackage(): If SilentNoPlot selected, use "using SignalTables.SilentNoPlot" instead of "import SignalTables.SilentNoPlot: plot ..:". @@ -223,7 +231,7 @@ are different to the Python 2.x version. ### Version 0.3.0 -- Slightly non-backwards compatiable to 0.2.0. +- Slightly non-backwards compatible to 0.2.0. - Various new functions (e.g. storing a signal table in JSON format on file). - DataFrames.jl, Tables.jl are supported as signal tables. - Plotting/flattening: Support of Measurements.jl and MonteCarloMeasurements.jl diff --git a/src/AbstractSignalTableInterface.jl b/src/AbstractSignalTableInterface.jl index f8e5b2d..93b8c58 100644 --- a/src/AbstractSignalTableInterface.jl +++ b/src/AbstractSignalTableInterface.jl @@ -79,7 +79,7 @@ end """ getSignalInfo(signalTable, name::String) -Returns signal in form of a [`Var`](@ref) or a [`Par`](@ref)) without :values or :value +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) and :_size (if defined on the values) diff --git a/src/ExampleSignalTables.jl b/src/ExampleSignalTables.jl index 2f83fc8..ef69529 100644 --- a/src/ExampleSignalTables.jl +++ b/src/ExampleSignalTables.jl @@ -141,7 +141,7 @@ elseif signalTableName == "VariousTypes" "motor.inertia" => Par(value = 0.02f0, unit="kg*m/s^2"), "motor.data" => Par(value = "resources/motorMap.json"), - "attributes" => Par(info = "This is a test signal table") + "attributes" => Map(experiment=Map(stoptime=0.5, interval=0.01)) ) else diff --git a/src/SignalTable.jl b/src/SignalTable.jl index bc448c1..ee2b7ab 100644 --- a/src/SignalTable.jl +++ b/src/SignalTable.jl @@ -5,23 +5,8 @@ Returns a new SignalTable dictionary. -Arguments `args...` are dictionary pairs where `values` must be [`Var`](@ref)(...) or [`Par`](@ref)(...). Example: - -```julia -using SignalTables -using Unitful - -t = 0.0:0.1:0.5 -sigTable = SignalTable( - "time" => Var(values= t, unit="s", independent = true), - "load.r" => Var(values= [sin.(t) cos.(t) sin.(t)]), - "motor.angle" => Var(values= sin.(t), unit="rad"), - "motor.w_ref" => Var(values= cos.(t), unit="rad/s", info="Reference"), - "motor.w_m" => Var(values= Clocked(0.9*cos.(t),factor=2), unit="rad/s", info="Measured"), - "motor.inertia"=> Par(value = 0.02, unit="kg*m/s^2"), - "attributes" => Par(info = "This is a test signal table") -) -``` +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), ...` and `values` must be an `AbstractVector`. Further added signals with a `:values` key, must have the @@ -51,7 +36,7 @@ sigTable = SignalTable( variability="clocked", clock="ref.clock"), "motor.inertia"=> Par(value = 0.02f0, unit="kg*m/s^2"), "motor.data" => Par(value = "resources/motorMap.json"), - "attributes" => Par(info = "This is a test signal table") + "attributes" => Map(experiment=Map(stoptime=0.5, interval=0.01)) ) signalInfo(sigTable) @@ -60,19 +45,19 @@ signalInfo(sigTable) This results in the following output: ```julia -name unit size eltypeOrType kind attributes -───────────────────────────────────────────────────────────────────────────────────────── -time "s" (6,) Float64 Var independent=true -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_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,) Bool Var variability="clock" -motor.w_c (6,) Float64 Var variability="clocked", clock="ref.clock" -motor.inertia "kg*m/s^2" () Float32 Par -motor.data String Par -attributes Par info="This is a test signal table" +name unit size eltypeOrType kind attributes +───────────────────────────────────────────────────────────────────────────────────────────────────────── +time "s" [6] Float64 Var independent=true +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_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 +attributes Map experiment=Map(stoptime=0.5, interval=0.01) ``` The command `show(IOContext(stdout, :compact => true), sigTable)` results in the following output: @@ -90,7 +75,7 @@ SignalTable( "motor.w_c" => Var(values=Union{Missing, Float64}[0.8, missing, missing, 1.5, missing, missing], variability="clocked", clock="ref.clock"), "motor.inertia" => Par(value=0.02, unit="kg*m/s^2"), "motor.data" => Par(value="resources/motorMap.json"), - "attributes" => Par(info="This is a test signal table"), + "attributes" => Map(experiment=Map(stoptime=0.5, interval=0.01)), ) ``` """ @@ -106,7 +91,7 @@ struct SignalTable <: AbstractDict{String,Any} k = 0 for (key, sig) in args if !isSignal(sig) - error("SignalTable(\"$key\" => signal, ...): The added signal is neither a Var(..) nor a Par(..)\ntypeof(signal) = $(typeof(sig))!") + error("SignalTable(\"$key\" => signal, ...): The added signal is neither a Var(..) nor a Par(..) nor a Map(...)\ntypeof(signal) = $(typeof(sig))!") end if isVar(sig) if haskey(sig, :values) @@ -143,7 +128,7 @@ struct SignalTable <: AbstractDict{String,Any} sigAlias = dict[aliasName] sig = merge(sigAlias,sig) end - else + elseif isPar(sig) if haskey(sig, :alias) aliasName = sig[:alias] if haskey(sig,:value) @@ -202,12 +187,12 @@ end # Implementation of AbstractSignalTableInterface -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) +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 getDefaultHeading(sigTable::SignalTable)::String diff --git a/src/SignalTableFunctions.jl b/src/SignalTableFunctions.jl index efe5765..d6536d0 100644 --- a/src/SignalTableFunctions.jl +++ b/src/SignalTableFunctions.jl @@ -100,10 +100,31 @@ end const doNotShowAttributes = [:_class, :_eltypeOrType, :_size, :unit] +function showMapValue(iostr,mapValue)::Nothing + print(iostr, "Map(") + first = true + for (key,val) in mapValue + if key in doNotShowAttributes + continue + end + if first + first = false + else + print(iostr, ", ") + end + print(iostr, key, "=") + if isMap(val) + showMap(iostr, val) + end + show(iostr,val) + end + print(iostr, ")") + return nothing +end """ showInfo([io::IO=stdout,] signalTable; - sorted=false, Var=true, Par=true, attributes=true) + sorted=false, showVar=true, showPar=true, showMap=true, showAttributes=true) Writes info about a signal table to the output stream. The keyword arguments define what information shall be printed @@ -126,13 +147,11 @@ sigTable = SignalTable( "wm" => Var(alias = "motor.w"), "ref.clock" => Var(values= [true, missing, missing, true, missing, missing], variability="clock"), - "ref.trigger" => Var(values= [missing, missing, true, missing, true, true], - variability="trigger"), "motor.w_c" => Var(values= [0.8, missing, missing, 1.5, missing, missing], variability="clocked", clock="ref.clock"), "motor.inertia"=> Par(value = 0.02f0, unit="kg*m/s^2"), "motor.data" => Par(value = "resources/motorMap.json"), - "attributes" => Par(info = "This is a test signal table") + "attributes" => Map(experiment=Map(stoptime=0.5, interval=0.01)) ) signalInfo(sigTable) @@ -141,24 +160,23 @@ signalInfo(sigTable) results in the following output ```julia -name unit size eltypeOrType kind attributes -───────────────────────────────────────────────────────────────────────────────────────── -time "s" (6,) Float64 Var independent=true -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_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,) Bool Var variability="clock" -ref.trigger (6,) Bool Var variability="trigger" -motor.w_c (6,) Float64 Var variability="clocked", clock="ref.clock" -motor.inertia "kg*m/s^2" () Float32 Par -motor.data () String Par -attributes Par info="This is a test signal table" +name unit size eltypeOrType kind attributes +─────────────────────────────────────────────────────────────────────────────────────────────────────── +time "s" [6] Float64 Var independent=true +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_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 +attributes Map experiment=Map(stoptime=0.5, interval=0.01) ``` """ function showInfo(io::IO, signalTable; - sorted=false, Var=true, Par=true, attributes=true)::Nothing + sorted=false, showVar=true, showPar=true, showMap=true, showAttributes=true)::Nothing if isnothing(signalTable) @info "The call of showInfo(signalTable) is ignored, since the argument is nothing." return @@ -175,15 +193,13 @@ function showInfo(io::IO, signalTable; if sorted sigNames = sort(sigNames) end - if attributes - iostr = IOBuffer() - end + iostr = IOBuffer() for name in sigNames signal = getSignalInfo(signalTable, name) - kind = isVar(signal) ? "Var" : "Par" - if Var && kind == "Var" || Par && kind == "Par" - if attributes + kind = isVar(signal) ? "Var" : (isPar(signal) ? "Par" : "Map") + if showVar && kind == "Var" || showPar && kind == "Par" || showMap && kind == "Map" + if showAttributes first = true for (key,val) in signal if key in doNotShowAttributes @@ -196,7 +212,11 @@ function showInfo(io::IO, signalTable; print(iostr, ", ") end print(iostr, key, "=") - show(iostr, val) + if isMap(val) + showMapValue(iostr, val) + else + show(iostr, val) + end end attr = String(take!(iostr)) end @@ -205,7 +225,7 @@ function showInfo(io::IO, signalTable; valBaseType = string( get(signal, :_eltypeOrType, "") ) valBaseType = compactPaths(valBaseType) valSize = string( get(signal, :_size, "") ) - valSize = replace(valSize, " " => "") # Remove blanks + valSize = replace(valSize, "()" => "", " " => "", ",)" => "]", "(" => "[", ")" => "]") valUnit = get(signal, :unit, "") if typeof(valUnit) <: AbstractString if valUnit != "" @@ -217,29 +237,18 @@ function showInfo(io::IO, signalTable; #valUnit = replace(valUnit, "; " => ";\n") end - if independent - pushfirst!(name2 , name) - pushfirst!(unit2 , valUnit) - pushfirst!(size2 , valSize) - pushfirst!(eltypeOrType2, valBaseType) - pushfirst!(kind2 , kind) - if attributes - pushfirst!(attr2, attr) - end - else - push!(name2 , name) - push!(unit2 , valUnit) - push!(size2 , valSize) - push!(eltypeOrType2, valBaseType) - push!(kind2 , kind) - if attributes - push!(attr2, attr) - end + push!(name2 , name) + push!(unit2 , valUnit) + push!(size2 , valSize) + push!(eltypeOrType2, valBaseType) + push!(kind2 , kind) + if showAttributes + push!(attr2, attr) end end end - if attributes + if showAttributes infoTable = DataFrames.DataFrame(name=name2, unit=unit2, size=size2, eltypeOrType=eltypeOrType2, kind=kind2, attributes=attr2) else infoTable = DataFrames.DataFrame(name=name2, unit=unit2, size=size2, eltypeOrType=eltypeOrType2, kind=kind2) @@ -391,7 +400,8 @@ function getFlattenedSignal(signalTable, name::String; independentSignalsSize = getIndependentSignalsSize(signalTable) if length(independentSignalsSize) != 1 ni = length(independentSignalsSize) - error("getFlattenedSignal(.., $name) currently only supported for one independent signal,\nbut number of independent signals = $ni!") + @info "getFlattenedSignal(.., \"$name\") supported for one independent signal,\nbut number of independent signals = $(ni)! Signal is ignored." + return nothing end lenx = independentSignalsSize[1] sigPresent = false @@ -710,8 +720,10 @@ function encodeSignalTableElement(path, element) if isSignal(element) if isVar(element) jdict = OrderedDict{String,Any}("_class" => "Var") - else + elseif isPar(element) jdict = OrderedDict{String,Any}("_class" => "Par") + else + jdict = OrderedDict{String,Any}("_class" => "Map") end available = true for (key,val) in element diff --git a/src/SignalTables.jl b/src/SignalTables.jl index 60b9d58..395a8b7 100644 --- a/src/SignalTables.jl +++ b/src/SignalTables.jl @@ -1,8 +1,8 @@ module SignalTables const path = dirname(dirname(@__FILE__)) -const version = "0.3.5" -const version_SignalTable_JSON = "0.3.5" # version tag to be stored in JSON files +const version = "0.3.6" +const version_SignalTable_JSON = "0.3.6" # version tag to be stored in JSON files using OrderedCollections using Unitful @@ -13,7 +13,7 @@ import DataFrames import Pkg # Signals -export SignalType, Var, Par, isVar, isPar, isSignal, showSignal, eltypeOrType, quantity, unitAsParseableString +export SignalType, Var, Par, Map, isVar, isPar, isMap, isSignal, showSignal, eltypeOrType, quantity, unitAsParseableString # Abstract Signal Table Interface export isSignalTable, getIndependentSignalNames, getSignalNames, hasSignal, getSignal, getSignalInfo, getIndependentSignalsSize, getDefaultHeading diff --git a/src/Signals.jl b/src/Signals.jl index cc181de..aeaca68 100644 --- a/src/Signals.jl +++ b/src/Signals.jl @@ -80,7 +80,8 @@ A *signal array* has indices `[i1,i2,...,j1,j2,...]` to hold variable elements ` at the `[i1,i2,...]` independent signal(s). If an element of a signal array is *not defined* it has a value of *missing*. Furthermore, additional attributes can be stored. -The following keys are recognized (all are *optional*, but usually *:values* is present): +The following keys are recognized (all are *optional* with exception of *:values* that must be +either directly defined or via :alias): |key | value (of type String, if not obvious from context) | |:---------------|:------------------------------------------------------------------------------------------------------| @@ -134,12 +135,11 @@ The following keys are recognized (all are *optional*): | `:value` | `signal[:value]` is a constant value that holds for all values of the independent signals. | | `:unit` | String: Unit of all signal elements (parseable with `Unitful.uparse`), e.g., `"kg*m*s^2"`. Array{String,N}: `signal[:unit][j1,j2,...]` is unit of variable element `[j1,j2,...]`. | | `:info` | Short description of signal (= `description` of [FMI 3.0](https://fmi-standard.org/docs/3.0/) and of [Modelica](https://specification.modelica.org/maint/3.5/MLS.html)). | -|`:alias` | String: `signal[:value]` is a *reference* to [`getSignal`](@ref)`(signalTable, signal[:alias])[:value]`. The *reference* is set and attributes are merged when the Par-signal is added to the signal table. | +| `:alias` | String: `signal[:value]` is a *reference* to [`getSignal`](@ref)`(signalTable, signal[:alias])[:value]`. The *reference* is set and attributes are merged when the Par-signal is added to the signal table. | Additionally, any other signal attributes can be stored in `signal` with a desired key, such as *Variable Types* of [FMI 3.0](https://fmi-standard.org/docs/3.0/#definition-of-types). - # Example ```julia @@ -153,6 +153,34 @@ J_alias = Par(alias = "J") Par(; kwargs...) = newSignal(kwargs, :Par) +""" + signal = Map(; kwargs...)::OrderedDict{Symbol,Any} + +Returns a set of key/value pairs in form of a dictionary. +A Map is used to associate a set of attributes to a Variable, Parameter or Signal Table. + +The following keys are recognized (all are *optional*): + +| key | value | +|:---------|:-----------------------------------------------------| +| `:info` | Short description. | + +Additionally, any other signal attributes can be stored in `signal` with a desired key, + +# Example + +```julia +using SignalTables + +sigTable = SignalTable( + J = Par(value = 0.02), + attributes = Map(author = "xyz") +) +``` +""" +Map(; kwargs...) = newSignal(kwargs, :Map) + + """ isVar(signal) @@ -169,12 +197,20 @@ Returns true, if signal is a [`Par`](@ref). isPar(signal) = typeof(signal) <: SymbolDictType && get(signal, :_class, :_) == :Par +""" + isMap(signal) + +Returns true, if signal is a [`Map`](@ref). +""" +isMap(signal) = typeof(signal) <: SymbolDictType && get(signal, :_class, :_) == :Map + + """ isSignal(signal) -Returns true, if signal is a [`Var`](@ref) or a [`Par`](@ref). +Returns true, if signal is a [`Var`](@ref) or a [`Par`](@ref) or a [`Map`](@ref) """ -isSignal(signal) = isVar(signal) || isPar(signal) +isSignal(signal) = isVar(signal) || isPar(signal) || isMap(signal) const doNotShow = [:_class] # [:_class, :_type, :_size] @@ -189,6 +225,8 @@ function showSignal(io, sig) print(io, Var) elseif isPar(sig) print(io, Par) + elseif isMap(sig) + print(io, Map) else print(io, typeof(sig)) end diff --git a/test/SignalTableFunctions/test_SignalTable.jl b/test/SignalTableFunctions/test_SignalTable.jl index 9abfe67..5171aa4 100644 --- a/test/SignalTableFunctions/test_SignalTable.jl +++ b/test/SignalTableFunctions/test_SignalTable.jl @@ -22,7 +22,7 @@ sigTable = SignalTable( "motor.inertia"=> Par(value = 0.02f0, unit="kg*m/s^2"), "motor.data" => Par(value = "resources/motorMap.json"), - "attributes" => Par(info = "This is a test signal table") + "attributes" => Map(experiment=Map(stoptime=0.5, interval=0.01)) ) # Abstract Signal Tables Interface @@ -36,4 +36,5 @@ wm = getValues(sigTable, "wm") @test motor_w === wm showInfo(sigTable) +showInfo(sigTable, showPar=false, showAttributes=false) end \ No newline at end of file diff --git a/test/SignalTableFunctions/test_json.jl b/test/SignalTableFunctions/test_json.jl index 6d535b6..8b5e7e8 100644 --- a/test/SignalTableFunctions/test_json.jl +++ b/test/SignalTableFunctions/test_json.jl @@ -22,7 +22,7 @@ signalTable1 = SignalTable( "motor.inertia"=> Par(value = 0.02f0, unit="kg*m/s^2"), "motor.data" => Par(value = "resources/motorMap.json"), - "attributes" => Par(info = "This is a test signal table") + "attributes" => Map(info = "This is a test signal table") ) writeSignalTable("test_json_signalTable1.json", signalTable1, log=true, indent=2) From 758bc1f9b4338bcb54eb85f69cbdcd59f9788206 Mon Sep 17 00:00:00 2001 From: "Martin.Otter@dlr.de" Date: Thu, 4 Aug 2022 23:35:20 +0200 Subject: [PATCH 2/2] Version number adapted --- src/SignalTables.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SignalTables.jl b/src/SignalTables.jl index 395a8b7..7ec1571 100644 --- a/src/SignalTables.jl +++ b/src/SignalTables.jl @@ -1,8 +1,8 @@ module SignalTables const path = dirname(dirname(@__FILE__)) -const version = "0.3.6" -const version_SignalTable_JSON = "0.3.6" # version tag to be stored in JSON files +const version = "0.4.0" +const version_SignalTable_JSON = "0.4.0" # version tag to be stored in JSON files using OrderedCollections using Unitful