Automatic seq graph rendering, with debounce

This commit is contained in:
Ladd Hoffman 2022-11-14 10:17:43 -06:00
parent 41506fdcd5
commit a481710cde
12 changed files with 115 additions and 37 deletions

View File

@ -47,7 +47,7 @@ export class Box {
} }
setId(id) { setId(id) {
this.el.id = id || this.name; this.el.id = (id || this.name).replace(/ /g, "");
return this; return this;
} }

View File

@ -37,7 +37,10 @@ class Reputation {
const tokensLocked = Array.from(this.locks.values()) const tokensLocked = Array.from(this.locks.values())
.filter(({dateCreated, duration}) => now - dateCreated < duration) .filter(({dateCreated, duration}) => now - dateCreated < duration)
.reduce((acc, cur) => acc += cur.tokens, 0); .reduce((acc, cur) => acc += cur.tokens, 0);
return Math.max(this.tokens - tokensLocked, 0); if (tokensLocked > this.tokens) {
throw new Error("Assertion failure. tokensLocked > tokens");
}
return this.tokens - tokensLocked;
} }
} }

View File

@ -1,6 +1,7 @@
import { Actor } from './actor.js'; import { Actor } from './actor.js';
import { Action } from './action.js'; import { Action } from './action.js';
// import mermaid from 'https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.mjs'; import { debounce } from '../util.js';
import mermaid from 'https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.mjs';
export class Scene { export class Scene {
constructor(name, rootBox) { constructor(name, rootBox) {
@ -10,11 +11,14 @@ export class Scene {
this.box.addBox('Spacer').setInnerHTML('&nbsp;'); this.box.addBox('Spacer').setInnerHTML('&nbsp;');
this.displayValuesBox = this.box.addBox(`${this.name}-values`); this.displayValuesBox = this.box.addBox(`${this.name}-values`);
this.box.addBox('Spacer').setInnerHTML('&nbsp;'); this.box.addBox('Spacer').setInnerHTML('&nbsp;');
this.logBox = this.box.addBox(`${this.name}-log`);
this.actors = new Set(); this.actors = new Set();
// this.seqDiagramContainer = this.box.addBox(`${this.name}-seq-diagram-container`); this.seqDiagramContainer = this.box.addBox(`${this.name}-seq-diagram-container`);
// this.seqDiagramBox = this.box.addBox(`${this.name}-seq-diagram`); this.seqDiagramElement = this.box.addBox(`${this.name}-seq-diagram-element`).setId();
// mermaid.mermaidAPI.initialize({ startOnLoad: false }); this.seqDiagramBox = this.box.addBox(`${this.name}-seq-diagram`);
this.box.addBox('Spacer').setInnerHTML('&nbsp;');
this.logBox = this.box.addBox(`${this.name}-log`);
mermaid.mermaidAPI.initialize({ startOnLoad: false });
this.dateLastRender = null;
} }
addActor(name) { addActor(name) {
@ -38,6 +42,7 @@ export class Scene {
log(msg) { log(msg) {
this.logBox.addBox().setInnerHTML(msg).monospace(); this.logBox.addBox().setInnerHTML(msg).monospace();
this.renderSequenceDiagram();
return this; return this;
} }
@ -50,16 +55,24 @@ export class Scene {
} }
} }
// async renderSequenceDiagram() { async renderSequenceDiagram() {
// await mermaid.mermaidAPI.render( const render = async () => {
// `${this.name}-seq-diagram-element`, const dateStart = new Date();
// this.logBox.getInnerText(), const graph = await mermaid.mermaidAPI.render(
// this.insertSvg, this.seqDiagramElement.getId(),
// this.seqDiagramContainer.el this.logBox.getInnerText(),
// ); );
// } this.seqDiagramBox.setInnerHTML(graph);
if (!this.dateLastRender) {
this.dateLastRender = new Date();
}
console.log(`renderSequenceDiagram time: ${new Date() - dateStart} ms, time since last render: ${dateStart - this.dateLastRender}`);
this.dateLastRender = dateStart;
};
debounce(render, 100);
}
// insertSvg (svgCode) { insertSvg (svgCode) {
// this.seqDiagramBox.setInnerHTML(svgCode); this.seqDiagramElement.setInnerHTML(svgCode);
// }; };
} }

View File

