Web client: Count election asynchronously in Web Worker

This commit is contained in:
RunasSudo 2021-06-04 15:01:53 +10:00
parent ba8d9bf79c
commit b7f18a74ac
No known key found for this signature in database
GPG Key ID: 7234E476BF21C61A
4 changed files with 302 additions and 259 deletions

View File

@ -24,6 +24,11 @@
<link rel="stylesheet" type="text/css" href="main.css?v=GITVERSION"> <link rel="stylesheet" type="text/css" href="main.css?v=GITVERSION">
</head> </head>
<body class="interactive"> <body class="interactive">
<div id="divLoading">
Loading&hellip;
</div>
<div id="divUI" style="display: none;">
<div class="menudiv"> <div class="menudiv">
<input type="file" id="bltFile"> <input type="file" id="bltFile">
<button onclick="clickCount()">Count</button> <button onclick="clickCount()">Count</button>
@ -39,8 +44,8 @@
</select> </select>
</label>--> </label>-->
<button id="btnAdvancedOptions" onclick="clickAdvancedOptions()">Show advanced options</button> <button id="btnAdvancedOptions" onclick="clickAdvancedOptions()">Show advanced options</button>
<!--GITREV--> OpenTally (revision <span id="spanRevNum"></span>)
<!--<a href="https://yingtongli.me/blog/2020/12/24/pyrcv2.html">Information and instructions</a> &middot; <!--&middot; <a href="https://yingtongli.me/blog/2020/12/24/pyrcv2.html">Information and instructions</a> &middot;
<a href="blt/">Ballot input/editor</a>--> <a href="blt/">Ballot input/editor</a>-->
</div> </div>
@ -239,10 +244,7 @@
</div> </div>
<div id="printWarning">Printing directly from this page is not supported. Use the Print result button to generate a printer-friendly report.</div> <div id="printWarning">Printing directly from this page is not supported. Use the Print result button to generate a printer-friendly report.</div>
</div>
<script>
var openTallyVersion = 'GITVERSION';
</script>
<script src="opentally.js?v=GITVERSION"></script> <script src="opentally.js?v=GITVERSION"></script>
<script src="index.js?v=GITVERSION"></script> <script src="index.js?v=GITVERSION"></script>

View File

@ -25,22 +25,40 @@ function clickAdvancedOptions() {
} }
} }
var wasm = wasm_bindgen;
var tblResult = document.getElementById('result'); var tblResult = document.getElementById('result');
var divLogs2 = document.getElementById('resultLogs2'); var divLogs2 = document.getElementById('resultLogs2');
var olStageComments; var olStageComments;
function updateResultTable(result) { var worker = new Worker('worker.js');
for (let i = 0; i < result.length; i++) {
tblResult.rows[i].insertAdjacentHTML('beforeend', result[i]); worker.onmessage = function(evt) {
} if (evt.data.type === 'init') {
document.getElementById('spanRevNum').innerText = evt.data.version;
document.getElementById('divLoading').style.display = 'none';
document.getElementById('divUI').style.display = 'block';
} else if (evt.data.type === 'initResultsTable') {
tblResult.innerHTML = evt.data.content;
divLogs2.innerHTML = '<p>Stage comments:</p>';
olStageComments = document.createElement('ol');
divLogs2.append(olStageComments);
} else if (evt.data.type === 'describeCount') {
document.getElementById('resultLogs1').innerHTML = evt.data.content;
} else if (evt.data.type === 'updateResultsTable') {
for (let i = 0; i < evt.data.result.length; i++) {
tblResult.rows[i].insertAdjacentHTML('beforeend', evt.data.result[i]);
} }
function updateStageComments(comment) { } else if (evt.data.type === 'updateStageComments') {
let elLi = document.createElement('li'); let elLi = document.createElement('li');
elLi.innerHTML = comment; elLi.innerHTML = evt.data.comment;
olStageComments.append(elLi); olStageComments.append(elLi);
} else if (evt.data.type === 'finalResultSummary') {
divLogs2.insertAdjacentHTML('beforeend', evt.data.summary);
}
} }
async function clickCount() { async function clickCount() {
@ -49,24 +67,14 @@ async function clickCount() {
} }
// Read BLT file // Read BLT file
let filePath = document.getElementById('bltFile').value;
filePath = filePath.substring(Math.max(filePath.lastIndexOf('\\'), filePath.lastIndexOf('/')) + 1);
let bltFile = document.getElementById('bltFile').files[0]; let bltFile = document.getElementById('bltFile').files[0];
let electionData = await bltFile.text(); let electionData = await bltFile.text();
// Load WASM
await wasm_bindgen('opentally_bg.wasm');
// Init election
let election = wasm.election_from_blt_Rational(electionData);
// Init results table
tblResult.innerHTML = wasm.init_results_table_Rational(election);
divLogs2.innerHTML = '<p>Stage comments:</p>';
olStageComments = document.createElement('ol');
divLogs2.append(olStageComments);
// Init STV options // Init STV options
let opts = wasm.STVOptions.new( let optsStr = [
document.getElementById('chkRoundTVs').checked ? parseInt(document.getElementById('txtRoundTVs').value) : null, document.getElementById('chkRoundTVs').checked ? parseInt(document.getElementById('txtRoundTVs').value) : null,
document.getElementById('chkRoundWeights').checked ? parseInt(document.getElementById('txtRoundWeights').value) : null, document.getElementById('chkRoundWeights').checked ? parseInt(document.getElementById('txtRoundWeights').value) : null,
document.getElementById('chkRoundVotes').checked ? parseInt(document.getElementById('txtRoundVotes').value) : null, document.getElementById('chkRoundVotes').checked ? parseInt(document.getElementById('txtRoundVotes').value) : null,
@ -78,29 +86,13 @@ async function clickCount() {
document.getElementById('selPapers').value == 'transferable', document.getElementById('selPapers').value == 'transferable',
document.getElementById('selExclusion').value, document.getElementById('selExclusion').value,
parseInt(document.getElementById('txtPPDP').value), parseInt(document.getElementById('txtPPDP').value),
); ];
// Describe count // Dispatch to worker
let filePath = document.getElementById('bltFile').value; worker.postMessage({
filePath = filePath.substring(Math.max(filePath.lastIndexOf('\\'), filePath.lastIndexOf('/')) + 1); 'type': 'countElection',
document.getElementById('resultLogs1').innerHTML = wasm.describe_count_Rational(filePath, election, opts); 'electionData': electionData,
'optsStr': optsStr,
// Step election 'filePath': filePath
let state = wasm.CountStateRational.new(election); });
wasm.count_init_Rational(state, opts);
updateResultTable(wasm.update_results_table_Rational(1, state, opts));
updateStageComments(wasm.update_stage_comments_Rational(state));
for (let stageNum = 2;; stageNum++) {
let isDone = wasm.count_one_stage_Rational(state, opts);
if (isDone) {
break;
}
updateResultTable(wasm.update_results_table_Rational(stageNum, state, opts));
updateStageComments(wasm.update_stage_comments_Rational(state));
}
updateResultTable(wasm.finalise_results_table_Rational(state));
divLogs2.insertAdjacentHTML('beforeend', wasm.final_result_summary_Rational(state));
} }

