Add error service
This commit is contained in:
parent
0ad6b11ebf
commit
6f6c6b7714
1 changed files with 86 additions and 46 deletions
132
src/player.jsx
132
src/player.jsx
|
|
@ -26,21 +26,6 @@ let $ = require("jquery");
|
||||||
require("console.css");
|
require("console.css");
|
||||||
require("bootstrap-slider");
|
require("bootstrap-slider");
|
||||||
|
|
||||||
/*
|
|
||||||
* Get an object field, verifying its presence and type.
|
|
||||||
*/
|
|
||||||
let getValidField = function (object, field, type) {
|
|
||||||
let value;
|
|
||||||
if (!(field in object)) {
|
|
||||||
throw Error("\"" + field + "\" field is missing");
|
|
||||||
}
|
|
||||||
value = object[field];
|
|
||||||
if (typeof (value) != typeof (type)) {
|
|
||||||
throw Error("invalid \"" + field + "\" field type: " + typeof (value));
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
let scrollToBottom = function(id) {
|
let scrollToBottom = function(id) {
|
||||||
const el = document.getElementById(id);
|
const el = document.getElementById(id);
|
||||||
if (el) {
|
if (el) {
|
||||||
|
|
@ -48,6 +33,54 @@ let scrollToBottom = function(id) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function ErrorList(props) {
|
||||||
|
let list = [];
|
||||||
|
|
||||||
|
if (props.list) {
|
||||||
|
list = props.list.map((message, key) => { return <ErrorItem key={key} message={message} /> });
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{list}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ErrorItem(props) {
|
||||||
|
return (
|
||||||
|
<div className="alert alert-danger alert-dismissable" >
|
||||||
|
<button type="button" className="close" data-dismiss="alert" aria-hidden="true">
|
||||||
|
<span className="pficon pficon-close" />
|
||||||
|
</button>
|
||||||
|
<span className="pficon pficon-error-circle-o" />
|
||||||
|
{props.message}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ErrorService = class {
|
||||||
|
constructor() {
|
||||||
|
this.addMessage = this.addMessage.bind(this);
|
||||||
|
this.errors = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
addMessage(message) {
|
||||||
|
if (typeof message === "object" && message !== null) {
|
||||||
|
if ("toString" in message) {
|
||||||
|
message = message.toString();
|
||||||
|
} else {
|
||||||
|
message = _("unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof message === "string" || message instanceof String) {
|
||||||
|
if (this.errors.indexOf(message) === -1) {
|
||||||
|
this.errors.push(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* An auto-loading buffer of recording's packets.
|
* An auto-loading buffer of recording's packets.
|
||||||
*/
|
*/
|
||||||
|
|
@ -55,10 +88,11 @@ let PacketBuffer = class {
|
||||||
/*
|
/*
|
||||||
* Initialize a buffer.
|
* Initialize a buffer.
|
||||||
*/
|
*/
|
||||||
constructor(matchList) {
|
constructor(matchList, reportError) {
|
||||||
this.handleError = this.handleError.bind(this);
|
this.handleError = this.handleError.bind(this);
|
||||||
this.handleStream = this.handleStream.bind(this);
|
this.handleStream = this.handleStream.bind(this);
|
||||||
this.handleDone = this.handleDone.bind(this);
|
this.handleDone = this.handleDone.bind(this);
|
||||||
|
this.getValidField = this.getValidField.bind(this);
|
||||||
/* RegExp used to parse message's timing field */
|
/* RegExp used to parse message's timing field */
|
||||||
this.timingRE = new RegExp(
|
this.timingRE = new RegExp(
|
||||||
/* Delay (1) */
|
/* Delay (1) */
|
||||||
|
|
@ -81,6 +115,7 @@ let PacketBuffer = class {
|
||||||
);
|
);
|
||||||
/* List of matches to apply when loading the buffer from Journal */
|
/* List of matches to apply when loading the buffer from Journal */
|
||||||
this.matchList = matchList;
|
this.matchList = matchList;
|
||||||
|
this.reportError = reportError;
|
||||||
/*
|
/*
|
||||||
* An array of two-element arrays (tuples) each containing a
|
* An array of two-element arrays (tuples) each containing a
|
||||||
* packet index and a deferred object. The list is kept sorted to
|
* packet index and a deferred object. The list is kept sorted to
|
||||||
|
|
@ -119,6 +154,21 @@ let PacketBuffer = class {
|
||||||
this.done = false;
|
this.done = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get an object field, verifying its presence and type.
|
||||||
|
*/
|
||||||
|
getValidField(object, field, type) {
|
||||||
|
let value;
|
||||||
|
if (!(field in object)) {
|
||||||
|
this.reportError("\"" + field + "\" field is missing");
|
||||||
|
}
|
||||||
|
value = object[field];
|
||||||
|
if (typeof (value) != typeof (type)) {
|
||||||
|
this.reportError("invalid \"" + field + "\" field type: " + typeof (value));
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return a promise which is resolved when a packet at a particular
|
* Return a promise which is resolved when a packet at a particular
|
||||||
* index is received by the buffer. The promise is rejected with a
|
* index is received by the buffer. The promise is rejected with a
|
||||||
|
|
@ -235,6 +285,7 @@ let PacketBuffer = class {
|
||||||
this.idxDfdList[i][1].reject(error);
|
this.idxDfdList[i][1].reject(error);
|
||||||
}
|
}
|
||||||
this.idxDfdList = [];
|
this.idxDfdList = [];
|
||||||
|
this.reportError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -257,7 +308,7 @@ let PacketBuffer = class {
|
||||||
/* Match next timing entry */
|
/* Match next timing entry */
|
||||||
matches = this.timingRE.exec(timing);
|
matches = this.timingRE.exec(timing);
|
||||||
if (matches === null) {
|
if (matches === null) {
|
||||||
throw Error("invalid timing string");
|
this.reportError(_("invalid timing string"));
|
||||||
} else if (matches[0] == "") {
|
} else if (matches[0] == "") {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -297,7 +348,7 @@ let PacketBuffer = class {
|
||||||
/* Add (replacement) input characters */
|
/* Add (replacement) input characters */
|
||||||
s = in_txt.slice(in_txt_pos, in_txt_pos += x);
|
s = in_txt.slice(in_txt_pos, in_txt_pos += x);
|
||||||
if (s.length != x) {
|
if (s.length != x) {
|
||||||
throw Error("timing entry out of input bounds");
|
this.reportError(_("timing entry out of input bounds"));
|
||||||
}
|
}
|
||||||
io.push(s);
|
io.push(s);
|
||||||
break;
|
break;
|
||||||
|
|
@ -319,7 +370,7 @@ let PacketBuffer = class {
|
||||||
/* Add (replacement) output characters */
|
/* Add (replacement) output characters */
|
||||||
s = out_txt.slice(out_txt_pos, out_txt_pos += x);
|
s = out_txt.slice(out_txt_pos, out_txt_pos += x);
|
||||||
if (s.length != x) {
|
if (s.length != x) {
|
||||||
throw Error("timing entry out of output bounds");
|
this.reportError(_("timing entry out of output bounds"));
|
||||||
}
|
}
|
||||||
io.push(s);
|
io.push(s);
|
||||||
break;
|
break;
|
||||||
|
|
@ -348,10 +399,10 @@ let PacketBuffer = class {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_txt_pos < in_txt.length) {
|
if (in_txt_pos < in_txt.length) {
|
||||||
throw Error("extra input present");
|
this.reportError(_("extra input present"));
|
||||||
}
|
}
|
||||||
if (out_txt_pos < out_txt.length) {
|
if (out_txt_pos < out_txt.length) {
|
||||||
throw Error("extra output present");
|
this.reportError(_("extra output present"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (io.length > 0) {
|
if (io.length > 0) {
|
||||||
|
|
@ -375,24 +426,24 @@ let PacketBuffer = class {
|
||||||
const string = String();
|
const string = String();
|
||||||
|
|
||||||
/* Check version */
|
/* Check version */
|
||||||
ver = getValidField(message, "ver", string);
|
ver = this.getValidField(message, "ver", string);
|
||||||
matches = ver.match("^(\\d+)\\.(\\d+)$");
|
matches = ver.match("^(\\d+)\\.(\\d+)$");
|
||||||
if (matches === null || matches[1] > 2) {
|
if (matches === null || matches[1] > 2) {
|
||||||
throw Error("\"ver\" field has invalid value: " + ver);
|
this.reportError("\"ver\" field has invalid value: " + ver);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO Perhaps check host, rec, user, term, and session fields */
|
/* TODO Perhaps check host, rec, user, term, and session fields */
|
||||||
|
|
||||||
/* Extract message ID */
|
/* Extract message ID */
|
||||||
id = getValidField(message, "id", number);
|
id = this.getValidField(message, "id", number);
|
||||||
if (id <= this.id) {
|
if (id <= this.id) {
|
||||||
throw Error("out of order \"id\" field value: " + id);
|
this.reportError("out of order \"id\" field value: " + id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extract message time position */
|
/* Extract message time position */
|
||||||
pos = getValidField(message, "pos", number);
|
pos = this.getValidField(message, "pos", number);
|
||||||
if (pos < this.message_pos) {
|
if (pos < this.message_pos) {
|
||||||
throw Error("out of order \"pos\" field value: " + pos);
|
this.reportError("out of order \"pos\" field value: " + pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update last received message ID and time position */
|
/* Update last received message ID and time position */
|
||||||
|
|
@ -401,9 +452,9 @@ let PacketBuffer = class {
|
||||||
|
|
||||||
/* Parse message data */
|
/* Parse message data */
|
||||||
this.parseMessageData(
|
this.parseMessageData(
|
||||||
getValidField(message, "timing", string),
|
this.getValidField(message, "timing", string),
|
||||||
getValidField(message, "in_txt", string),
|
this.getValidField(message, "in_txt", string),
|
||||||
getValidField(message, "out_txt", string));
|
this.getValidField(message, "out_txt", string));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -573,7 +624,6 @@ export class Player extends React.Component {
|
||||||
containerWidth: 630,
|
containerWidth: 630,
|
||||||
currentTsPost: 0,
|
currentTsPost: 0,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
error: null,
|
|
||||||
input: "",
|
input: "",
|
||||||
mark: 0,
|
mark: 0,
|
||||||
};
|
};
|
||||||
|
|
@ -581,7 +631,9 @@ export class Player extends React.Component {
|
||||||
this.containerHeight = 290;
|
this.containerHeight = 290;
|
||||||
|
|
||||||
/* Auto-loading buffer of recording's packets */
|
/* Auto-loading buffer of recording's packets */
|
||||||
this.buf = new PacketBuffer(this.props.matchList);
|
this.error_service = new ErrorService();
|
||||||
|
this.reportError = this.error_service.addMessage;
|
||||||
|
this.buf = new PacketBuffer(this.props.matchList, this.reportError);
|
||||||
|
|
||||||
/* Current recording time, ms */
|
/* Current recording time, ms */
|
||||||
this.recTS = 0;
|
this.recTS = 0;
|
||||||
|
|
@ -655,7 +707,7 @@ export class Player extends React.Component {
|
||||||
/* Handle packet retrieval error */
|
/* Handle packet retrieval error */
|
||||||
handleError(error) {
|
handleError(error) {
|
||||||
if (error !== null) {
|
if (error !== null) {
|
||||||
this.setState({error: error});
|
this.reportError(error);
|
||||||
console.warn(error);
|
console.warn(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1046,18 +1098,6 @@ export class Player extends React.Component {
|
||||||
"float": "right",
|
"float": "right",
|
||||||
};
|
};
|
||||||
|
|
||||||
let error = "";
|
|
||||||
if (this.state.error) {
|
|
||||||
error = (
|
|
||||||
<div className="alert alert-danger alert-dismissable" >
|
|
||||||
<button type="button" className="close" data-dismiss="alert" aria-hidden="true">
|
|
||||||
<span className="pficon pficon-close" />
|
|
||||||
</button>
|
|
||||||
<span className="pficon pficon-error-circle-o" />
|
|
||||||
{this.state.error}.
|
|
||||||
</div>);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure react never reuses this div by keying it with the terminal widget
|
// ensure react never reuses this div by keying it with the terminal widget
|
||||||
return (
|
return (
|
||||||
<div id="recording-wrap">
|
<div id="recording-wrap">
|
||||||
|
|
@ -1124,8 +1164,8 @@ export class Player extends React.Component {
|
||||||
<div id="input-player-wrap">
|
<div id="input-player-wrap">
|
||||||
<InputPlayer input={this.state.input} />
|
<InputPlayer input={this.state.input} />
|
||||||
</div>
|
</div>
|
||||||
|
<ErrorList list={this.error_service.errors} />
|
||||||
</div>
|
</div>
|
||||||
{error}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue