Skip to content

Commit d1f3c41

Browse files
authored
Merge pull request #576 from JuliaRobotics/feat/20Q3/small_CRUD
Small Data Types and in-memory CRUD
2 parents a6fe4ee + b4d5b97 commit d1f3c41

File tree

9 files changed

+200
-12
lines changed

9 files changed

+200
-12
lines changed

attic/InMemoryDataStore.jl

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""
2+
$(TYPEDEF)
3+
Simple in-memory data store with a specified data type and a specified key type.
4+
"""
5+
struct InMemoryDataStore{T, E <: AbstractDataEntry} <: AbstractDataStore{T}
6+
data::Dict{Symbol, T}
7+
entries::Dict{Symbol, E}
8+
end
9+
10+
"""
11+
$(SIGNATURES)
12+
Create an in-memory store using a specific data type.
13+
"""
14+
function InMemoryDataStore{T, E}() where {T, E <: AbstractDataEntry}
15+
return InMemoryDataStore{T, E}(Dict{Symbol, T}(), Dict{Symbol, E}())
16+
end
17+
18+
"""
19+
$(SIGNATURES)
20+
Create an in-memory store using binary data (UInt8) as a type.
21+
"""
22+
function InMemoryDataStore()
23+
return InMemoryDataStore{Vector{UInt8}, GeneralDataEntry}()
24+
end
25+
26+
27+
##==============================================================================
28+
## InMemoryDataStore CRUD
29+
##==============================================================================
30+
function getDataBlob(store::InMemoryDataStore{T, E}, entry::E)::Union{T, Nothing} where {T, E <: AbstractDataEntry}
31+
!haskey(store.data, entry.storeKey) && return nothing
32+
return store.data[entry.storeKey]
33+
end
34+
35+
function addDataBlob!(store::InMemoryDataStore{T, E}, entry::E, data::T)::T where {T, E <: AbstractDataEntry}
36+
haskey(store.entries, entry.storeKey) && @warn "Key '$(entry.storeKey)' already exists in the data store, overwriting!"
37+
store.entries[entry.storeKey] = entry
38+
# Update timestamp
39+
entry.lastUpdatedTimestamp = now()
40+
return store.data[entry.storeKey] = data
41+
end
42+
43+
function updateDataBlob!(store::InMemoryDataStore{T, E}, entry::E, data::T)::Union{T, Nothing} where {T, E <: AbstractDataEntry}
44+
!haskey(store.entries, entry.storeKey) && (@error "Key '$(entry.storeKey)' doesn't exist in the data store!"; return nothing)
45+
store.entries[entry.storeKey] = entry
46+
# Update timestamp
47+
entry.lastUpdatedTimestamp = now()
48+
return store.data[entry.storeKey] = data
49+
end
50+
51+
function deleteDataBlob!(store::InMemoryDataStore{T, E}, entry::E)::T where {T, E <: AbstractDataEntry}
52+
data = getDataBlob(store, entry)
53+
data == nothing && return nothing
54+
delete!(store.data, entry.storeKey)
55+
delete!(store.entries, entry.storeKey)
56+
return data
57+
end
58+
59+
function listDataBlobs(store::InMemoryDataStore{T, E})::Vector{E} where {T, E <: AbstractDataEntry}
60+
return collect(values(store.entries))
61+
end

src/Deprecated.jl

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ listDataBlobs(store::AbstractDataStore) = error("AbstractDataStore $(typeof(stor
8585

8686

8787

88-
8988
"""
9089
$(SIGNATURES)
9190
Add Big Data Entry to distributed factor graph.
@@ -188,4 +187,20 @@ function Base.getproperty(x::InferenceVariable, f::Symbol)
188187
end
189188
end
190189
return getfield(x,f)
191-
end
190+
end
191+
192+
193+
194+
# SmallData is now a Symbol Dict
195+
function createSymbolDict(d::Dict{String, String})
196+
newsmalld = Dict{Symbol,SmallDataTypes}()
197+
for p in pairs(d)
198+
push!(newsmalld, Symbol(p.first) => p.second)
199+
end
200+
return newsmalld
201+
end
202+
203+
@deprecate setSmallData!(v::DFGVariable, smallData::Dict{String, String}) setSmallData!(v, createSymbolDict(smallData))
204+
205+
206+

src/DistributedFactorGraphs.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ export getSolverData
123123

124124
export getVariableType
125125

