调整后端api,完善历史数据面板的交互

This commit is contained in:
JIANG
2025-12-17 11:13:09 +08:00
parent 3a97e01dda
commit 6d672800f9
2 changed files with 76 additions and 48 deletions

View File

@@ -45,8 +45,8 @@ export interface TimeSeriesPoint {
}
export interface SCADADataPanelProps {
/** 选中的设备 ID 列表 */
deviceIds: string[];
/** 选中的要素信息列表,格式为 [[id, type], [id, type]] */
featureInfos: [string, string][];
/** 数据类型: realtime-查询模拟值和监测值, none-仅查询监测值, scheme-查询策略模拟值和监测值 */
type?: "realtime" | "scheme" | "none";
/** 策略类型 */
@@ -55,7 +55,7 @@ export interface SCADADataPanelProps {
scheme_name?: string;
/** 自定义数据获取器,默认使用后端 API */
fetchTimeSeriesData?: (
deviceIds: string[],
featureInfos: [string, string][],
range: { from: Date; to: Date },
type?: "realtime" | "scheme" | "none",
scheme_type?: string,
@@ -75,28 +75,36 @@ type LoadingState = "idle" | "loading" | "success" | "error";
* 从后端 API 获取 SCADA 数据
*/
const fetchFromBackend = async (
deviceIds: string[],
featureInfos: [string, string][],
range: { from: Date; to: Date },
type: "realtime" | "scheme" | "none" = "realtime",
scheme_type?: string,
scheme_name?: string
): Promise<TimeSeriesPoint[]> => {
if (deviceIds.length === 0) {
if (featureInfos.length === 0) {
return [];
}
const device_ids = deviceIds.join(",");
// 提取设备 ID 列表
const featureIds = featureInfos.map(([id]) => id);
const feature_ids = featureIds.join(",");
const start_time = dayjs(range.from).toISOString();
const end_time = dayjs(range.to).toISOString();
// 监测值数据接口
const rawDataUrl = `${config.BACKEND_URL}/timescaledb/scada/by-ids-field-time-range?device_ids=${device_ids}&field=raw_value&start_time=${start_time}&end_time=${end_time}`;
// 清洗数据接口
const cleanedDataUrl = `${config.BACKEND_URL}/timescaledb/scada/by-ids-field-time-range?device_ids=${device_ids}&field=cleaned_value&start_time=${start_time}&end_time=${end_time}`;
// 将 featureInfos 转换为后端期望的格式: id1:type1,id2:type2
const feature_infos = featureInfos
.map(([id, type]) => `${id}:${type}`)
.join(",");
// 监测值数据接口use_cleaned=false
const rawDataUrl = `${config.BACKEND_URL}/timescaledb/composite/element-scada?element_id=${feature_ids}&start_time=${start_time}&end_time=${end_time}&use_cleaned=false`;
// 清洗数据接口use_cleaned=true
const cleanedDataUrl = `${config.BACKEND_URL}/timescaledb/composite/element-scada?element_id=${feature_ids}&start_time=${start_time}&end_time=${end_time}&use_cleaned=true`;
// 模拟数据接口
const simulationDataUrl = `${config.BACKEND_URL}/timescaledb/composite/scada-simulation?device_ids=${device_ids}&start_time=${start_time}&end_time=${end_time}`;
const simulationDataUrl = `${config.BACKEND_URL}/timescaledb/composite/element-simulation?feature_infos=${feature_infos}&start_time=${start_time}&end_time=${end_time}`;
// 策略模拟数据接口
const schemeSimulationDataUrl = `${config.BACKEND_URL}/timescaledb/composite/scada-simulation?device_ids=${device_ids}&start_time=${start_time}&end_time=${end_time}&scheme_type=${scheme_type}&scheme_name=${scheme_name}`;
const schemeSimulationDataUrl = `${config.BACKEND_URL}/timescaledb/composite/element-simulation?feature_infos=${feature_infos}&start_time=${start_time}&end_time=${end_time}&scheme_type=${scheme_type}&scheme_name=${scheme_name}`;
try {
if (type === "none") {
@@ -110,13 +118,13 @@ const fetchFromBackend = async (
.catch(() => null),
]);
const cleanedData = transformBackendData(cleanedRes, deviceIds);
const rawData = transformBackendData(rawRes, deviceIds);
const cleanedData = transformBackendData(cleanedRes, featureIds);
const rawData = transformBackendData(rawRes, featureIds);
return mergeTimeSeriesData(
cleanedData,
rawData,
deviceIds,
featureIds,
"clean",
"raw"
);
@@ -134,9 +142,9 @@ const fetchFromBackend = async (
.catch(() => null),
]);
const cleanedData = transformBackendData(cleanedRes, deviceIds);
const rawData = transformBackendData(rawRes, deviceIds);
const schemeSimData = transformBackendData(schemeSimRes, deviceIds);
const cleanedData = transformBackendData(cleanedRes, featureIds);
const rawData = transformBackendData(rawRes, featureIds);
const schemeSimData = transformBackendData(schemeSimRes, featureIds);
// 合并三组数据
const timeMap = new Map<string, Record<string, number | null>>();
@@ -148,7 +156,7 @@ const fetchFromBackend = async (
timeMap.set(point.timestamp, {});
}
const values = timeMap.get(point.timestamp)!;
deviceIds.forEach((deviceId) => {
featureIds.forEach((deviceId) => {
const value = point.values[deviceId];
if (value !== undefined) {
values[`${deviceId}_${suffix}`] = value;
@@ -184,9 +192,9 @@ const fetchFromBackend = async (
.catch(() => null),
]);
const cleanedData = transformBackendData(cleanedRes, deviceIds);
const rawData = transformBackendData(rawRes, deviceIds);
const simulationData = transformBackendData(simulationRes, deviceIds);
const cleanedData = transformBackendData(cleanedRes, featureIds);
const rawData = transformBackendData(rawRes, featureIds);
const simulationData = transformBackendData(simulationRes, featureIds);
// 合并三组数据
const timeMap = new Map<string, Record<string, number | null>>();
@@ -198,7 +206,7 @@ const fetchFromBackend = async (
timeMap.set(point.timestamp, {});
}
const values = timeMap.get(point.timestamp)!;
deviceIds.forEach((deviceId) => {
featureIds.forEach((deviceId) => {
const value = point.values[deviceId];
if (value !== undefined) {
values[`${deviceId}_${suffix}`] = value;
@@ -384,7 +392,7 @@ const emptyStateMessages: Record<
};
const SCADADataPanel: React.FC<SCADADataPanelProps> = ({
deviceIds,
featureInfos,
type = "realtime",
scheme_type,
scheme_name,
@@ -396,6 +404,12 @@ const SCADADataPanel: React.FC<SCADADataPanelProps> = ({
return fetchTimeSeriesData;
}, [fetchTimeSeriesData]);
// 从 featureInfos 中提取设备 ID 列表
const deviceIds = useMemo(
() => featureInfos.map(([id]) => id),
[featureInfos]
);
const [from, setFrom] = useState<Dayjs>(() => dayjs().subtract(1, "day"));
const [to, setTo] = useState<Dayjs>(() => dayjs());
const [activeTab, setActiveTab] = useState<PanelTab>(defaultTab);
@@ -405,7 +419,7 @@ const SCADADataPanel: React.FC<SCADADataPanelProps> = ({
const [deviceLabels, setDeviceLabels] = useState<Record<string, string>>({});
const [selectedSource, setSelectedSource] = useState<
"raw" | "clean" | "sim" | "all"
>(() => (deviceIds.length === 1 ? "all" : "clean"));
>(() => (featureInfos.length === 1 ? "all" : "clean"));
const draggableRef = useRef<HTMLDivElement>(null);
// 获取 SCADA 设备信息,生成 deviceLabels
@@ -466,7 +480,7 @@ const SCADADataPanel: React.FC<SCADADataPanelProps> = ({
try {
const { from: rangeFrom, to: rangeTo } = normalizedRange;
const result = await customFetcher(
deviceIds,
featureInfos,
{
from: rangeFrom.toDate(),
to: rangeTo.toDate(),
@@ -483,7 +497,7 @@ const SCADADataPanel: React.FC<SCADADataPanelProps> = ({
}
},
[
deviceIds,
featureInfos,
customFetcher,
hasDevices,
normalizedRange,
@@ -500,14 +514,14 @@ const SCADADataPanel: React.FC<SCADADataPanelProps> = ({
} else {
setTimeSeries([]);
}
}, [deviceIds.join(",")]);
}, [JSON.stringify(featureInfos)]);
// 当设备数量变化时,调整数据源选择
useEffect(() => {
if (deviceIds.length > 1 && selectedSource === "all") {
if (featureInfos.length > 1 && selectedSource === "all") {
setSelectedSource("clean");
}
}, [deviceIds.length, selectedSource]);
}, [featureInfos.length, selectedSource]);
const columns: GridColDef[] = useMemo(() => {
const base: GridColDef[] = [
@@ -863,7 +877,7 @@ const SCADADataPanel: React.FC<SCADADataPanelProps> = ({
</Typography>
<Chip
size="small"
label={`${deviceIds.length}`}
label={`${featureInfos.length}`}
sx={{
backgroundColor: "rgba(255,255,255,0.2)",
color: "primary.contrastText",