mirror of https://gitlab.com/pamhyr/pamhyr2
Compare commits
No commits in common. "e8f1f0ac43fe3c510a11aa51ab208f7339446289" and "a61d130baa12a1041d15b4634e1645e3108b316e" have entirely different histories.
e8f1f0ac43
...
a61d130baa
|
|
@ -875,9 +875,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||||
if z in self._get_water_limits_cache:
|
if z in self._get_water_limits_cache:
|
||||||
return self._get_water_limits_cache[z]
|
return self._get_water_limits_cache[z]
|
||||||
|
|
||||||
v = self.get_water_limits_compute(z)
|
return self.get_water_limits_compute(z)
|
||||||
|
|
||||||
return v
|
|
||||||
|
|
||||||
def get_water_limits_compute(self, z):
|
def get_water_limits_compute(self, z):
|
||||||
"""
|
"""
|
||||||
|
|
@ -885,59 +883,54 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||||
"""
|
"""
|
||||||
# Get the index of first point with elevation lesser than water
|
# Get the index of first point with elevation lesser than water
|
||||||
# elevation (for the right and left river side)
|
# elevation (for the right and left river side)
|
||||||
i_left = next(
|
i_left = -1
|
||||||
filter(
|
i_right = -1
|
||||||
lambda i: self.point(i).z <= z,
|
|
||||||
range(self.number_points)
|
|
||||||
),
|
|
||||||
-1
|
|
||||||
)
|
|
||||||
|
|
||||||
i_right = next(
|
for i in range(self.number_points):
|
||||||
filter(
|
if self.point(i).z <= z:
|
||||||
lambda i: self.point(i).z <= z,
|
i_left = i
|
||||||
reversed(range(self.number_points))
|
break
|
||||||
),
|
|
||||||
-1
|
for i in reversed(range(self.number_points)):
|
||||||
)
|
if self.point(i).z <= z:
|
||||||
|
i_right = i
|
||||||
|
break
|
||||||
|
|
||||||
# Interpolate points at river left side
|
# Interpolate points at river left side
|
||||||
if (i_left > 0):
|
if (i_left > 0):
|
||||||
pi = self.point(i_left)
|
if abs(self.point(i_left - 1).z - self.point(i_left).z) < 1e-20:
|
||||||
pim1 = self.point(i_left - 1)
|
pt_left = self.point(i_left)
|
||||||
|
|
||||||
if abs(pim1.z - pi.z) < 1e-20:
|
|
||||||
pt_left = pi
|
|
||||||
else:
|
else:
|
||||||
fact = (z - pi.z) / (pim1.z - pi.z)
|
fact = (z - self.point(i_left).z) / \
|
||||||
x = pi.x + fact * (pim1.x - pi.x)
|
(self.point(i_left - 1).z - self.point(i_left).z)
|
||||||
y = pi.y + fact * (pim1.y - pi.y)
|
x = self.point(i_left).x + fact * \
|
||||||
|
(self.point(i_left - 1).x - self.point(i_left).x)
|
||||||
|
y = self.point(i_left).y + fact * \
|
||||||
|
(self.point(i_left - 1).y - self.point(i_left).y)
|
||||||
pt_left = PointXYZ(x=x, y=y, z=z, name="wl_left")
|
pt_left = PointXYZ(x=x, y=y, z=z, name="wl_left")
|
||||||
else:
|
else:
|
||||||
pt_left = self.point(0)
|
pt_left = self.point(0)
|
||||||
|
|
||||||
# Interpolate points at river right side
|
# Interpolate points at river right side
|
||||||
if (i_right < self.number_points - 1):
|
if (i_right < self.number_points - 1):
|
||||||
pi = self.point(i_right)
|
if abs(self.point(i_right + 1).z - self.point(i_right).z) < 1e-20:
|
||||||
pip1 = self.point(i_right + 1)
|
pt_right = self.point(i_right)
|
||||||
|
|
||||||
if abs(pip1.z - pi.z) < 1e-20:
|
|
||||||
pt_right = pi
|
|
||||||
else:
|
else:
|
||||||
fact = (z - pi.z) / (pip1.z - pi.z)
|
fact = (z - self.point(i_right).z) / \
|
||||||
x = pi.x + fact * (pip1.x - pi.x)
|
(self.point(i_right + 1).z - self.point(i_right).z)
|
||||||
y = pi.y + fact * (pip1.y - pi.y)
|
x = self.point(i_right).x + fact * \
|
||||||
|
(self.point(i_right + 1).x - self.point(i_right).x)
|
||||||
|
y = self.point(i_right).y + fact * \
|
||||||
|
(self.point(i_right + 1).y - self.point(i_right).y)
|
||||||
pt_right = PointXYZ(x=x, y=y, z=z, name="wl_right")
|
pt_right = PointXYZ(x=x, y=y, z=z, name="wl_right")
|
||||||
else:
|
else:
|
||||||
pt_right = self.point(self.number_points - 1)
|
pt_right = self.point(self.number_points - 1)
|
||||||
|
|
||||||
v = (pt_left, pt_right)
|
|
||||||
|
|
||||||
# Put results in cache
|
# Put results in cache
|
||||||
self._get_water_limits_cache[z] = v
|
self._get_water_limits_cache[z] = (pt_left, pt_right)
|
||||||
|
|
||||||
# Create a generator to improve results data reading speed
|
# Create a generator to improve results data reading speed
|
||||||
yield v
|
yield pt_left, pt_right
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
def compute_tabulation(self):
|
def compute_tabulation(self):
|
||||||
|
|
|
||||||
|
|
@ -157,185 +157,8 @@ class AdditionalData(SQLSubModel):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class TableData(SQLSubModel):
|
|
||||||
_sub_classes = []
|
|
||||||
|
|
||||||
def __init__(self, id=-1, study=None,
|
|
||||||
legend="", data=None,
|
|
||||||
owner_scenario=-1):
|
|
||||||
super(TableData, self).__init__(
|
|
||||||
id=id, status=study.status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._study = study
|
|
||||||
self._legend = legend
|
|
||||||
self._data = data
|
|
||||||
|
|
||||||
@property
|
|
||||||
def data(self):
|
|
||||||
return self._data
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_create(cls, execute, ext=""):
|
|
||||||
execute(f"""
|
|
||||||
CREATE TABLE results_table_data{ext} (
|
|
||||||
{cls.create_db_add_pamhyr_id()},
|
|
||||||
result INTEGER NOT NULL,
|
|
||||||
legend TEXT NOT NULL,
|
|
||||||
unit TEXT NOT NULL,
|
|
||||||
data_shape_x INTEGER NOT NULL,
|
|
||||||
data_shape_y INTEGER NOT NULL,
|
|
||||||
data_shape_z INTEGER DEFAULT -1,
|
|
||||||
data BLOB NOT NULL,
|
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(result) REFERENCES results(pamhyr_id),
|
|
||||||
PRIMARY KEY(pamhyr_id, result, legend, scenario)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
if ext != "":
|
|
||||||
return True
|
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update(cls, execute, version, data=None):
|
|
||||||
major, minor, release = version.strip().split(".")
|
|
||||||
create = False
|
|
||||||
|
|
||||||
if major == "0" and int(minor) == 2:
|
|
||||||
if int(release) < 7 and not create:
|
|
||||||
cls._db_create(execute)
|
|
||||||
create = True
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load(cls, execute, data=None):
|
|
||||||
res = {}
|
|
||||||
|
|
||||||
study = data['study']
|
|
||||||
status = data['status']
|
|
||||||
scenario = data["scenario"]
|
|
||||||
result = data["result"]
|
|
||||||
rid = data["result_pid"]
|
|
||||||
|
|
||||||
table = execute(
|
|
||||||
"SELECT pamhyr_id, " +
|
|
||||||
"legend, unit, " +
|
|
||||||
"data_shape_x, data_shape_y, data_shape_z, " +
|
|
||||||
"data, " +
|
|
||||||
"scenario " +
|
|
||||||
"FROM results_table_data " +
|
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND result = {rid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for v in table:
|
|
||||||
it = iter(v)
|
|
||||||
|
|
||||||
pid = next(it)
|
|
||||||
legend = next(it)
|
|
||||||
unit = next(it)
|
|
||||||
data_shape_x = next(it)
|
|
||||||
data_shape_y = next(it)
|
|
||||||
data_shape_z = next(it)
|
|
||||||
data = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
dtype = cls._unit_to_dtype(unit)
|
|
||||||
|
|
||||||
np_data = np.frombuffer(data, dtype=dtype)
|
|
||||||
if data_shape_y == -1:
|
|
||||||
np_data = np.reshape(np_data, data_shape_x)
|
|
||||||
elif data_shape_z == -1:
|
|
||||||
np_data = np.reshape(np_data, (data_shape_x,
|
|
||||||
data_shape_y))
|
|
||||||
else:
|
|
||||||
np_data = np.reshape(np_data, (data_shape_x,
|
|
||||||
data_shape_y,
|
|
||||||
data_shape_z))
|
|
||||||
|
|
||||||
new_table = cls(study=study,
|
|
||||||
legend=legend, data=np_data,
|
|
||||||
owner_scenario=owner_scenario)
|
|
||||||
|
|
||||||
res[legend] = new_table
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
if self._status.scenario.id != self._owner_scenario:
|
|
||||||
return
|
|
||||||
|
|
||||||
shape_x = -1
|
|
||||||
shape_y = -1
|
|
||||||
shape_z = -1
|
|
||||||
shape = self._data.shape
|
|
||||||
|
|
||||||
if len(shape) == 2:
|
|
||||||
shape_x, shape_y = shape
|
|
||||||
elif len(shape == 1):
|
|
||||||
shape_x, = shape
|
|
||||||
elif len(shape == 3):
|
|
||||||
shape_x, shape_y, shape_z = shape
|
|
||||||
else:
|
|
||||||
logger.error(
|
|
||||||
f"Failed to parse numpy shape: {shape}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT INTO " +
|
|
||||||
"results_table_data (pamhyr_id, result, " +
|
|
||||||
"legend, unit, " +
|
|
||||||
"data_shape_x, data_shape_y, data_shape_z, " +
|
|
||||||
"data, " +
|
|
||||||
"scenario) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
||||||
self._pamhyr_id, data["result"],
|
|
||||||
self._legend, self._dtype_to_unit(self._data.dtype),
|
|
||||||
shape_x, shape_y, shape_z,
|
|
||||||
self._data,
|
|
||||||
self._owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _unit_to_dtype(cls, unit):
|
|
||||||
t = {
|
|
||||||
"i32": np.int32, "i64": np.int64,
|
|
||||||
"u32": np.uint32, "u64": np.uint64,
|
|
||||||
"f32": np.float32, "f64": np.float64,
|
|
||||||
}
|
|
||||||
|
|
||||||
return t[unit]
|
|
||||||
|
|
||||||
def _dtype_to_unit(self, dtype):
|
|
||||||
t = {
|
|
||||||
np.dtype(np.int32): "i32", np.dtype(np.int64): "i64",
|
|
||||||
np.dtype(np.uint32): "u32", np.dtype(np.uint64): "u64",
|
|
||||||
np.dtype(np.float32): "f32", np.dtype(np.float64): "f64",
|
|
||||||
}
|
|
||||||
|
|
||||||
return t[dtype]
|
|
||||||
|
|
||||||
def table(self):
|
|
||||||
return self._data
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return len(self._data)
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
if isinstance(key, tuple):
|
|
||||||
return self._data[key[0], key[1]]
|
|
||||||
|
|
||||||
self._date[key]
|
|
||||||
|
|
||||||
|
|
||||||
class Results(SQLSubModel):
|
class Results(SQLSubModel):
|
||||||
_sub_classes = [River, TableData, AdditionalData]
|
_sub_classes = [River, AdditionalData]
|
||||||
|
|
||||||
def __init__(self, id=-1, study=None, solver=None,
|
def __init__(self, id=-1, study=None, solver=None,
|
||||||
repertory="", name="0"):
|
repertory="", name="0"):
|
||||||
|
|
@ -356,11 +179,8 @@ class Results(SQLSubModel):
|
||||||
"creation_date": datetime.now(),
|
"creation_date": datetime.now(),
|
||||||
"study_revision": study.status.version,
|
"study_revision": study.status.version,
|
||||||
"additional_data": [],
|
"additional_data": [],
|
||||||
"table": {},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self._ts_index = {}
|
|
||||||
|
|
||||||
if solver is not None:
|
if solver is not None:
|
||||||
self.set("solver_type", solver._type)
|
self.set("solver_type", solver._type)
|
||||||
|
|
||||||
|
|
@ -401,10 +221,6 @@ class Results(SQLSubModel):
|
||||||
def get_timestamp_id(self, ts):
|
def get_timestamp_id(self, ts):
|
||||||
return self._ts_index[ts]
|
return self._ts_index[ts]
|
||||||
|
|
||||||
def compute_timestamp_index(self):
|
|
||||||
for i, ts in enumerate(self._meta_data["timestamps"]):
|
|
||||||
self._ts_index[ts] = i
|
|
||||||
|
|
||||||
def reload(self):
|
def reload(self):
|
||||||
return self._solver.results(
|
return self._solver.results(
|
||||||
self._study,
|
self._study,
|
||||||
|
|
@ -502,19 +318,11 @@ class Results(SQLSubModel):
|
||||||
new_results.set("study_revision", revision)
|
new_results.set("study_revision", revision)
|
||||||
new_results.set("creation_date", creation_date)
|
new_results.set("creation_date", creation_date)
|
||||||
|
|
||||||
data["result_pid"] = pid
|
|
||||||
data["result"] = new_results
|
|
||||||
|
|
||||||
table_data = TableData._db_load(execute, data)
|
|
||||||
new_results.set("table", table_data)
|
|
||||||
|
|
||||||
sf = ">" + ''.join(itertools.repeat("d", nb_timestamps))
|
sf = ">" + ''.join(itertools.repeat("d", nb_timestamps))
|
||||||
ts = struct.unpack(sf, timestamps_bytes)
|
ts = struct.unpack(sf, timestamps_bytes)
|
||||||
new_results.set("timestamps", ts)
|
new_results.set("timestamps", ts)
|
||||||
|
|
||||||
data["timestamps"] = sorted(ts)
|
data["timestamps"] = sorted(ts)
|
||||||
new_results.compute_timestamp_index()
|
|
||||||
|
|
||||||
data["parent"] = new_results
|
data["parent"] = new_results
|
||||||
new_results._river = River._db_load(execute, data)
|
new_results._river = River._db_load(execute, data)
|
||||||
|
|
||||||
|
|
@ -523,20 +331,6 @@ class Results(SQLSubModel):
|
||||||
AdditionalData._db_load(execute, data)
|
AdditionalData._db_load(execute, data)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update global index
|
|
||||||
i = 0
|
|
||||||
for reach in new_results._river.reachs:
|
|
||||||
nb = len(reach.profiles)
|
|
||||||
reach.set_global_index(range(i, i + nb + 1))
|
|
||||||
i += nb
|
|
||||||
|
|
||||||
if "Z" in table_data:
|
|
||||||
for ts in data["timestamps"]:
|
|
||||||
new_results._river.update_water_limits(
|
|
||||||
ts,
|
|
||||||
table_data["Z"][new_results.get_timestamp_id(ts), :]
|
|
||||||
)
|
|
||||||
|
|
||||||
yield (solver_type, new_results)
|
yield (solver_type, new_results)
|
||||||
|
|
||||||
def _db_save_clear(self, execute, solver_type, data=None):
|
def _db_save_clear(self, execute, solver_type, data=None):
|
||||||
|
|
@ -559,11 +353,6 @@ class Results(SQLSubModel):
|
||||||
f"WHERE scenario = {self._owner_scenario} " +
|
f"WHERE scenario = {self._owner_scenario} " +
|
||||||
f"AND result = {pid}"
|
f"AND result = {pid}"
|
||||||
)
|
)
|
||||||
execute(
|
|
||||||
"DELETE FROM results_table_data " +
|
|
||||||
f"WHERE scenario = {self._owner_scenario} " +
|
|
||||||
f"AND result = {pid}"
|
|
||||||
)
|
|
||||||
execute(
|
execute(
|
||||||
"DELETE FROM results_add_data " +
|
"DELETE FROM results_add_data " +
|
||||||
f"WHERE scenario = {self._owner_scenario} " +
|
f"WHERE scenario = {self._owner_scenario} " +
|
||||||
|
|
@ -607,19 +396,7 @@ class Results(SQLSubModel):
|
||||||
data["result"] = self._pamhyr_id
|
data["result"] = self._pamhyr_id
|
||||||
self._river._db_save(execute, data)
|
self._river._db_save(execute, data)
|
||||||
|
|
||||||
tables = self.get("table")
|
|
||||||
for legend in tables:
|
|
||||||
tables[legend]._db_save(execute, data)
|
|
||||||
|
|
||||||
for add_data in self.get("additional_data"):
|
for add_data in self.get("additional_data"):
|
||||||
add_data._db_save(execute, data)
|
add_data._db_save(execute, data)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def new_table_data(self, legend, data):
|
|
||||||
return TableData(
|
|
||||||
study=self._study,
|
|
||||||
legend=legend,
|
|
||||||
data=data,
|
|
||||||
owner_scenario=self._owner_scenario
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,7 @@ class Profile(SQLSubModel):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = {}
|
||||||
status = data['status']
|
status = data['status']
|
||||||
|
|
||||||
parent = data['parent']
|
parent = data['parent']
|
||||||
|
|
@ -147,8 +148,6 @@ class Profile(SQLSubModel):
|
||||||
loaded = data['loaded_pid']
|
loaded = data['loaded_pid']
|
||||||
timestamps = data['timestamps']
|
timestamps = data['timestamps']
|
||||||
|
|
||||||
new = cls(profile, study, parent)
|
|
||||||
|
|
||||||
values = execute(
|
values = execute(
|
||||||
"SELECT pamhyr_id, result, key, " +
|
"SELECT pamhyr_id, result, key, " +
|
||||||
"len_data, data, scenario " +
|
"len_data, data, scenario " +
|
||||||
|
|
@ -168,6 +167,12 @@ class Profile(SQLSubModel):
|
||||||
data = next(it)
|
data = next(it)
|
||||||
owner_scenario = next(it)
|
owner_scenario = next(it)
|
||||||
|
|
||||||
|
if profile not in new:
|
||||||
|
new_data = cls(profile, study, parent)
|
||||||
|
new[profile] = new_data
|
||||||
|
else:
|
||||||
|
new_data = new[profile]
|
||||||
|
|
||||||
if key in ["Z", "Q", "V"]:
|
if key in ["Z", "Q", "V"]:
|
||||||
sf = ">" + ''.join(itertools.repeat("f", len_data))
|
sf = ">" + ''.join(itertools.repeat("f", len_data))
|
||||||
len_values = len(values)
|
len_values = len(values)
|
||||||
|
|
@ -180,11 +185,11 @@ class Profile(SQLSubModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
for timestamp, value in zip(timestamps, values):
|
for timestamp, value in zip(timestamps, values):
|
||||||
new.set(timestamp, key, value)
|
new_data.set(timestamp, key, value)
|
||||||
if key == "Z":
|
if key == "Z":
|
||||||
new.update_water_limits(timestamp, value)
|
new_data.update_water_limits(timestamp, value)
|
||||||
|
|
||||||
return new
|
return list(new.values())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load_data_sl_format(cls, values, len_data, timestamps):
|
def _db_load_data_sl_format(cls, values, len_data, timestamps):
|
||||||
|
|
@ -356,10 +361,6 @@ class Reach(SQLSubModel):
|
||||||
for i, p in enumerate(self._profiles):
|
for i, p in enumerate(self._profiles):
|
||||||
self._buffers[key][:, i] = p.get_key(key)
|
self._buffers[key][:, i] = p.get_key(key)
|
||||||
|
|
||||||
def update_water_limits(self, timestamp, z_data):
|
|
||||||
for profile, z in zip(self._profiles, z_data):
|
|
||||||
profile.update_water_limits(timestamp, z)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute, ext=""):
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
@ -379,7 +380,7 @@ class Reach(SQLSubModel):
|
||||||
|
|
||||||
for i, profile in enumerate(reach.profiles):
|
for i, profile in enumerate(reach.profiles):
|
||||||
data["profile"] = profile
|
data["profile"] = profile
|
||||||
new_reach._profiles += [Profile._db_load(execute, data)]
|
new_reach._profiles += Profile._db_load(execute, data)
|
||||||
|
|
||||||
return new_reach
|
return new_reach
|
||||||
|
|
||||||
|
|
@ -434,15 +435,6 @@ class River(SQLSubModel):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def update_water_limits(self, timestamp, data):
|
|
||||||
for reach in self._reachs:
|
|
||||||
ind = reach.global_index
|
|
||||||
|
|
||||||
reach.update_water_limits(
|
|
||||||
timestamp,
|
|
||||||
data[ind[0]:ind[1]]
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute, ext=""):
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class Study(SQLModel):
|
class Study(SQLModel):
|
||||||
_version = "0.2.7"
|
_version = "0.2.6"
|
||||||
|
|
||||||
_sub_classes = [
|
_sub_classes = [
|
||||||
Scenario,
|
Scenario,
|
||||||
|
|
|
||||||
|
|
@ -1206,12 +1206,6 @@ class Mage8(Mage):
|
||||||
j += 1
|
j += 1
|
||||||
|
|
||||||
table["V"] = velocity_arr
|
table["V"] = velocity_arr
|
||||||
|
|
||||||
for legend in table:
|
|
||||||
table[legend] = results.new_table_data(
|
|
||||||
legend, table[legend]
|
|
||||||
)
|
|
||||||
|
|
||||||
results.set("table", table)
|
results.set("table", table)
|
||||||
|
|
||||||
logger.info(f"read_bin: ... end with {len(ts)} timestamp read")
|
logger.info(f"read_bin: ... end with {len(ts)} timestamp read")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue