22581b2f创建于 2025年12月16日历史提交
/*
 * -------------------------------------------------------------------------
 * This file is part of the MindStudio project.
 * Copyright (c) 2025 Huawei Technologies Co.,Ltd.
 *
 * MindStudio is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 */

import React, { useEffect, useState } from 'react';
import { observer } from 'mobx-react';
import { eventViewData, getDefaultColumData, getPageData, queryOneKernel } from './Common';
import { ResizeTable, fetchColumnFilterProps } from '@insight/lib/resize';
import { getDetailTimeDisplay } from '../../insight/units/AscendUnit';
import type { ThreadMetaData } from '../../entity/data';
import { Button } from '@insight/lib/components';
import { getTimeOffset } from '../../insight/units/utils';
import { useTranslation } from 'react-i18next';
import i18n from '@insight/lib/i18n';
import { DETAIL_HEADER_HEIGHT_ETC_PX, SelectContentViewProps } from './SystemView';
import jumpToUnitOperator from '../../utils/jumpToUnitOperator';
export interface EventTableData {
    eventDetails: EventDetails[];
    columnList: EventTableColumn[];
    count: number;
}

export interface EventDetails {
    id: string;
    start: number;
    name: string;
    duration: number;
    startTime: string;
    tid?: string;
    pid?: string;
    threadName?: string;
    rankId?: string;
    analysisType?: string;
}

export interface EventTableColumn {
    name: string;
    type: string;
    key: string;
}

interface UpdateDatas {
    pages: { current: number; pageSize: number; total: number };
    sorters: { field: string; order: string };
    filters: any;
    props: any;
    setLoading: React.Dispatch<React.SetStateAction<boolean>>;
    setDataSource: React.Dispatch<React.SetStateAction<any[]>>;
    setPage: React.Dispatch<React.SetStateAction<any>>;
    setEventColum: React.Dispatch<React.SetStateAction<string[]>>;
}

const getColumns = (tableColumns: EventTableColumn[], filters: { [key: string]: string[] }): any => {
    const result = [];
    for (const tableColumn of tableColumns) {
        if (tableColumn.key === 'rankId') {
            result.push({ title: i18n.t(`timeline:tableHead.${tableColumn.name}`), dataIndex: tableColumn.key });
        } else if (tableColumn.key === 'name') {
            result.push({ title: i18n.t(`timeline:tableHead.${tableColumn.name}`), dataIndex: tableColumn.key, ...getDefaultColumData(tableColumn.key), ...fetchColumnFilterProps('name', 'Name'), filteredValue: filters.name });
        } else if (tableColumn.key === 'start') {
            result.push({ title: i18n.t(`timeline:tableHead.${tableColumn.name}`), dataIndex: 'startTime', ...getDefaultColumData('startTime') });
        } else {
            result.push({ title: i18n.t(`timeline:tableHead.${tableColumn.name}`), dataIndex: tableColumn.key, ...getDefaultColumData(tableColumn.key) });
        }
    }
    return result;
};

const filterColumn = ['name'];

const defaultPage = { current: 1, pageSize: 10, total: 0 };
const defaultSorter = { field: 'duration', order: 'descend' };
const defaultFilters: { [key: string]: string[] } = { name: [] };
export const EventDetail = observer((props: SelectContentViewProps & { request: any }) => {
    const [dataSource, setDataSource] = useState<any[]>([]);
    const [page, setPage] = useState(defaultPage);
    const [sorter, setSorter] = useState(defaultSorter);
    const [filters, setFilters] = useState(defaultFilters);
    const [isLoading, setLoading] = useState(false);
    const [eventColum, setEventColum] = useState<string[]>([]);
    const [rowData, setRowData] = useState<any>({});
    const [allCondition, setAllCondition] = useState({ showEvent: props.session.showEvent, page, sorter, filters });
    const { t } = useTranslation();

    useEffect(() => {
        setAllCondition({ ...allCondition, page, sorter, filters });
    }, [sorter, filters, page.current, page.pageSize]);
    useEffect(() => {
        setPage(defaultPage);
        setSorter(defaultSorter);
        setFilters(defaultFilters);
        setAllCondition({ ...allCondition, showEvent: props.session.showEvent, page: defaultPage, sorter: defaultSorter, filters: defaultFilters });
    }, [props.session.showEvent, props.session.timeAnalysisRange]);

    useEffect(() => {
        if (props.session.eventUnits === undefined || props.session.eventUnits.length === 0) {
            setDataSource([]);
            setPage(defaultPage);
            setSorter(defaultSorter);
            setFilters(defaultFilters);
            setEventColum([]);
            setAllCondition({ ...allCondition, page: defaultPage, sorter: defaultSorter, filters: defaultFilters });
            return;
        }
        updateData({ pages: allCondition.page, sorters: allCondition.sorter, filters: allCondition.filters, props, setLoading, setDataSource, setPage, setEventColum });
    }, [props.session.timeAnalysisRange, allCondition.showEvent, allCondition.page.current, allCondition.page.pageSize,
        allCondition.sorter.field, allCondition.sorter.order, allCondition.filters, props.session.doReset, t]);

    useEffect(() => {
        if (rowData.name !== null && rowData.name !== undefined) {
            handleSelected(rowData, props);
        }
    }, [rowData]);

    const eventColumns = generateEventColumns(eventColum, setRowData);

    return (
        <div style={{ height: '100%' }}>
            <ResizeTable
                onChange={(pagination: unknown, newFilters: unknown, newsorter: unknown, extra: {action: string}): void => {
                    if (extra.action === 'sort') {
                        setSorter(newsorter as typeof sorter);
                    }
                    if (extra.action === 'filter') {
                        setFilters(newFilters as typeof filters);
                    }
                }}
                pagination={getPageData(page, setPage)} dataSource={dataSource} columns={eventColumns} size="small" loading={isLoading}
                scroll={{ y: props.bottomHeight - DETAIL_HEADER_HEIGHT_ETC_PX }}
                rowClassName={(record: any): string => {
                    return record.id === rowData.id ? 'selected-row' : 'click-able';
                }}
            />
        </div>
    );
});

