blob: 1c3c1ee531a122698f247139449923d0df3fc57b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useState, useEffect } from "react";
import { Card, Layout, Button, Drawer, Tree, Input, Tooltip, DatePicker, Pagination, notification, Table, PageHeader } from 'antd';
import styles from '../style.less'
import { GroupOutlined, CopyOutlined, RetweetOutlined, MinusSquareOutlined, PlusSquareOutlined, QuestionCircleOutlined } from '@ant-design/icons';
import {
getTimeseriesIndexListUsingPOST, getChunkListUsingPOST, getPageListUsingPOST
, getPageInfoThroughTimeseriesIndexOffsetUsingPOST, getTimeseriesIndexInfoUsingPOST
} from '@/services/swagger1/tsfileViewerController'
import moment from 'moment';
import { useIntl } from 'umi';
import { Tree as TreeArborist } from "react-arborist";
const { Sider, Content } = Layout;
const { Search } = Input;
const MoreTimeseriesIndex = (props) => {
const [details, setDetails] = useState()
const [openChunk, setopenChunk] = useState(false)
const [openPage, setopenPage] = useState(false)
const [currentTimeseriesIndex, setCurrentTimeseriesIndex] = useState()
const [pageSize, setPageSize] = useState(10)
const [pageNo, setPageNo] = useState(1)
const [totalSize, setTotalSize] = useState()
const [treeData, setTreeData] = useState([])
const [columns, setColumns] = useState();
const [columnsLength, setColumnsLength] = useState();
const [pageData, setPageData] = useState()
const [beginDateCache, setBeginDateCache] = useState()
const [endDateCache, setEndDateCache] = useState();
const [beginDate, setBeginDate] = useState()
const [endDate, setEndDate] = useState();
const [deviceNameLike, setDeviceNameLike] = useState()
const { fileName, filePath, cardList, setCardList } = props;
const intl = useIntl();
const [structureMapLoading, setStructureMapLoading] = useState();
const [treeHeight, setTreeHeight] = useState()
const [randomFlag, setRandomFlag] = useState()
var pageDataCache;
const gridStyle = {
width: '100%',
background: "#f2f2f2",
};
const showTimeseriesIndexDetails = async (ti) => {
let info = JSON.parse(ti)
setCurrentTimeseriesIndex(info.measurementId);
let res = await getTimeseriesIndexInfoUsingPOST({ offset: info.offset, filePath: filePath })
if (res.code == 0) {
let data = res.data;
data['offset'] = info.offset;
setDetails(data)
} else {
notification.error({
message: res.message,
});
}
}
const showChunk = async (props) => {
if (details == undefined) {
return
}
setStructureMapLoading(true)
let res = await getChunkListUsingPOST({ offset: details.offset, filePath: filePath, offsetType: 'TS_INDEX', beginDate: beginDateCache, endDate: endDateCache })
if (res.code == 0) {
setRandomFlag(moment(new Date()).valueOf())
let tree = Object.values(res.data).map((node) => {
let tree = {};
tree['name'] = node.measurementId;
tree['id'] = node.measurementId + '-' + node.offset + randomFlag;
// tree['icon'] = <RightOutlined />
tree['isLeaf'] = false;
tree['position'] = node.offset;
return tree;
})
setTreeData(tree)
setopenChunk(true);
var div = document.getElementById('tree-div');
setTreeHeight(div.offsetHeight)
setStructureMapLoading(false)
} else {
notification.error({
message: res.message,
});
}
};
const updateTreeData = (list, id, children) => {
return list.map((node) => {
if (node.id === id) {
return {
...node,
children,
};
}
//这个应该是用来加载子节点的子节点的,应该需要修改children对象
if (node.children) {
return {
...node,
children: updateTreeData(node.children, id, children),
};
}
return node;
});
}
const onLoadTreeData = async (expanded, node) => {
if (expanded && node.children == undefined) {
let res = await getPageListUsingPOST({ offset: node.position, filePath: filePath, beginDate: beginDateCache, endDate: endDateCache })
if (res.code == 0) {
let newTree = Object.values(res.data).map((child) => {
let tree = {};
tree['name'] = child.pageNo;
tree['id'] = child.pageNo + '-' + child.offset + randomFlag;
tree['isLeaf'] = true;
tree['position'] = child.offset;
tree['chunkOffset'] = node.position;
tree['pageInfo'] = child;
return tree;
})
let data = treeData;
let pa = updateTreeData(data, node.id, newTree);
setTreeData(pa)
} else {
notification.error({
message: res.message,
});
}
}
}
function Node({ node, style, dragHandle, tree }) {
if (!node.isOpen && !node.data.isLeaf && node.data.children == undefined && node.isSelected) {
onLoadTreeData(!node.isOpen, node.data)
}
/* This node instance can do many things. See the API reference. */
return (
<div style={{ ...style, overflow: "hidden", width: "155vh", textOverflow: "ellipsis", whiteSpace: "nowrap" }} ref={dragHandle} onClick={() => (onSelect(node.data))}>
{node.data.isLeaf ? "" : node.isOpen ? <MinusSquareOutlined onClick={() => (node.toggle())} /> : <PlusSquareOutlined onClick={() => (node.toggle())} />} <span style={{ background: node.isSelected && node.data.isLeaf ? "#FFDFD4" : "white" }}>{node.data.isLeaf ? <CopyOutlined /> : <GroupOutlined />}{node.data.name}</span>
</div>
);
}
const onCloseChunk = () => {
setopenChunk(false);
};
const showPage = () => {
setopenPage(true);
};
const onClosePage = () => {
setopenPage(false);
};
const onSelect = async (info) => {
if (!info.isLeaf) {
return
}
let param = info.pageInfo;
param['timeseriesIndexOffset'] = details.offset;
param['chunkOffset'] = info.chunkOffset;
param['filePath'] = filePath;
param['beginDate'] = beginDate;
param['endDate'] = endDate;
let res = await getPageInfoThroughTimeseriesIndexOffsetUsingPOST(param)
if (res.code == 0) {
//pagedata信息
let cols = [{
title: 'No',
fixed: 'left',
width: '100px',
// render: (text, record, index) => `${index + 1}`, //每一页都从1开始
render: (text, record, index) => {
return index + 1
}
}]
cols.push(...Object.values(res.data.title).map((titleName, key) => {
if (titleName == 'timestamp') {
return {
title: (
<>
{titleName}<span>{'\u00A0\u00A0\u00A0\u00A0'}</span>
<RetweetOutlined
onClick={() => {
pageDataCache = Object.values(pageDataCache).map((item) => {
if ((item[0] + "").indexOf("-") > -1) {
item[0] = moment(item[0], 'YYYY-MM-DD HH:mm:ss.SSS').valueOf()
} else {
item[0] = moment(Number(item[0])).format('YYYY-MM-DD HH:mm:ss.SSS')
}
return item
})
setPageData(pageDataCache)
}}
/>
</>),
dataIndex: titleName,
key: titleName,
fixed: 'left',
width: '250px',
render: (text, record, index) => {
return (
<>
<span id={index}>
{record[key]}
</span>
</>
)
}
}
} else {
return {
title: titleName,
dataIndex: titleName,
key: titleName,
render: (text, record, index) => {
return record[key]
}
}
}
}))
setColumnsLength(cols.length)
setColumns(cols)
setPageData(res.data.values);
pageDataCache = res.data.values;
showPage()
} else {
notification.error({
message: res.message,
});
}
};
const Details = (props) => {
const show = () => {
if (details != undefined && details != '') {
return (
<pre style={{ height: "55vh", overflow: "auto", whiteSpace: "pre-wrap" }}>
{"TimeseriesIndex +" + intl.formatMessage({ id: 'tsviewer.more.briefInfo', }) + "+:\n"}
{"offset:"}{details.offset + "\n"}
{"MEASUREMENTIDS:"}{currentTimeseriesIndex}{"\n"}
{"TM:\n"}{JSON.stringify(details.tm, null, '\t')}{"\n"}
{"FIRST CM:\n"}{JSON.stringify(details.cm, null, '\t')}{"\n"}
</pre>
)
}
}
return (
<div style={{ background: "#f2f2f2", padding: 20, height: "100%" }}>
{show()}
</div>
)
}
const generateTimeSeriesCards = async (page, pSize) => {
let res = await getTimeseriesIndexListUsingPOST({
filePath: filePath, pageNo: page, pageSize: pSize, deviceNameLike: deviceNameLike,
beginDate: beginDate, endDate: endDate
});
//暂存 开始和结束时间
setBeginDateCache(beginDate)
setEndDateCache(endDate)
//清除上一条数据状态
setDetails()
if (res.code == 0) {
let list = Object.values(res.data.pageItems)
setTotalSize(res.data.totalCount)
setCardList(list.map((ti, key) => {
return (
<div style={{ backgroud: "white", margin: '4px 0px 0px 0px' }}>
<Card.Grid key={key} id={"cardgrid" + key}
style={gridStyle}
hoverable={false}
onClick={() => { selectCard("cardgrid" + key), showTimeseriesIndexDetails(JSON.stringify(ti)) }}>
<strong style={{ whiteSpace: 'pre-wrap' }}>[DEVICE]{ti.deviceId} {ti.aligned == true ? '' : '\n[MEASUREMENT]:' + ti.measurementId}</strong>
</Card.Grid>
</div>
)
}))
setPageNo(page)
} else {
notification.error({
message: res.message,
});
}
}
//记录card选中项
var cardSelect;
const selectCard = (id) => {
if (id == cardSelect) {
return
}
var card = document.getElementById(id);
card.style.background = "#FC4C2F"
var oldCard = document.getElementById(cardSelect);
if (oldCard != null) {
oldCard.style.background = "#f2f2f2";
}
cardSelect = id;
}
useEffect(() => {
generateTimeSeriesCards(1, pageSize);
}, [])
return (
<div className={styles.sitedrawerrenderincurrentwrapper}>
<Layout>
<Sider width="55%" style={{ height: "70vh", width: "60%", background: "white", padding: 5 }}>
<div style={{ height: "70vh", overflow: "auto" }}>
<PageHeader
extra={(
<Button.Group>
<DatePicker
format='YYYY-MM-DD HH:mm:ss'
placeholder={intl.formatMessage({ id: 'tsviewer.more.beginDate', })}
showTime={{ format: 'HH:mm:ss' }}
onChange={(date) => {
if (date != null) {
date = date.set({ millisecond: 0 })
}
setBeginDate(isNaN(moment(date).valueOf()) ? '' : moment(date).valueOf())
}}
/>
<DatePicker
format='YYYY-MM-DD HH:mm:ss'
placeholder={intl.formatMessage({ id: 'tsviewer.more.endDate', })}
showTime={{ format: 'HH:mm:ss' }}
onChange={(date) => {
if (date != null) {
date = date.set({ millisecond: 0 })
}
setEndDate(isNaN(moment(date).valueOf()) ? '' : moment(date).valueOf())
}}
/>
<Search
placeholder="device"
allowClear
onChange={(e) => {
setDeviceNameLike(e.target.value)
}}
onSearch={() => {setCardList([]),generateTimeSeriesCards(1, pageSize)}}
style={{
width: 200,
}}
/>
</Button.Group>
)}>
</PageHeader>
<Card style={{ height: "55vh", overflow: "auto" }}>
{cardList}
</Card>
<div style={{ width: "100%", textAlign: "center" }}>
<Pagination style={{ margin: '20px 0px 0px 0px' }} current={pageNo} pageSize={pageSize} total={totalSize}
showQuickJumper={true}
onChange={(page, pSize) => { setCardList([]), generateTimeSeriesCards(page, pSize) }} showSizeChanger={false} />
</div>
</div>
</Sider>
<Layout>
<Content width="40%" style={{ height: "60vh", background: "white" }}>
<PageHeader
style={{ background: "#f2f2f2" }}
extra={(
<Button type="primary" onClick={() => showChunk(details)}>{intl.formatMessage({ id: 'tsviewer.more.structureMap', })}</Button>
)}>
</PageHeader>
<Details></Details>
</Content>
</Layout>
</Layout>
<Drawer
title={<>
<Tooltip placement="bottom" title={<span>
{intl.formatMessage({ id: 'tsviewer.moreChunkGroup.chunk.explanation', })}<br />
{intl.formatMessage({ id: 'tsviewer.moreChunkGroup.chunk.explanation1', })}<br />
{intl.formatMessage({ id: 'tsviewer.moreChunkGroup.chunk.explanation2', })}<br />
</span>}>
<QuestionCircleOutlined />
</Tooltip>
<span>
{"\u00A0\u00A0 ChunkInfo"}
</span>
</>}
width={"80%"}
closable={false}
destroyOnClose={true}
onClose={onCloseChunk}
open={openChunk}
>
<div id="tree-div" style={{ height: "80vh", background: "white", margin: '4px 0px 0px 0px' }}>
<TreeArborist
openByDefault={false}
disableDrag={false}
width={"100%"}
height={treeHeight}
// paddingBottom={200}
//height={400}
data={treeData}
>
{Node}
</TreeArborist>
</div>
<Drawer
title={<>
<Tooltip placement="bottom" title={<span>
{intl.formatMessage({ id: 'tsviewer.moreChunkGroup.pageData.explanation', })}<br />
</span>}>
<QuestionCircleOutlined />
</Tooltip>
<span>
{"\u00A0\u00A0 PageData"}
</span>
</>}
width={"75%"}
closable={false}
destroyOnClose={true}
onClose={onClosePage}
open={openPage}
>
<Table columns={columns} dataSource={pageData} scroll={{ x: 180 * columnsLength, y: "80vh" }}
rowKey={(record) => {
return record[0];
}}
pagination={{ defaultPageSize: 100, showQuickJumper: true, position: ["bottomCenter"] }}
bordered />
</Drawer>
</Drawer>
</div>
)
}
export default MoreTimeseriesIndex