@ -35,6 +35,7 @@ export class ValidationPool extends Actor {
against: fee * params.mintingRatio * (1 - params.stakeForWin), against: fee * params.mintingRatio * (1 - params.stakeForWin),
author: fee * params.mintingRatio * params.stakeForAuthor, author: fee * params.mintingRatio * params.stakeForAuthor,
} }
// TODO: Consider availability stakes
} }
castVote(signingPublicKey, position, stake, lockingTime) { castVote(signingPublicKey, position, stake, lockingTime) {
@ -126,7 +127,7 @@ export class ValidationPool extends Actor {
distributeTokens(result) { distributeTokens(result) {
// Reward the author // Reward the author
// TODO: Penalty to the author if the vote does not pass? // TODO: If the vote fails, distribute tokens.author among winning voters
this.bench.reputations.addTokens(this.authorId, this.tokens.author); this.bench.reputations.addTokens(this.authorId, this.tokens.author);
// Reward the vote winners, in proportion to their stakes // Reward the vote winners, in proportion to their stakes
const tokensForWinners = result ? this.tokens.for : this.tokens.against; const tokensForWinners = result ? this.tokens.for : this.tokens.against;

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<head>
<title>Forum Graph</title>
<script type="module" src="./debounce-test.js" defer></script>
<link type="text/css" rel="stylesheet" href="./index.css" />
</head>
<body>
<div id="debounce-test"></div>
</body>

View File

@ -0,0 +1,15 @@
import { Box } from './classes/box.js';
import { Scene } from './classes/scene.js';
import { debounce, delay } from "./util.js";
const rootElement = document.getElementById('debounce-test');
const rootBox = new Box('rootBox', rootElement).flex();
const scene = window.scene = new Scene('Debounce test', rootBox);
const log = () => scene.log("event");
debounce(log, 500);
debounce(log, 500);
await delay(500);
debounce(log, 500);
debounce(log, 500);

View File

@ -4,10 +4,7 @@ import { Post } from './classes/post.js';
import { Member } from './classes/member.js'; import { Member } from './classes/member.js';
import { ForumNode } from './classes/forum-node.js'; import { ForumNode } from './classes/forum-node.js';
import { ForumNetwork } from './classes/forum-network.js'; import { ForumNetwork } from './classes/forum-network.js';
import { delay } from './util.js';
const delay = async (ms) => {
await new Promise((resolve) => setTimeout(resolve, ms));
};
const rootElement = document.getElementById('forum-network'); const rootElement = document.getElementById('forum-network');
const rootBox = new Box('rootBox', rootElement).flex(); const rootBox = new Box('rootBox', rootElement).flex();
@ -23,10 +20,12 @@ window.forumNode1 = await new ForumNode('node1', window.scene).initialize(window
window.forumNode2 = await new ForumNode('node2', window.scene).initialize(window.forumNetwork); window.forumNode2 = await new ForumNode('node2', window.scene).initialize(window.forumNetwork);
window.forumNode3 = await new ForumNode('node3', window.scene).initialize(window.forumNetwork); window.forumNode3 = await new ForumNode('node3', window.scene).initialize(window.forumNetwork);
setInterval(async () => { const processInterval = setInterval(async () => {
await window.forumNode1.processNextMessage(); await window.forumNode1.processNextMessage();
await window.forumNode2.processNextMessage(); await window.forumNode2.processNextMessage();
await window.forumNode3.processNextMessage(); await window.forumNode3.processNextMessage();
await window.scene.renderSequenceDiagram();
}, 100); }, 100);
// const blockchain = new Blockchain(); // const blockchain = new Blockchain();
@ -38,3 +37,6 @@ await delay(1000);
await window.author1.submitPost(window.forumNode1, window.post1, 50); await window.author1.submitPost(window.forumNode1, window.post1, 50);
await delay(1000); await delay(1000);
await window.author2.submitPost(window.forumNode2, window.post2, 100); await window.author2.submitPost(window.forumNode2, window.post2, 100);
await delay(1000);
clearInterval(processInterval);

View File

@ -22,3 +22,6 @@
font-family: monospace; font-family: monospace;
font-size: 11pt; font-size: 11pt;
} }
svg {
width: 800px;
}

View File

@ -10,5 +10,6 @@
<li><a href="./graph-test.html">Graph test</a></li> <li><a href="./graph-test.html">Graph test</a></li>
<li><a href="./validation-pool-test.html">Validation Pool test</a></li> <li><a href="./validation-pool-test.html">Validation Pool test</a></li>
<li><a href="./mermaid-test.html">Mermaid test</a></li> <li><a href="./mermaid-test.html">Mermaid test</a></li>
<li><a href="./debounce-test.html">Debounce test</a></li>
</ul> </ul>
</body> </body>

View File

@ -2,7 +2,25 @@
<head> <head>
<title>Forum Network</title> <title>Forum Network</title>
<link type="text/css" rel="stylesheet" href="./index.css" /> <link type="text/css" rel="stylesheet" href="./index.css" />
<script type="module" defer>
// import mermaid from './mermaid.mjs';
import mermaid from 'https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.mjs';
mermaid.mermaidAPI.initialize({ startOnLoad: false });
// Example of using the API var
const element = document.querySelector('#graphDiv');
const insertSvg = function (svgCode, bindFunctions) {
element.innerHTML = svgCode;
};
const graphDefinition = 'graph TB\na-->b';
const graph = await mermaid.mermaidAPI.render('graphDiv', graphDefinition, insertSvg);
console.log("executed...");
console.log(graph);
const div = document.createElement('div');
div.innerHTML = graph;
document.body.append(div);
</script>
</head> </head>
<body> <body>
<div id="graphDiv"></div>
</body> </body>

View File

@ -0,0 +1,15 @@
const timeouts = new Map();
export const debounce = (fn, delay) => {
const key = fn.toString();
if (!timeouts.get(key)) {
timeouts.set(key, setTimeout(async () => {
timeouts.delete(key);
await fn();
}, delay));
}
};
export const delay = async (ms) => {
await new Promise((resolve) => setTimeout(resolve, ms));
};

View File

@ -2,28 +2,24 @@ import { Box } from './classes/box.js';
import { Scene } from './classes/scene.js'; import { Scene } from './classes/scene.js';
import { Member } from './classes/member.js'; import { Member } from './classes/member.js';
import { Bench } from './classes/bench.js'; import { Bench } from './classes/bench.js';
import { delay } from './util.js';
const delay = async (ms) => {
await new Promise((resolve) => setTimeout(resolve, ms));
};
const rootElement = document.getElementById('validation-pool'); const rootElement = document.getElementById('validation-pool');
const rootBox = new Box('rootBox', rootElement).flex(); const rootBox = new Box('rootBox', rootElement).flex();
const scene = window.scene = new Scene('Validation Pool test', rootBox).log('sequenceDiagram'); const scene = window.scene = new Scene('Validation Pool test', rootBox).log('sequenceDiagram');
const bench = window.bench = new Bench("Bench", scene);
const member1 = window.member1 = await new Member("Member1", scene).initialize(); const member1 = window.member1 = await new Member("Member1", scene).initialize();
const member2 = window.member2 = await new Member("Member2", scene).initialize(); const member2 = window.member2 = await new Member("Member2", scene).initialize();
const bench = window.bench = new Bench("Bench", scene);
const updateDisplayValues = () => { const updateDisplayValues = async () => {
member1.setValue('rep', bench.reputations.getTokens(member1.reputationPublicKey)); member1.setValue('rep', bench.reputations.getTokens(member1.reputationPublicKey));
member2.setValue('rep', bench.reputations.getTokens(member2.reputationPublicKey)); member2.setValue('rep', bench.reputations.getTokens(member2.reputationPublicKey));
bench.setValue('total rep', bench.getTotalReputation()); bench.setValue('total rep', bench.getTotalReputation());
bench.setValue('available rep', bench.getTotalAvailableReputation()); bench.setValue('available rep', bench.getTotalAvailableReputation());
bench.setValue('active rep', bench.getTotalActiveReputation()); bench.setValue('active rep', bench.getTotalActiveReputation());
bench.setValue('active available rep', bench.getTotalActiveAvailableReputation()); bench.setValue('active available rep', bench.getTotalActiveAvailableReputation());
await scene.renderSequenceDiagram();
}; };
updateDisplayValues(); updateDisplayValues();
@ -34,7 +30,7 @@ await delay(1000);
const pool = member1.initiateValidationPool(bench, {fee: 1, duration: 1000, tokenLossRatio: 1}); const pool = member1.initiateValidationPool(bench, {fee: 1, duration: 1000, tokenLossRatio: 1});
await member1.castVote(pool, true, 0, 0); await member1.castVote(pool, true, 0, 0);
await member1.revealIdentity(pool); // Vote passes await member1.revealIdentity(pool); // Vote passes
updateDisplayValues(); await updateDisplayValues();
await delay(1000); await delay(1000);
} }
@ -43,7 +39,7 @@ try {
const pool = member2.initiateValidationPool(bench, {fee: 1, duration: 1000, tokenLossRatio: 1}); const pool = member2.initiateValidationPool(bench, {fee: 1, duration: 1000, tokenLossRatio: 1});
await member2.castVote(pool, true, 0, 0); await member2.castVote(pool, true, 0, 0);
await member2.revealIdentity(pool); // Quorum not met! await member2.revealIdentity(pool); // Quorum not met!
updateDisplayValues(); await updateDisplayValues();
await delay(1000); await delay(1000);
} catch(e) { } catch(e) {
if (e.message.match(/Quorum is not met/)) { if (e.message.match(/Quorum is not met/)) {
@ -59,10 +55,12 @@ try {
const pool = member2.initiateValidationPool(bench, {fee: 1, duration: 1000, tokenLossRatio: 1}); const pool = member2.initiateValidationPool(bench, {fee: 1, duration: 1000, tokenLossRatio: 1});
await member1.castVote(pool, true, 0.5, 1); await member1.castVote(pool, true, 0.5, 1);
await member1.revealIdentity(pool); // Vote passes await member1.revealIdentity(pool); // Vote passes
updateDisplayValues(); await updateDisplayValues();
await delay(1000); await delay(1000);
} }
updateDisplayValues(); await updateDisplayValues();
scene.deactivateAll(); scene.deactivateAll();
await updateDisplayValues();