* -------------------------------------------------------------------------
* 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 { makeAutoObservable } from 'mobx';
import {
ActiveDataSource,
type DataSource,
FileOrDirectory,
GLOBAL_HOST,
LayerType,
Project,
} from '@/centralServer/websocket/defs';
import type { CompareData } from '@/utils/Compare';
import { SessionAction } from '@/utils/enum';
import { deleteProjectDataPath } from '@/utils/Project';
export type Scene = 'Default' | 'Cluster' | 'Compute' | 'OnlyTraceJson' | 'IE' | 'Leaks' | 'RL' | 'HybridParse' | 'Triton';
interface ContextMenu {
visible: boolean;
}
export interface File {
projectName: string;
fileType: LayerType;
filePath: string;
rankId?: string;
}
export interface ClusterFile extends File {
baselineClusterPath: string;
currentClusterPath: string;
}
export interface Rank extends File {
rankId: string;
cardName: string;
cardPath: string;
host?: string;
}
interface ClusterInfo {
name: string;
path: string;
parsed: boolean;
durationParsed: boolean;
}
export interface ClusterPageInfo {
clusterList: ClusterInfo[];
selectedClusterPath: string;
}
export interface TimelinePageInfo {
unitCount: number;
}
export interface CardInfo {
cardId: string;
dbPath: string;
index?: number;
}
export interface RankInfo {
clusterId: string;
host: string;
rankName: string;
rankId: string;
deviceId: string;
}
export interface CardRankInfo {
rankInfo: RankInfo;
dbPath: string;
index?: number;
}
export const DEFAULT_ACTIVE_DATASOURCE: ActiveDataSource = { ...GLOBAL_HOST, projectName: '', projectPath: [], children: [], selectedFileType: 'UNKNOWN', selectedFilePath: '' };
export class Session {
language: 'zhCN' | 'enUS' = 'enUS';
defaultConnected?: boolean;
actionListener: { type: SessionAction; value: string } = { type: SessionAction.NO_ACTION, value: '' };
loading: boolean = false;
rankMap: Map<string, Rank> = new Map();
isCluster: boolean | null = false;
isBinary: boolean | null = false;
isIE: boolean | null = false;
isReset: boolean = false;
isFullDb: boolean = false;
isOnlyTraceJson: boolean = false;
isLeaks: boolean = false;
isTriton: boolean = false;
isRL: boolean = false;
isHybridParse: boolean = false;
hasCachelineRecords: boolean = false;
instrVersion: number = -1;
parseCompleted: boolean = false;
clusterPageInfo: ClusterPageInfo = { clusterList: [], selectedClusterPath: '' };
timelinePageInfo: TimelinePageInfo = { unitCount: 0 };
startTime: number = -1;
endTimeAll: number = -1;
unitcount: number = 0;
memoryCardInfos: Array<Required<CardRankInfo>> = [];
operatorCardInfos: Array<Required<CardRankInfo>> = [];
iERankIds: string[] = [];
deviceIds: any = {};
threadIds: number[] = [];
module: string = '';
tritonParsed: boolean = false;
coreList: string[] = [];
sourceList: string[] = [];
contextMenu: ContextMenu = { visible: false };
selectedFile: File = { projectName: '', fileType: 'UNKNOWN', filePath: '' };
compareSet: { baseline: CompareData; comparison: CompareData } = {
baseline: { projectName: '', fileType: 'UNKNOWN', filePath: '', rankId: '' },
comparison: { projectName: '', fileType: 'UNKNOWN', filePath: '', rankId: '' },
};
toIframeUrl: string = '';
projectContentEditStatus: boolean = false;
profilingExpertDataParsed: boolean | null = null;
toBeActivedProject: Project | undefined;
private _dataSources: DataSource[] = [];
private _dataSourceIndexMap: Map<string, number> = new Map();
private _activeDataSource: ActiveDataSource = DEFAULT_ACTIVE_DATASOURCE;
constructor() {
makeAutoObservable(this);
}
get scene(): Scene {
let scene: Scene;
if (this.isHybridParse) {
scene = 'HybridParse';
} else if (this.isOnlyTraceJson) {
scene = 'OnlyTraceJson';
} else if (this.isLeaks) {
scene = 'Leaks';
} else if (this.isTriton) {
scene = 'Triton';
} else if (this.isBinary) {
scene = 'Compute';
} else if (this.isCluster) {
scene = 'Cluster';
} else if (this.isIE) {
scene = 'IE';
} else {
scene = 'Default';
}
return scene;
}
get isCompareStatus(): boolean {
const { baseline, comparison } = this.compareSet;
return baseline.projectName !== '' && comparison.filePath !== '' && comparison.projectName !== '' && comparison.filePath !== '';
}
get dataSources(): DataSource[] {
return this._dataSources;
}
set dataSources(data: DataSource[]) {
this._dataSources = data;
this._dataSourceIndexMap = new Map();
data.forEach((item, index): void => {
this._dataSourceIndexMap.set(item.projectName, index);
});
}
get activeDataSource(): ActiveDataSource {
return this._activeDataSource;
}
set activeDataSource(data: ActiveDataSource) {
this._activeDataSource = data;
this.updateClusterPageInfo(data);
}
getDataSourceByProjectName(name: string): DataSource | undefined {
const idx = this._dataSourceIndexMap.get(name);
if (idx === undefined) {
return undefined;
}
return this._dataSources[idx];
}
setClusterParsed(clusterPath: string): void {
const currentClusterList = [...this.clusterPageInfo.clusterList];
const foundIdx = currentClusterList.findIndex(({ path }) => path === clusterPath);
if (foundIdx >= 0) {
currentClusterList[foundIdx].parsed = true;
}
this.clusterPageInfo.clusterList = currentClusterList;
}
setClusterDurationParsed(clusterPath: string): void {
const currentClusterList = [...this.clusterPageInfo.clusterList];
const foundIdx = currentClusterList.findIndex(({ path }) => path === clusterPath);
if (foundIdx >= 0) {
currentClusterList[foundIdx].durationParsed = true;
}
this.clusterPageInfo.clusterList = currentClusterList;
}
reset(remove?: boolean): void {
this.isCluster = remove ? false : null;
this.isBinary = remove ? false : null;
this.isIE = remove ? false : null;
this.isRL = false;
this.hasCachelineRecords = false;
this.toIframeUrl = '';
this.startTime = -1;
this.endTimeAll = -1;
this.parseCompleted = false;
this.clusterPageInfo = { clusterList: [], selectedClusterPath: '' };
this.timelinePageInfo = { unitCount: 0 };
this.unitcount = 0;
this.coreList = [];
this.sourceList = [];
this.memoryCardInfos = [];
this.operatorCardInfos = [];
this.iERankIds = [];
this.compareSet = {
baseline: { projectName: '', fileType: 'UNKNOWN', filePath: '', rankId: '' },
comparison: { projectName: '', fileType: 'UNKNOWN', filePath: '', rankId: '' },
};
this.profilingExpertDataParsed = null;
this.isHybridParse = false;
this.deviceIds = {};
this.threadIds = [];
this.module = '';
}
deleteDataSource(projectIndex: number): void {
if (projectIndex < 0 || projectIndex >= this._dataSources.length) {
throw new RangeError('projectIndex out of range');
}
this.dataSources = this._dataSources.filter((dataSource, index) => index !== projectIndex);
}
deleteDataPath(projectIndex: number, dataPath: string): DataSource {
if (projectIndex < 0 || projectIndex >= this._dataSources.length) {
throw new RangeError('projectIndex out of range');
}
const dataSources = JSON.parse(JSON.stringify(this._dataSources));
deleteProjectDataPath(dataSources[projectIndex], dataPath);
this.dataSources = dataSources;
return dataSources[projectIndex];
}
* 获取 cluster 类型的 path
* 导入:
* 1. 非集群
* 2. 单集群,无 cluster
* - 导入 rank路径
* - 点 project rank路径
* - 点 rank rank路径
* - 二次导入 project路径
* 3. 单集群,有 cluster
* - 导入 rank路径
* - 点 project rank路径
* - 点 cluster cluster路径
* - 点 rank rank路径
* - 二次导入 project路径
* 4. 多集群
* - 导入 rank路径
* - 点 project rank路径
* - 点 cluster cluster路径
* - 点 rank rank路径
* - 二次导入 project路径
* @param selectedProject 选中的工程项目
*/
getClusterPath(selectedProject: ActiveDataSource): string {
const selectedFilePath = selectedProject.selectedFilePath;
let clusterPath: string = '';
const findClusterPath = (children: FileOrDirectory[], depth: number): string => {
if (depth >= 5) { return ''; }
for (let i = 0; i < children.length; ++i) {
const item = children[i];
if (item.type === 'CLUSTER') {
clusterPath = item.path;
}
if (item.path === selectedFilePath) {
return clusterPath;
}
const childFindPath = findClusterPath(item.children, depth + 1);
if (childFindPath !== '') {
return childFindPath;
}
if (item.type === 'CLUSTER') {
clusterPath = '';
}
}
return '';
};
const findFirstClusterPath = (children: FileOrDirectory[], depth: number): string => {
if (depth >= 5) { return ''; }
for (let i = 0; i < children.length; ++i) {
const item = children[i];
if (item.type === 'CLUSTER') {
return item.path;
}
const childFindPath = findFirstClusterPath(item.children, depth + 1);
if (childFindPath !== '') {
return childFindPath;
}
}
return '';
};
return (selectedProject.selectedFileType === 'PROJECT' ? findFirstClusterPath : findClusterPath)(selectedProject.children, 0);
}
private getClusterInfoByPath(path: string): ClusterInfo | undefined {
return this.clusterPageInfo.clusterList.find((item) => item.path === path);
}
private updateClusterPageInfo(selectedProject: ActiveDataSource): void {
if (selectedProject.projectName === '') {
return;
}
let clusterList = selectedProject.children.filter((child) => child.type === 'CLUSTER').map((child) => {
const prev = this.getClusterInfoByPath(child.path);
return {
name: child.name,
path: child.path,
parsed: prev?.parsed ?? false,
durationParsed: prev?.durationParsed ?? false,
} as ClusterInfo;
});
let selectedClusterPath: string = this.getClusterPath(selectedProject);
if (clusterList.length === 0) {
if (this.clusterPageInfo.clusterList.length !== 0) {
return;
}
const prev = this.getClusterInfoByPath(selectedProject.projectPath[0]);
clusterList = [{
name: selectedProject.projectName,
path: selectedProject.projectPath[0],
parsed: prev?.parsed ?? false,
durationParsed: prev?.durationParsed ?? false,
}];
selectedClusterPath = selectedProject.projectPath[0];
}
this.clusterPageInfo.clusterList = clusterList;
this.clusterPageInfo.selectedClusterPath = selectedClusterPath;
}
resetActionListener(): void {
this.actionListener = { type: SessionAction.NO_ACTION, value: '' };
}
}