fix(timeseries): align timezone and scada updates
This commit is contained in:
@@ -10,6 +10,7 @@ from app.core.config import get_timescaledb_pgconn_string
|
||||
from app.infra.db.timescaledb.repositories.scheme import SchemeRepository
|
||||
from app.infra.db.timescaledb.repositories.realtime import RealtimeRepository
|
||||
from app.infra.db.timescaledb.repositories.scada import ScadaRepository
|
||||
from app.services.time_api import parse_utc_time
|
||||
|
||||
|
||||
class InternalStorage:
|
||||
@@ -89,10 +90,9 @@ class InternalQueries:
|
||||
) -> dict:
|
||||
"""查询指定时间点的 SCADA 数据"""
|
||||
|
||||
# 解析时间,假设是北京时间
|
||||
beijing_time = datetime.fromisoformat(query_time)
|
||||
start_time = beijing_time - timedelta(seconds=1)
|
||||
end_time = beijing_time + timedelta(seconds=1)
|
||||
target_time = parse_utc_time(query_time, field_name="query_time")
|
||||
start_time = target_time - timedelta(seconds=1)
|
||||
end_time = target_time + timedelta(seconds=1)
|
||||
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
@@ -132,14 +132,8 @@ class InternalQueries:
|
||||
max_retries: int = 3,
|
||||
) -> dict[str, list[dict]]:
|
||||
"""查询指定时间窗的 SCADA 数据,返回 {device_id: [{time, value}, ...]}。"""
|
||||
start_dt = (
|
||||
datetime.fromisoformat(start_time)
|
||||
if isinstance(start_time, str)
|
||||
else start_time
|
||||
)
|
||||
end_dt = (
|
||||
datetime.fromisoformat(end_time) if isinstance(end_time, str) else end_time
|
||||
)
|
||||
start_dt = parse_utc_time(start_time, field_name="start_time")
|
||||
end_dt = parse_utc_time(end_time, field_name="end_time")
|
||||
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
@@ -238,14 +232,8 @@ class InternalQueries:
|
||||
if not element_ids:
|
||||
return {}
|
||||
|
||||
start_dt = (
|
||||
datetime.fromisoformat(start_time)
|
||||
if isinstance(start_time, str)
|
||||
else start_time
|
||||
)
|
||||
end_dt = (
|
||||
datetime.fromisoformat(end_time) if isinstance(end_time, str) else end_time
|
||||
)
|
||||
start_dt = parse_utc_time(start_time, field_name="start_time")
|
||||
end_dt = parse_utc_time(end_time, field_name="end_time")
|
||||
table_name, valid_fields = InternalQueries._resolve_simulation_table(element_type)
|
||||
if field not in valid_fields:
|
||||
raise ValueError(f"Invalid field for {element_type}: {field}")
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
from typing import List, Any, Dict
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from datetime import datetime, timedelta
|
||||
from collections import defaultdict
|
||||
from psycopg import AsyncConnection, Connection, sql
|
||||
|
||||
# 定义UTC+8时区
|
||||
UTC_8 = timezone(timedelta(hours=8))
|
||||
from app.services.time_api import parse_utc_time
|
||||
|
||||
|
||||
class RealtimeRepository:
|
||||
@@ -102,10 +100,12 @@ class RealtimeRepository:
|
||||
async def get_links_by_time_range(
|
||||
conn: AsyncConnection, start_time: datetime, end_time: datetime
|
||||
) -> List[dict]:
|
||||
normalized_start_time = parse_utc_time(start_time, field_name="start_time")
|
||||
normalized_end_time = parse_utc_time(end_time, field_name="end_time")
|
||||
async with conn.cursor() as cur:
|
||||
await cur.execute(
|
||||
"SELECT * FROM realtime.link_simulation WHERE time >= %s AND time <= %s",
|
||||
(start_time, end_time),
|
||||
(normalized_start_time, normalized_end_time),
|
||||
)
|
||||
return await cur.fetchall()
|
||||
|
||||
@@ -298,10 +298,12 @@ class RealtimeRepository:
|
||||
async def get_nodes_by_time_range(
|
||||
conn: AsyncConnection, start_time: datetime, end_time: datetime
|
||||
) -> List[dict]:
|
||||
normalized_start_time = parse_utc_time(start_time, field_name="start_time")
|
||||
normalized_end_time = parse_utc_time(end_time, field_name="end_time")
|
||||
async with conn.cursor() as cur:
|
||||
await cur.execute(
|
||||
"SELECT * FROM realtime.node_simulation WHERE time >= %s AND time <= %s",
|
||||
(start_time, end_time),
|
||||
(normalized_start_time, normalized_end_time),
|
||||
)
|
||||
return await cur.fetchall()
|
||||
|
||||
@@ -397,24 +399,9 @@ class RealtimeRepository:
|
||||
link_result_list: List of link simulation results
|
||||
result_start_time: Start time for the results (ISO format string)
|
||||
"""
|
||||
# Convert result_start_time string to datetime if needed
|
||||
if isinstance(result_start_time, str):
|
||||
# 如果是ISO格式字符串,解析并转换为UTC+8
|
||||
if result_start_time.endswith("Z"):
|
||||
# UTC时间,转换为UTC+8
|
||||
utc_time = datetime.fromisoformat(
|
||||
result_start_time.replace("Z", "+00:00")
|
||||
)
|
||||
simulation_time = utc_time.astimezone(UTC_8)
|
||||
else:
|
||||
# 假设已经是UTC+8时间
|
||||
simulation_time = datetime.fromisoformat(result_start_time)
|
||||
if simulation_time.tzinfo is None:
|
||||
simulation_time = simulation_time.replace(tzinfo=UTC_8)
|
||||
else:
|
||||
simulation_time = result_start_time
|
||||
if simulation_time.tzinfo is None:
|
||||
simulation_time = simulation_time.replace(tzinfo=UTC_8)
|
||||
simulation_time = parse_utc_time(
|
||||
result_start_time, field_name="result_start_time"
|
||||
)
|
||||
|
||||
# Prepare node data for batch insert
|
||||
node_data = []
|
||||
@@ -475,24 +462,9 @@ class RealtimeRepository:
|
||||
link_result_list: List of link simulation results
|
||||
result_start_time: Start time for the results (ISO format string)
|
||||
"""
|
||||
# Convert result_start_time string to datetime if needed
|
||||
if isinstance(result_start_time, str):
|
||||
# 如果是ISO格式字符串,解析并转换为UTC+8
|
||||
if result_start_time.endswith("Z"):
|
||||
# UTC时间,转换为UTC+8
|
||||
utc_time = datetime.fromisoformat(
|
||||
result_start_time.replace("Z", "+00:00")
|
||||
)
|
||||
simulation_time = utc_time.astimezone(UTC_8)
|
||||
else:
|
||||
# 假设已经是UTC+8时间
|
||||
simulation_time = datetime.fromisoformat(result_start_time)
|
||||
if simulation_time.tzinfo is None:
|
||||
simulation_time = simulation_time.replace(tzinfo=UTC_8)
|
||||
else:
|
||||
simulation_time = result_start_time
|
||||
if simulation_time.tzinfo is None:
|
||||
simulation_time = simulation_time.replace(tzinfo=UTC_8)
|
||||
simulation_time = parse_utc_time(
|
||||
result_start_time, field_name="result_start_time"
|
||||
)
|
||||
|
||||
# Prepare node data for batch insert
|
||||
node_data = []
|
||||
@@ -556,21 +528,7 @@ class RealtimeRepository:
|
||||
Returns:
|
||||
List of records matching the criteria
|
||||
"""
|
||||
# Convert query_time string to datetime
|
||||
if isinstance(query_time, str):
|
||||
if query_time.endswith("Z"):
|
||||
# UTC时间,转换为UTC+8
|
||||
utc_time = datetime.fromisoformat(query_time.replace("Z", "+00:00"))
|
||||
target_time = utc_time.astimezone(UTC_8)
|
||||
else:
|
||||
# 假设已经是UTC+8时间
|
||||
target_time = datetime.fromisoformat(query_time)
|
||||
if target_time.tzinfo is None:
|
||||
target_time = target_time.replace(tzinfo=UTC_8)
|
||||
else:
|
||||
target_time = query_time
|
||||
if target_time.tzinfo is None:
|
||||
target_time = target_time.replace(tzinfo=UTC_8)
|
||||
target_time = parse_utc_time(query_time, field_name="query_time")
|
||||
|
||||
# Create time range: query_time ± 1 second
|
||||
start_time = target_time - timedelta(seconds=1)
|
||||
@@ -614,21 +572,7 @@ class RealtimeRepository:
|
||||
Returns:
|
||||
List of records matching the criteria
|
||||
"""
|
||||
# Convert query_time string to datetime
|
||||
if isinstance(query_time, str):
|
||||
if query_time.endswith("Z"):
|
||||
# UTC时间,转换为UTC+8
|
||||
utc_time = datetime.fromisoformat(query_time.replace("Z", "+00:00"))
|
||||
target_time = utc_time.astimezone(UTC_8)
|
||||
else:
|
||||
# 假设已经是UTC+8时间
|
||||
target_time = datetime.fromisoformat(query_time)
|
||||
if target_time.tzinfo is None:
|
||||
target_time = target_time.replace(tzinfo=UTC_8)
|
||||
else:
|
||||
target_time = query_time
|
||||
if target_time.tzinfo is None:
|
||||
target_time = target_time.replace(tzinfo=UTC_8)
|
||||
target_time = parse_utc_time(query_time, field_name="query_time")
|
||||
|
||||
# Create time range: query_time ± 1 second
|
||||
start_time = target_time - timedelta(seconds=1)
|
||||
|
||||
@@ -89,12 +89,17 @@ class ScadaRepository:
|
||||
if field not in valid_fields:
|
||||
raise ValueError(f"Invalid field: {field}")
|
||||
|
||||
query = sql.SQL(
|
||||
update_query = sql.SQL(
|
||||
"UPDATE scada.scada_data SET {} = %s WHERE time = %s AND device_id = %s"
|
||||
).format(sql.Identifier(field))
|
||||
insert_query = sql.SQL(
|
||||
"INSERT INTO scada.scada_data (time, device_id, {}) VALUES (%s, %s, %s)"
|
||||
).format(sql.Identifier(field))
|
||||
|
||||
async with conn.cursor() as cur:
|
||||
await cur.execute(query, (value, time, device_id))
|
||||
await cur.execute(update_query, (value, time, device_id))
|
||||
if cur.rowcount == 0:
|
||||
await cur.execute(insert_query, (time, device_id, value))
|
||||
|
||||
@staticmethod
|
||||
async def delete_scada_by_id_time_range(
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
from typing import List, Any, Dict
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from datetime import datetime, timedelta
|
||||
from collections import defaultdict
|
||||
from psycopg import AsyncConnection, Connection, sql
|
||||
import app.services.globals as globals
|
||||
|
||||
# 定义UTC+8时区
|
||||
UTC_8 = timezone(timedelta(hours=8))
|
||||
from app.services.time_api import parse_utc_time
|
||||
|
||||
|
||||
class SchemeRepository:
|
||||
@@ -466,24 +464,9 @@ class SchemeRepository:
|
||||
link_result_list: List of link simulation results
|
||||
result_start_time: Start time for the results (ISO format string)
|
||||
"""
|
||||
# Convert result_start_time string to datetime if needed
|
||||
if isinstance(result_start_time, str):
|
||||
# 如果是ISO格式字符串,解析并转换为UTC+8
|
||||
if result_start_time.endswith("Z"):
|
||||
# UTC时间,转换为UTC+8
|
||||
utc_time = datetime.fromisoformat(
|
||||
result_start_time.replace("Z", "+00:00")
|
||||
)
|
||||
simulation_time = utc_time.astimezone(UTC_8)
|
||||
else:
|
||||
# 假设已经是UTC+8时间
|
||||
simulation_time = datetime.fromisoformat(result_start_time)
|
||||
if simulation_time.tzinfo is None:
|
||||
simulation_time = simulation_time.replace(tzinfo=UTC_8)
|
||||
else:
|
||||
simulation_time = result_start_time
|
||||
if simulation_time.tzinfo is None:
|
||||
simulation_time = simulation_time.replace(tzinfo=UTC_8)
|
||||
simulation_time = parse_utc_time(
|
||||
result_start_time, field_name="result_start_time"
|
||||
)
|
||||
|
||||
timestep_parts = globals.hydraulic_timestep.split(":")
|
||||
timestep = timedelta(
|
||||
@@ -564,24 +547,9 @@ class SchemeRepository:
|
||||
link_result_list: List of link simulation results
|
||||
result_start_time: Start time for the results (ISO format string)
|
||||
"""
|
||||
# Convert result_start_time string to datetime if needed
|
||||
if isinstance(result_start_time, str):
|
||||
# 如果是ISO格式字符串,解析并转换为UTC+8
|
||||
if result_start_time.endswith("Z"):
|
||||
# UTC时间,转换为UTC+8
|
||||
utc_time = datetime.fromisoformat(
|
||||
result_start_time.replace("Z", "+00:00")
|
||||
)
|
||||
simulation_time = utc_time.astimezone(UTC_8)
|
||||
else:
|
||||
# 假设已经是UTC+8时间
|
||||
simulation_time = datetime.fromisoformat(result_start_time)
|
||||
if simulation_time.tzinfo is None:
|
||||
simulation_time = simulation_time.replace(tzinfo=UTC_8)
|
||||
else:
|
||||
simulation_time = result_start_time
|
||||
if simulation_time.tzinfo is None:
|
||||
simulation_time = simulation_time.replace(tzinfo=UTC_8)
|
||||
simulation_time = parse_utc_time(
|
||||
result_start_time, field_name="result_start_time"
|
||||
)
|
||||
|
||||
timestep_parts = globals.hydraulic_timestep.split(":")
|
||||
timestep = timedelta(
|
||||
@@ -664,21 +632,7 @@ class SchemeRepository:
|
||||
Returns:
|
||||
List of records matching the criteria
|
||||
"""
|
||||
# Convert query_time string to datetime
|
||||
if isinstance(query_time, str):
|
||||
if query_time.endswith("Z"):
|
||||
# UTC时间,转换为UTC+8
|
||||
utc_time = datetime.fromisoformat(query_time.replace("Z", "+00:00"))
|
||||
target_time = utc_time.astimezone(UTC_8)
|
||||
else:
|
||||
# 假设已经是UTC+8时间
|
||||
target_time = datetime.fromisoformat(query_time)
|
||||
if target_time.tzinfo is None:
|
||||
target_time = target_time.replace(tzinfo=UTC_8)
|
||||
else:
|
||||
target_time = query_time
|
||||
if target_time.tzinfo is None:
|
||||
target_time = target_time.replace(tzinfo=UTC_8)
|
||||
target_time = parse_utc_time(query_time, field_name="query_time")
|
||||
|
||||
# Create time range: query_time ± 1 second
|
||||
start_time = target_time - timedelta(seconds=1)
|
||||
@@ -727,21 +681,7 @@ class SchemeRepository:
|
||||
Returns:
|
||||
List of records matching the criteria
|
||||
"""
|
||||
# Convert query_time string to datetime
|
||||
if isinstance(query_time, str):
|
||||
if query_time.endswith("Z"):
|
||||
# UTC时间,转换为UTC+8
|
||||
utc_time = datetime.fromisoformat(query_time.replace("Z", "+00:00"))
|
||||
target_time = utc_time.astimezone(UTC_8)
|
||||
else:
|
||||
# 假设已经是UTC+8时间
|
||||
target_time = datetime.fromisoformat(query_time)
|
||||
if target_time.tzinfo is None:
|
||||
target_time = target_time.replace(tzinfo=UTC_8)
|
||||
else:
|
||||
target_time = query_time
|
||||
if target_time.tzinfo is None:
|
||||
target_time = target_time.replace(tzinfo=UTC_8)
|
||||
target_time = parse_utc_time(query_time, field_name="query_time")
|
||||
|
||||
# Create time range: query_time ± 1 second
|
||||
start_time = target_time - timedelta(seconds=1)
|
||||
|
||||
Reference in New Issue
Block a user