2017-05-29 17:52:17 -06:00
|
|
|
import React, {Component} from 'react';
|
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import ReactList from 'react-list';
|
|
|
|
|
|
|
|
import { StyleSheet, css } from 'aphrodite/no-important';
|
|
|
|
import { formatMsgDec, formatMsgHex } from '../models/can-msg-fmt';
|
2017-06-13 18:40:05 -06:00
|
|
|
import { elementWiseEquals } from '../utils/array';
|
2017-06-20 16:01:06 -06:00
|
|
|
import Images from '../styles/images';
|
2017-05-29 17:52:17 -06:00
|
|
|
|
|
|
|
export default class CanLog extends Component {
|
|
|
|
static ITEMS_PER_PAGE = 50;
|
|
|
|
|
|
|
|
static propTypes = {
|
|
|
|
plottedSignals: PropTypes.array,
|
2017-06-13 18:40:05 -06:00
|
|
|
segmentIndices: PropTypes.array,
|
2017-05-29 17:52:17 -06:00
|
|
|
onSignalUnplotPressed: PropTypes.func,
|
2017-06-13 18:40:05 -06:00
|
|
|
onSignalPlotPressed: PropTypes.func,
|
|
|
|
message: PropTypes.object,
|
2017-06-14 19:40:30 -06:00
|
|
|
messageIndex: PropTypes.number,
|
|
|
|
onMessageExpanded: PropTypes.func
|
2017-05-29 17:52:17 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
// only want to display up to length elements at a time
|
|
|
|
// offset, length
|
|
|
|
|
|
|
|
this.state = {
|
|
|
|
length: 0,
|
|
|
|
expandedMessages: [],
|
|
|
|
messageHeights: []
|
|
|
|
}
|
|
|
|
|
|
|
|
this.messageRow = this.messageRow.bind(this);
|
|
|
|
this.addDisplayedMessages = this.addDisplayedMessages.bind(this);
|
|
|
|
this.renderMessage = this.renderMessage.bind(this);
|
|
|
|
this.renderTable = this.renderTable.bind(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillReceiveProps(nextProps) {
|
2017-06-13 18:40:05 -06:00
|
|
|
if(nextProps.message && !this.props.message) {
|
2017-05-29 17:52:17 -06:00
|
|
|
this.addDisplayedMessages();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:40:05 -06:00
|
|
|
shouldComponentUpdate(nextProps, nextState) {
|
|
|
|
const curMessageLength = this.props.message ? this.props.message.entries.length : 0;
|
|
|
|
const nextMessageLength = nextProps.message ? nextProps.message.entries.length : 0;
|
|
|
|
|
2017-06-14 18:30:50 -06:00
|
|
|
const shouldUpdate = nextMessageLength != curMessageLength
|
|
|
|
|| nextProps.messageIndex != this.props.messageIndex
|
2017-06-13 18:40:05 -06:00
|
|
|
|| nextProps.plottedSignals.length != this.props.plottedSignals.length
|
|
|
|
|| JSON.stringify(nextProps.segmentIndices) != JSON.stringify(this.props.segmentIndices)
|
|
|
|
|| JSON.stringify(nextState) != JSON.stringify(this.state)
|
|
|
|
|| (this.props.message !== undefined
|
|
|
|
&& nextProps.message !== undefined
|
2017-06-19 21:40:07 -06:00
|
|
|
&&
|
|
|
|
(
|
|
|
|
(this.props.message.signals
|
|
|
|
&& nextProps.message.signals
|
|
|
|
&& JSON.stringify(this.props.message.signals) != JSON.stringify(nextProps.message.signals))
|
|
|
|
||
|
|
|
|
(JSON.stringify(this.props.message.frame) != JSON.stringify(nextProps.message.frame))
|
|
|
|
));
|
2017-06-14 18:30:50 -06:00
|
|
|
return shouldUpdate;
|
2017-06-13 18:40:05 -06:00
|
|
|
}
|
|
|
|
|
2017-05-29 17:52:17 -06:00
|
|
|
addDisplayedMessages() {
|
2017-06-19 17:14:27 -06:00
|
|
|
const {length} = this.state;
|
2017-05-29 17:52:17 -06:00
|
|
|
const newLength = length + CanLog.ITEMS_PER_PAGE;
|
|
|
|
|
2017-06-19 17:14:27 -06:00
|
|
|
this.setState({length: newLength});
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
expandMessage(msg, msgIdx) {
|
|
|
|
this.setState({expandedMessages: this.state.expandedMessages.concat([msg.time])})
|
2017-06-14 19:40:30 -06:00
|
|
|
this.props.onMessageExpanded();
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
collapseMessage(msg, msgIdx) {
|
|
|
|
this.setState({expandedMessages: this.state.expandedMessages
|
|
|
|
.filter((expMsgTime) => expMsgTime !== msg.time)})
|
|
|
|
}
|
|
|
|
|
|
|
|
isSignalPlotted(msgId, signalName) {
|
2017-06-19 19:03:18 -06:00
|
|
|
const plottedSignal = this.props.plottedSignals.find((signal) => signal.messageId == msgId && signal.signalName == signalName);
|
2017-05-29 17:52:17 -06:00
|
|
|
return plottedSignal !== undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
expandedMessage(msg) {
|
|
|
|
return (<div className={css(Styles.row, Styles.signalRow)} key={msg.time + '-expanded'}>
|
|
|
|
<div className={css(Styles.col)}>
|
|
|
|
<div className={css(Styles.signalCol)}>
|
|
|
|
<table>
|
|
|
|
<tbody>
|
|
|
|
{Object.entries(msg.signals).map(([name, value]) => {
|
2017-06-13 18:40:05 -06:00
|
|
|
return [name, value, this.isSignalPlotted(this.props.message.id, name)]
|
2017-05-29 17:52:17 -06:00
|
|
|
}).map(([name, value, isPlotted]) => {
|
2017-06-13 18:40:05 -06:00
|
|
|
const {unit} = this.props.message.signals[name];
|
2017-05-29 17:52:17 -06:00
|
|
|
return (<tr key={name}>
|
|
|
|
<td>{name}</td>
|
|
|
|
<td>{value} {unit}</td>
|
|
|
|
{isPlotted ?
|
2017-06-13 18:40:05 -06:00
|
|
|
<td className={css(Styles.pointerUnderlineHover)}
|
|
|
|
onClick={() => {this.props.onSignalUnplotPressed(this.props.message.id, name)}}>[unplot]</td>
|
2017-05-29 17:52:17 -06:00
|
|
|
:
|
2017-06-13 18:40:05 -06:00
|
|
|
<td className={css(Styles.pointerUnderlineHover)}
|
|
|
|
onClick={() => {this.props.onSignalPlotPressed(this.props.message.id, name)}}>[plot]</td>
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
</tr>);
|
|
|
|
})}
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>)
|
|
|
|
}
|
|
|
|
|
|
|
|
isMessageExpanded(msg) {
|
|
|
|
return this.state.expandedMessages.indexOf(msg.time) !== -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
messageRow(msg, key) {
|
|
|
|
const msgIsExpanded = this.isMessageExpanded(msg);
|
2017-06-19 17:14:27 -06:00
|
|
|
const hasSignals = Object.keys(msg.signals).length > 0;
|
|
|
|
const rowStyle = (hasSignals ? Styles.pointer : null);
|
2017-05-29 17:52:17 -06:00
|
|
|
const row = [<div key={key}
|
2017-06-20 16:46:19 -06:00
|
|
|
className={css(Styles.row, Styles.messageRow, rowStyle)}
|
2017-05-29 17:52:17 -06:00
|
|
|
onClick={() => {
|
2017-06-19 17:14:27 -06:00
|
|
|
if(!hasSignals) return;
|
2017-05-29 17:52:17 -06:00
|
|
|
if(msgIsExpanded) {
|
|
|
|
this.collapseMessage(msg);
|
|
|
|
} else {
|
|
|
|
this.expandMessage(msg);
|
|
|
|
}
|
|
|
|
}}>
|
2017-06-19 17:14:27 -06:00
|
|
|
{hasSignals ?
|
2017-06-20 16:01:06 -06:00
|
|
|
(msgIsExpanded ? <div className={css(Styles.col, Styles.cellCenter)}>{<Images.downArrow />}</div>
|
2017-06-19 17:14:27 -06:00
|
|
|
:
|
2017-06-20 16:01:06 -06:00
|
|
|
<div className={css(Styles.col, Styles.cellCenter)}>{<Images.rightArrow />}</div>
|
2017-06-19 17:14:27 -06:00
|
|
|
)
|
|
|
|
: <div className={css(Styles.col)}></div>
|
|
|
|
}
|
2017-06-13 18:40:05 -06:00
|
|
|
<div className={css(Styles.col, Styles.timefieldCol)}>
|
2017-06-15 16:40:36 -06:00
|
|
|
{msg.relTime.toFixed(3)}
|
|
|
|
</div>
|
|
|
|
<div className={css(Styles.col,
|
|
|
|
Styles.messageCol)}>
|
2017-06-19 21:40:07 -06:00
|
|
|
{(this.props.message.frame ? this.props.message.frame.name : null) || this.props.message.id}
|
2017-06-13 18:40:05 -06:00
|
|
|
</div>
|
|
|
|
<div className={css(Styles.col,
|
|
|
|
Styles.messageCol,
|
2017-06-15 16:40:36 -06:00
|
|
|
Styles.hex)}>
|
|
|
|
{msg.hexData}
|
2017-06-13 18:40:05 -06:00
|
|
|
</div>
|
2017-05-29 17:52:17 -06:00
|
|
|
</div>];
|
|
|
|
|
|
|
|
if(msgIsExpanded) {
|
|
|
|
row.push(this.expandedMessage(msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
return row;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderMessage(index, key) {
|
2017-06-13 18:40:05 -06:00
|
|
|
let offset = this.props.messageIndex;
|
|
|
|
if(offset === 0 && this.props.segmentIndices.length === 2) {
|
|
|
|
offset = this.props.segmentIndices[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.messageRow(this.props.message.entries[offset + index], key);
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
renderTable(items, ref) {
|
|
|
|
return (<div className={css(Styles.root)}>
|
|
|
|
<div className={css(Styles.row)}>
|
2017-06-19 17:14:27 -06:00
|
|
|
|
2017-05-29 17:52:17 -06:00
|
|
|
<div className={css(Styles.col, Styles.dropdownCol)}> </div>
|
2017-06-18 20:07:38 -06:00
|
|
|
<div className={css(Styles.col, Styles.timefieldCol)}>Time (s)</div>
|
2017-06-15 16:40:36 -06:00
|
|
|
<div className={css(Styles.col)}>
|
2017-06-18 20:07:38 -06:00
|
|
|
Message
|
2017-06-15 16:40:36 -06:00
|
|
|
</div>
|
|
|
|
<div className={css(Styles.col)}>
|
|
|
|
Bytes
|
2017-05-29 17:52:17 -06:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className={css(Styles.tableRowGroup)}
|
|
|
|
ref={ref}>
|
|
|
|
{items}
|
|
|
|
</div>
|
|
|
|
</div>)
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:40:05 -06:00
|
|
|
listLength() {
|
|
|
|
const {segmentIndices, messageIndex} = this.props;
|
|
|
|
if(messageIndex > 0) {
|
|
|
|
return this.props.message.entries.length - messageIndex;
|
|
|
|
} else if(segmentIndices.length == 2) {
|
|
|
|
return segmentIndices[1] - segmentIndices[0];
|
|
|
|
} else if(this.props.message) {
|
|
|
|
return this.props.message.entries.length;
|
|
|
|
} else {
|
|
|
|
// no message yet
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-29 17:52:17 -06:00
|
|
|
render() {
|
2017-06-13 18:40:05 -06:00
|
|
|
return <div>
|
|
|
|
<ReactList
|
2017-05-29 17:52:17 -06:00
|
|
|
itemRenderer={this.renderMessage}
|
|
|
|
itemsRenderer={this.renderTable}
|
2017-06-13 18:40:05 -06:00
|
|
|
length={this.listLength()}
|
2017-05-29 17:52:17 -06:00
|
|
|
pageSize={50}
|
2017-06-13 18:40:05 -06:00
|
|
|
updateWhenThisValueChanges={this.props.messageIndex}
|
|
|
|
type='variable' />
|
|
|
|
</div>;
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Styles = StyleSheet.create({
|
|
|
|
root: {
|
|
|
|
borderBottomWidth: '1px',
|
|
|
|
borderColor: 'gray',
|
|
|
|
width: '100%',
|
|
|
|
display: 'table',
|
2017-06-18 20:07:38 -06:00
|
|
|
|
2017-05-29 17:52:17 -06:00
|
|
|
},
|
|
|
|
row: {
|
|
|
|
display: 'table-row',
|
|
|
|
width: 'auto',
|
|
|
|
clear: 'both',
|
|
|
|
},
|
2017-06-19 17:14:27 -06:00
|
|
|
pointer: {
|
2017-05-29 17:52:17 -06:00
|
|
|
cursor: 'pointer',
|
|
|
|
},
|
|
|
|
tableRowGroup: {
|
|
|
|
display: 'table-row-group'
|
|
|
|
},
|
2017-06-20 16:46:19 -06:00
|
|
|
messageRow: {
|
|
|
|
lineHeight: '24px'
|
|
|
|
},
|
2017-05-29 17:52:17 -06:00
|
|
|
signalCol: {
|
2017-06-20 16:46:19 -06:00
|
|
|
width: '1px',
|
|
|
|
paddingBottom: '15px'
|
2017-05-29 17:52:17 -06:00
|
|
|
},
|
|
|
|
col: {
|
|
|
|
display: 'table-cell',
|
|
|
|
},
|
2017-06-20 16:01:06 -06:00
|
|
|
cellCenter: {
|
|
|
|
verticalAlign: 'middle',
|
|
|
|
textAlign: 'center',
|
|
|
|
},
|
2017-05-29 17:52:17 -06:00
|
|
|
dropdownCol: {
|
|
|
|
width: '10px',
|
|
|
|
padding: 0,
|
|
|
|
margin: 0
|
|
|
|
},
|
|
|
|
timefieldCol: {
|
|
|
|
|
|
|
|
},
|
2017-06-13 18:40:05 -06:00
|
|
|
messageCol: {
|
2017-05-29 17:52:17 -06:00
|
|
|
|
|
|
|
},
|
|
|
|
messageFormatHeader: {
|
|
|
|
|
|
|
|
},
|
2017-06-13 18:40:05 -06:00
|
|
|
pointerUnderlineHover: {
|
2017-05-29 17:52:17 -06:00
|
|
|
cursor: 'pointer',
|
|
|
|
':hover': {
|
|
|
|
textDecoration: 'underline'
|
|
|
|
}
|
2017-06-13 18:40:05 -06:00
|
|
|
},
|
|
|
|
hex: {
|
|
|
|
fontFamily: 'monospace'
|
2017-05-29 17:52:17 -06:00
|
|
|
}
|
|
|
|
});
|