126+
# Small Data CRUD
127+
export SmallDataTypes, getSmallData, addSmallData!, updateSmallData!, deleteSmallData!, listSmallData, emptySmallData!
126128
export getSmallData, setSmallData!
127129

128130

src/entities/DFGVariable.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,8 @@ getEstimateFields(::MeanMaxPPE) = [:suggested, :max, :mean]
217217
## DFG Variables
218218
##==============================================================================
219219

220+
const SmallDataTypes = Union{Int, Float64, String, Bool, Vector{Int}, Vector{Float64}, Vector{String}, Vector{Bool}}
221+
220222
##------------------------------------------------------------------------------
221223
## DFGVariable lv2
222224
##------------------------------------------------------------------------------
@@ -248,7 +250,7 @@ struct DFGVariable{T<:InferenceVariable} <: AbstractDFGVariable
248250
solverDataDict::Dict{Symbol, VariableNodeData{T}}
249251
"""Dictionary of small data associated with this variable.
250252
Accessors: [`getSmallData`](@ref), [`setSmallData!`](@ref)"""
251-
smallData::Dict{String, String}#Ref{Dict{String, String}} #why was Ref here?
253+
smallData::Dict{Symbol, SmallDataTypes}
252254
"""Dictionary of large data associated with this variable.
253255
Accessors: [`addDataEntry!`](@ref), [`getDataEntry`](@ref), [`updateDataEntry!`](@ref), and [`deleteDataEntry!`](@ref)"""
254256
dataDict::Dict{Symbol, AbstractDataEntry}
@@ -270,7 +272,7 @@ function DFGVariable(label::Symbol, softtype::T;
270272
tags::Set{Symbol}=Set{Symbol}(),
271273
estimateDict::Dict{Symbol, <: AbstractPointParametricEst}=Dict{Symbol, MeanMaxPPE}(),
272274
solverDataDict::Dict{Symbol, VariableNodeData{T}}=Dict{Symbol, VariableNodeData{T}}(),
273-
smallData::Dict{String, String}=Dict{String, String}(),
275+
smallData::Dict{Symbol, SmallDataTypes}=Dict{Symbol, SmallDataTypes}(),
274276
dataDict::Dict{Symbol, AbstractDataEntry}=Dict{Symbol,AbstractDataEntry}(),
275277
solvable::Int=1) where {T <: InferenceVariable}
276278

@@ -287,7 +289,7 @@ function DFGVariable(label::Symbol,
287289
nstime::Nanosecond = Nanosecond(0),
288290
tags::Set{Symbol}=Set{Symbol}(),
289291
estimateDict::Dict{Symbol, <: AbstractPointParametricEst}=Dict{Symbol, MeanMaxPPE}(),
290-
smallData::Dict{String, String}=Dict{String, String}(),
292+
smallData::Dict{Symbol, SmallDataTypes}=Dict{Symbol, SmallDataTypes}(),
291293
dataDict::Dict{Symbol, AbstractDataEntry}=Dict{Symbol,AbstractDataEntry}(),
292294
solvable::Int=1) where {T <: InferenceVariable}
293295
if timestamp isa DateTime

src/services/AbstractDFG.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,6 @@ end
166166
##==============================================================================
167167

168168
#NOTE with API standardization this should become something like:
169-
# JT, I however do not feel we should force it, as I prever dot notation
170169
getUserData(dfg::AbstractDFG, key::Symbol)::String = dfg.userData[key]
171170
getRobotData(dfg::AbstractDFG, key::Symbol)::String = dfg.robotData[key]
172171
getSessionData(dfg::AbstractDFG, key::Symbol)::String = dfg.sessionData[key]

src/services/DFGVariable.jl

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,19 +272,88 @@ setSolverData!(v::DFGVariable, data::VariableNodeData, key::Symbol=:default) = v
272272
"""
273273
$(SIGNATURES)
274274
Get the small data for a variable.
275+
Note: Rather use SmallData CRUD
275276
"""
276-
getSmallData(v::DFGVariable)::Dict{String, String} = v.smallData
277+
getSmallData(v::DFGVariable) = v.smallData
277278

278279
"""
279280
$(SIGNATURES)
280281
Set the small data for a variable.
281282
This will overwrite old smallData.
283+
Note: Rather use SmallData CRUD
282284
"""
283-
function setSmallData!(v::DFGVariable, smallData::Dict{String, String})::Dict{String, String}
285+
function setSmallData!(v::DFGVariable, smallData::Dict{Symbol, SmallDataTypes})
284286
empty!(v.smallData)
285287
merge!(v.smallData, smallData)
286288
end
287289

