调整后端api,完善历史数据面板的交互
This commit is contained in:
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user