Skip to content

Commit

Permalink
Merge 053ba23 into 3fbc78d
Browse files Browse the repository at this point in the history
  • Loading branch information
Gregor Billing committed Oct 4, 2020
2 parents 3fbc78d + 053ba23 commit c533166
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 37 deletions.
3 changes: 1 addition & 2 deletions tnoodle-ui/src/main/api/tnoodle.api.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ export const fetchZip = (

return scrambleClient
.loadScrambles(zipEndpoint, payload, wcif.id)
.then((result) => convertToBlob(result))
.catch((error) => console.error(error));
.then((result) => convertToBlob(result));
};

const convertToBlob = async (result) => {
Expand Down
20 changes: 17 additions & 3 deletions tnoodle-ui/src/main/api/tnoodle.socket.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export class ScrambleClient {

this.contentType = null;
this.resultPayload = null;

this.errorPayload = null;
}

loadScrambles(endpoint, payload, targetMarker) {
Expand All @@ -20,8 +22,8 @@ export class ScrambleClient {
ws.send(JSON.stringify(payload));
};

ws.onerror = (err) => {
reject(err);
ws.onerror = () => {
reject(this.errorPayload);
};

ws.onclose = (cls) => {
Expand All @@ -33,11 +35,20 @@ export class ScrambleClient {

resolve(resultObject);
} else {
reject(cls);
reject(this.errorPayload);
}
};

ws.onmessage = (msg) => {
if (this.state === MARKER_ERROR_MESSAGE) {
let rawPayload = msg.data.toString();
this.errorPayload = JSON.parse(rawPayload);
}

if (msg.data === MARKER_ERROR_MESSAGE) {
this.state = MARKER_ERROR_MESSAGE; // purposefully go into an error state
}

if (this.state === SCRAMBLING_STATES.INITIATE) {
this.state = SCRAMBLING_STATES.SCRAMBLING;

Expand Down Expand Up @@ -78,3 +89,6 @@ const SCRAMBLING_STATES = {
COMPUTED_DATA: "COMPUTED_DATA",
DONE: "DONE",
};

// this has to be identical with backend value in JobSchedulingHandler.kt
const MARKER_ERROR_MESSAGE = "%%ERROR%%";
20 changes: 14 additions & 6 deletions tnoodle-ui/src/main/components/Main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ const Main = connect(
this.state = {
competitionNameFileZip: "",
};

this.interceptor = React.createRef();
}
onSubmit = (evt) => {
evt.preventDefault();
Expand Down Expand Up @@ -77,11 +79,17 @@ const Main = connect(
this.props.mbld,
this.props.password,
this.props.translations
).then((blob) => {
this.props.updateFileZipBlob(blob);
this.props.updateGeneratingScrambles(false);
this.props.resetScramblingProgressCurrent();
});
)
.then((blob) => {
this.props.updateFileZipBlob(blob);
})
.catch((err) => {
this.interceptor.current.updateMessage(err);
})
.finally(() => {
this.props.updateGeneratingScrambles(false);
this.props.resetScramblingProgressCurrent();
});
this.props.updateGeneratingScrambles(true);
};

Expand Down Expand Up @@ -160,7 +168,7 @@ const Main = connect(
return (
<form onSubmit={this.onSubmit}>
<div className="sticky-top bg-light">
<Interceptor />
<Interceptor ref={this.interceptor} />
<VersionInfo />
<div className="container-fluid pt-2">
<div className="row">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ class WcifHandler(val environmentConfig: ServerEnvironmentConfig) : RouteHandler
}

override suspend fun ScramblingJobData.compute(statusBackend: StatusBackend): Pair<ContentType, ByteArray> {
val wcif = WCIFScrambleMatcher.fillScrambleSetsAsync(request.extendedWcif) { pzl, _ ->
statusBackend.onProgress(pzl.key)
val wcif = WCIFScrambleMatcher.fillScrambleSetsAsync(request.extendedWcif) { evt, _ ->
statusBackend.onProgress(evt.key)
}

return scrambledToResult(this, wcif, statusBackend)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import io.ktor.application.ApplicationCall
import io.ktor.application.call
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.http.cio.websocket.Frame
import io.ktor.http.cio.websocket.close
import io.ktor.http.cio.websocket.send
import io.ktor.http.cio.websocket.*
import io.ktor.response.respond
import io.ktor.response.respondBytes
import io.ktor.routing.*
Expand All @@ -16,6 +14,8 @@ import kotlinx.serialization.builtins.serializer
import org.worldcubeassociation.tnoodle.server.RouteHandler
import org.worldcubeassociation.tnoodle.server.crypto.StringEncryption.encodeBase64
import org.worldcubeassociation.tnoodle.server.serial.JsonConfig
import org.worldcubeassociation.tnoodle.server.webscrambles.serial.FrontendErrorMessage
import org.worldcubeassociation.tnoodle.server.webscrambles.serial.FrontendErrorMessage.Companion.asFrontendError

object JobSchedulingHandler : RouteHandler {
private val JOBS = mutableMapOf<Int, MutableMap<String, Int>>()
Expand All @@ -24,6 +24,8 @@ object JobSchedulingHandler : RouteHandler {

private const val JOB_ID_PARAM = "jobId"

const val MARKER_ERROR_MESSAGE = "%%ERROR%%";

private suspend fun ApplicationCall.checkAndYieldJobId(): Int? {
val jobId = parameters[JOB_ID_PARAM]?.toIntOrNull() ?: -1

Expand Down Expand Up @@ -106,24 +108,34 @@ object JobSchedulingHandler : RouteHandler {
webSocket {
for (frame in incoming) {
if (frame is Frame.Text) {
val request = job.extractFrame(call, frame)
try {
val request = job.extractFrame(call, frame)

val target = job.getTargetState(request)
val targetEnc = JsonConfig.SERIALIZER.encodeToString(MapSerializer(String.serializer(), Int.serializer()), target)
send(targetEnc)

val (type, data) = job.channel(request, this)

val target = job.getTargetState(request)
val targetEnc = JsonConfig.SERIALIZER.encodeToString(MapSerializer(String.serializer(), Int.serializer()), target)
send(targetEnc)
val targetMarker = job.getResultMarker(request)
val encodedData = data.encodeBase64()

val (type, data) = job.channel(request, this)
// signal that the computation result is about to start
send(targetMarker)

val targetMarker = job.getResultMarker(request)
val encodedData = data.encodeBase64()
send(type.toString())
send(encodedData)

// signal that the computation result is about to start
send(targetMarker)
close()
} catch (e: Throwable) {
val errorModel = e.asFrontendError()
val errorSerial = JsonConfig.SERIALIZER.encodeToString(FrontendErrorMessage.serializer(), errorModel)

send(type.toString())
send(encodedData)
send(MARKER_ERROR_MESSAGE)
send(errorSerial)

close()
close(CloseReason(CloseReason.Codes.INTERNAL_ERROR, ""))
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import kotlinx.coroutines.runBlocking
import org.worldcubeassociation.tnoodle.server.crypto.StringEncryption
import org.worldcubeassociation.tnoodle.server.crypto.SymmetricCipher
import org.worldcubeassociation.tnoodle.server.model.EventData
import org.worldcubeassociation.tnoodle.server.model.PuzzleData
import org.worldcubeassociation.tnoodle.server.webscrambles.exceptions.ScrambleMatchingException
import org.worldcubeassociation.tnoodle.server.webscrambles.wcif.model.*
import org.worldcubeassociation.tnoodle.server.webscrambles.wcif.model.extension.ExtensionBuilder
Expand Down Expand Up @@ -61,7 +60,7 @@ object WCIFScrambleMatcher {

// SCRAMBLE SET FILLING -----

suspend fun fillScrambleSetsAsync(wcif: Competition, onUpdate: (PuzzleData, String) -> Unit): Competition {
suspend fun fillScrambleSetsAsync(wcif: Competition, onUpdate: (EventData, String) -> Unit): Competition {
val scrambledEvents = wcif.events.map { e ->
val scrambledRounds = coroutineScope {
e.rounds.map { r -> async { scrambleRound(r, onUpdate) } }.awaitAll()
Expand All @@ -79,35 +78,36 @@ object WCIFScrambleMatcher {
// helper fn for synchronously running tests
fun fillScrambleSets(wcif: Competition) = runBlocking { fillScrambleSetsAsync(wcif) { _, _ -> Unit } }

private suspend fun scrambleRound(round: Round, onUpdate: (PuzzleData, String) -> Unit): Round {
private suspend fun scrambleRound(round: Round, onUpdate: (EventData, String) -> Unit): Round {
val scrambles = coroutineScope {
List(round.scrambleSetCount) { async { generateScrambleSet(round, onUpdate) } }.awaitAll()
}

return round.copy(scrambleSets = scrambles)
}

private fun generateScrambleSet(round: Round, onUpdate: (PuzzleData, String) -> Unit): ScrambleSet {
val puzzle = round.idCode.eventModel?.scrambler
private fun generateScrambleSet(round: Round, onUpdate: (EventData, String) -> Unit): ScrambleSet {
val eventModel = round.idCode.eventModel
?: ScrambleMatchingException.error("Unable to load scrambler for Round ${round.idCode}")

val standardScrambleNum = standardScrambleCountPerSet(round)
val puzzle = eventModel.scrambler

val scrambles = if (round.idCode.eventModel == EventData.THREE_MULTI_BLD) {
val scrambles = if (eventModel == EventData.THREE_MULTI_BLD) {
val countPerAttempt = standardScrambleNum / round.expectedAttemptNum

List(round.expectedAttemptNum) { _ ->
val scrambles = puzzle.generateEfficientScrambles(countPerAttempt) { onUpdate(puzzle, it) }
val scrambles = puzzle.generateEfficientScrambles(countPerAttempt) { onUpdate(eventModel, it) }
.joinToString(Scramble.WCIF_NEWLINE_CHAR)

Scramble(scrambles)
}
} else {
puzzle.generateEfficientScrambles(standardScrambleNum) { onUpdate(puzzle, it) }.map(::Scramble)
puzzle.generateEfficientScrambles(standardScrambleNum) { onUpdate(eventModel, it) }.map(::Scramble)
}

val extraScrambleNum = extraScrambleCountPerSet(round)
val extraScrambles = puzzle.generateEfficientScrambles(extraScrambleNum) { onUpdate(puzzle, it) }.map(::Scramble)
val extraScrambles = puzzle.generateEfficientScrambles(extraScrambleNum) { onUpdate(eventModel, it) }.map(::Scramble)

// dummy ID -- indexing happens afterwards
return ScrambleSet(ID_PENDING, scrambles, extraScrambles)
Expand Down

0 comments on commit c533166

Please sign in to comment.