290+
# Generic SmallData CRUD
291+
# TODO optimize for difference in in-memory by extending in other drivers.
292+
293+
"""
294+
$(SIGNATURES)
295+
Get the small data entry at `key` for variable `label` in `dfg`
296+
"""
297+
function getSmallData(dfg::AbstractDFG, label::Symbol, key::Symbol)
298+
getVariable(dfg, label).smallData[key]
299+
end
300+
301+
"""
302+
$(SIGNATURES)
303+
Add a small data pair `key=>value` for variable `label` in `dfg`
304+
"""
305+
function addSmallData!(dfg::AbstractDFG, label::Symbol, pair::Pair{Symbol, <:SmallDataTypes})
306+
v = getVariable(dfg, label)
307+
haskey(v.smallData, pair.first) && error("$(pair.first) already exists.")
308+
push!(v.smallData, pair)
309+
updateVariable!(dfg, v)
310+
return v.smallData #or pair TODO
311+
end
312+
313+
"""
314+
$(SIGNATURES)
315+
Update a small data pair `key=>value` for variable `label` in `dfg`
316+
"""
317+
function updateSmallData!(dfg::AbstractDFG, label::Symbol, pair::Pair{Symbol, <:SmallDataTypes})
318+
v = getVariable(dfg, label)
319+
!haskey(v.smallData, pair.first) && @warn("$(pair.first) does not exist, adding.")
320+
push!(v.smallData, pair)
321+
updateVariable!(dfg, v)
322+
return v.smallData #or pair TODO
323+
end
324+
325+
"""
326+
$(SIGNATURES)
327+
Delete a small data entry at `key` for variable `label` in `dfg`
328+
"""
329+
function deleteSmallData!(dfg::AbstractDFG, label::Symbol, key::Symbol)
330+
v = getVariable(dfg, label)
331+
rval = pop!(v.smallData, key)
332+
updateVariable!(dfg, v)
333+
return rval
334+
end
335+
336+
"""
337+
$(SIGNATURES)
338+
List all small data keys for a variable `label` in `dfg`
339+
"""
340+
function listSmallData(dfg::AbstractDFG, label::Symbol)
341+
v = getVariable(dfg, label)
342+
return collect(keys(v.smallData)) #or pair TODO
343+
end
344+
345+
"""
346+
$(SIGNATURES)
347+
Empty all small data from variable `label` in `dfg`
348+
"""
349+
function emptySmallData!(dfg::AbstractDFG, label::Symbol)
350+
v = getVariable(dfg, label)
351+
empty!(v.smallData)
352+
updateVariable!(dfg, v)
353+
return v.smallData #or pair TODO
354+
end
355+
356+
288357
##------------------------------------------------------------------------------
289358
## Data Entries and Blobs
290359
##------------------------------------------------------------------------------