const updateData = async ({
    pages,
    sorters,
    filters,
    props,
    setLoading,
    setDataSource,
    setPage,
    setEventColum,
}: UpdateDatas): Promise<void> => {
    const filterTypes: string[] = [];
    Object.keys(filters).forEach(key => {
        const filterValue = filters[key];
        if (filterColumn.includes(key) && filterValue != null) {
            if (Array.isArray((filterValue)) && filterValue.length > 0) {
                filterTypes.push(JSON.stringify({ columnName: key, value: filterValue[0] }));
            }
        }
    });
    setLoading(true);
    const res = await searchData(pages, sorters, filterTypes, props).finally(() => setLoading(false));
    const requestData = props.session.eventUnits?.[0]?.metadata as ThreadMetaData;
    const timestampoffset = getTimeOffset(props.session, requestData);
    const data = res.eventDetails.map((item: any) => {
        item.startTime = getDetailTimeDisplay(item.start - timestampoffset);
        return item;
    });
    setEventColum(getColumns(res.columnList, filters));
    setDataSource(data);
    setPage((prevPage: any) => ({ ...pages, total: res.count }));
};

const generateEventColumns = (
    eventColum: string[],
    setRowData: React.Dispatch<React.SetStateAction<any>>,
): any[] => eventColum.length === 0
    ? []
    : [
        ...eventColum,
        {
            title: i18n.t('timeline:tableHead.Click To Timeline'),
            dataIndex: 'click',
            key: 'click',
            ellipsis: true,
            render: (_: any, record: any) => (
                <Button type="link" onClick={(): void => {
                    setRowData(record);
                }}>
                    {i18n.t('timeline:tableHead.Click')}
                </Button>
            ),
        },
    ];

const handleSelected = async(rowData: any, props: SelectContentViewProps): Promise<void> => {
    if (!Array.isArray(props.session.eventUnits) || props.session.eventUnits.length <= 0) { return; }
    const rankId = props.session.eventUnits[0].metadata.cardId as string;
    const dbPath = props.session.eventUnits[0].metadata.dbPath as string;
    const res = await queryOneKernel({
        rankId,
        dbPath,
        name: rowData.name,
        timestamp: rowData.start,
        duration: rowData.duration,
    });
    const depth = rowData.depth > res.depth ? rowData.depth : res.depth;
    jumpToUnitOperator({
        ...rowData,
        cardId: rankId,
        dbPath,
        timestamp: rowData.start,
        tid: rowData.threadId,
        pid: rowData.processId,
        depth,
    });
};

const searchData = async(pages: any, sorters: {field: string;order: string}, filters: string[], prop: any): Promise<EventTableData> => {
    const requestData = prop.session.eventUnits?.[0]?.metadata as ThreadMetaData;
    let startTime = prop.session.timeAnalysisRange?.[0] ?? 0;
    startTime = startTime < 0 ? 0 : startTime;
    let endTime = prop.session.timeAnalysisRange?.[1] ?? 0;
    endTime = endTime < 0 ? 0 : endTime;
    const timestampoffset = getTimeOffset(prop.session, prop.card);
    return await eventViewData({
        rankId: requestData.cardId as string,
        dbPath: requestData.dbPath as string,
        pageSize: pages.pageSize,
        currentPage: pages.current,
        orderBy: sorters.field === 'startTime' ? 'start' : sorters.field ?? defaultSorter.field,
        order: sorters.order ?? defaultSorter.order,
        pid: requestData.processId as string,
        tid: requestData.threadId as string,
        threadIdList: requestData.threadIdList,
        threadName: requestData.threadName,
        processName: requestData.processName as string,
        metaType: requestData.metaType as string,
        filterCondition: filters,
        startTime: Math.floor(startTime + timestampoffset),
        endTime: Math.ceil(endTime + timestampoffset),
    });
};

export const EventView = observer((props: SelectContentViewProps) => {
    return <EventDetail request={eventViewData} {...props} />;
});