45
html/worker.js Normal file
View File

@ -0,0 +1,45 @@
importScripts('opentally.js');
var wasm = wasm_bindgen;
async function initWasm() {
await wasm_bindgen('opentally_bg.wasm');
postMessage({'type': 'init', 'version': wasm.version()});
}
initWasm();
onmessage = function(evt) {
if (evt.data.type === 'countElection') {
// Init election
let election = wasm.election_from_blt_Rational(evt.data.electionData);
// Init results table
postMessage({'type': 'initResultsTable', 'content': wasm.init_results_table_Rational(election)});
// Init STV options
let opts = wasm.STVOptions.new.apply(null, evt.data.optsStr);
// Describe count
postMessage({'type': 'describeCount', 'content': wasm.describe_count_Rational(evt.data.filePath, election, opts)});
// Step election
let state = wasm.CountStateRational.new(election);
wasm.count_init_Rational(state, opts);
postMessage({'type': 'updateResultsTable', 'result': wasm.update_results_table_Rational(1, state, opts)});
postMessage({'type': 'updateStageComments', 'comment': wasm.update_stage_comments_Rational(state)});
for (let stageNum = 2;; stageNum++) {
let isDone = wasm.count_one_stage_Rational(state, opts);
if (isDone) {
break;
}
postMessage({'type': 'updateResultsTable', 'result': wasm.update_results_table_Rational(stageNum, state, opts)});
postMessage({'type': 'updateStageComments', 'comment': wasm.update_stage_comments_Rational(state)});
}
postMessage({'type': 'updateResultsTable', 'result': wasm.finalise_results_table_Rational(state)});
postMessage({'type': 'finalResultSummary', 'summary': wasm.final_result_summary_Rational(state)});
}
}

View File

@ -21,5 +21,9 @@ pub mod numbers;
pub mod stv; pub mod stv;
use git_version::git_version; use git_version::git_version;
use wasm_bindgen::prelude::wasm_bindgen;
pub const VERSION: &str = git_version!(args=["--always", "--dirty=-dev"], fallback="unknown"); pub const VERSION: &str = git_version!(args=["--always", "--dirty=-dev"], fallback="unknown");
#[wasm_bindgen]
pub fn version() -> String { VERSION.to_string() }