src/services/Serialization.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ function unpackVariable(dfg::G,
3939
tags = Symbol.(packedProps["tags"])
4040
end
4141
ppeDict = unpackPPEs ? JSON2.read(packedProps["ppeDict"], Dict{Symbol, MeanMaxPPE}) : Dict{Symbol, MeanMaxPPE}()
42-
smallData = JSON2.read(packedProps["smallData"], Dict{String, String})
42+
smallData = JSON2.read(packedProps["smallData"], Dict{Symbol, SmallDataTypes})
4343

4444
softtypeString = packedProps["softtype"]
4545
softtype = getTypeFromSerializationModule(dfg, Symbol(softtypeString))

test/interfaceTests.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ end
5656
@test printFactor(fac1) == nothing
5757

5858
@test printVariable(iobuf, var1, skipfields=[:timestamp, :solver, :ppe, :nstime]) == nothing
59-
@test String(take!(iobuf)) == "DFGVariable{TestSofttype1}\nlabel:\n:a\ntags:\nSet([:VARIABLE, :POSE])\nsmallData:\nDict(\"small\"=>\"data\")\ndataDict:\nDict{Symbol,AbstractDataEntry}()\nsolvable:\n0\n"
59+
@test String(take!(iobuf)) == "DFGVariable{TestSofttype1}\nlabel:\n:a\ntags:\nSet([:VARIABLE, :POSE])\nsmallData:\nDict{Symbol,Union{Bool, Float64, Int64, Array{Bool,1}, Array{Float64,1}, Array{Int64,1}, Array{String,1}, String}}(:small=>\"data\")\ndataDict:\nDict{Symbol,AbstractDataEntry}()\nsolvable:\n0\n"
6060

6161
@test printVariable(iobuf, var1, short=true) == nothing
6262
@test String(take!(iobuf)) == "DFGVariable{TestSofttype1}\nlabel: a\ntags: Set([:VARIABLE, :POSE])\nsize marginal samples: (1, 1)\nkde bandwidths: [0.0]\nNo PPEs\n"
@@ -108,6 +108,10 @@ end
108108
VSDTestBlock!(fg1, var1)
109109
end
110110

111+
@testset "SmallData CRUD" begin
112+
smallDataTestBlock!(fg1)
113+
end
114+
111115
@testset "Data Entries and Blobs" begin
112116
if typeof(fg1) <: InMemoryDFGTypes
113117
DataEntriesTestBlock!(fg1, var2)

test/testBlocks.jl

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ function DFGVariableSCA()
238238

239239
v1_lbl = :a
240240
v1_tags = Set([:VARIABLE, :POSE])
241-
small = Dict("small"=>"data")
241+
small = Dict{Symbol, SmallDataTypes}(:small=>"data")
242242
testTimestamp = now(localzone())
243243
# Constructors
244244
v1 = DFGVariable(v1_lbl, TestSofttype1(), tags=v1_tags, solvable=0, solverDataDict=Dict(:default=>VariableNodeData{TestSofttype1}()))
@@ -262,7 +262,7 @@ function DFGVariableSCA()
262262

263263
@test getPPEDict(v1) == v1.ppeDict
264264

265-
@test getSmallData(v1) == Dict{String,String}()
265+
@test getSmallData(v1) == Dict{Symbol,SmallDataTypes}()
266266

267267
@test getSofttype(v1) == TestSofttype1()
268268

@@ -767,6 +767,42 @@ function VSDTestBlock!(fg, v1)
767767

768768
end
769769

770+
function smallDataTestBlock!(fg)
771+
772+
@test listSmallData(fg, :a) == Symbol[:small]
773+
@test listSmallData(fg, :b) == Symbol[]
774+
@test small = getSmallData(fg, :a, :small) == "data"
775+
776+
@test addSmallData!(fg, :a, :a=>5) == getVariable(fg, :a).smallData
777+
@test addSmallData!(fg, :a, :b=>10.0) == getVariable(fg, :a).smallData
778+
@test addSmallData!(fg, :a, :c=>true) == getVariable(fg, :a).smallData
779+
@test addSmallData!(fg, :a, :d=>"yes") == getVariable(fg, :a).smallData
780+
@test addSmallData!(fg, :a, :e=>[1, 2, 3]) == getVariable(fg, :a).smallData
781+
@test addSmallData!(fg, :a, :f=>[1.4, 2.5, 3.6]) == getVariable(fg, :a).smallData
782+
@test addSmallData!(fg, :a, :g=>["yes", "maybe"]) == getVariable(fg, :a).smallData
783+
@test addSmallData!(fg, :a, :h=>[true, false]) == getVariable(fg, :a).smallData
784+
785+
@test_throws ErrorException addSmallData!(fg, :a, :a=>3)
786+
@test updateSmallData!(fg, :a, :a=>3) == getVariable(fg, :a).smallData
787+
788+
@test_throws MethodError addSmallData!(fg, :a, :no=>0x01)
789+
@test_throws MethodError addSmallData!(fg, :a, :no=>1f0)
790+
@test_throws MethodError addSmallData!(fg, :a, :no=>Nanosecond(3))
791+
@test_throws MethodError addSmallData!(fg, :a, :no=>[0x01])
792+
@test_throws MethodError addSmallData!(fg, :a, :no=>[1f0])
793+
@test_throws MethodError addSmallData!(fg, :a, :no=>[Nanosecond(3)])
794+
795+
@test deleteSmallData!(fg, :a, :a) == 3
796+
@test updateSmallData!(fg, :a, :a=>3) == getVariable(fg, :a).smallData
797+
@test length(listSmallData(fg, :a)) == 9
798+
emptySmallData!(fg, :a)
799+
@test length(listSmallData(fg, :a)) == 0
800+
801+
end
802+
803+
804+
805+
770806
function DataEntriesTestBlock!(fg, v2)
771807
# "Data Entries"
772808

0 commit comments

Comments
 (0)