Skip to content

Commit

Permalink
Merge pull request #68 from team5499/dev/onetimeInlineCallbacks
Browse files Browse the repository at this point in the history
Add onetime callbacks
  • Loading branch information
andycate authored Mar 1, 2019
2 parents b5d4057 + 2404620 commit e6401c7
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 19 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ dependencies {
}

group = 'org.team5499'
version = '0.8.0' /* Change this when deploying a new version */
version = '0.9.0' /* Change this when deploying a new version */

task sourcesJar(type: Jar) {
from sourceSets.main.allJava
Expand Down
94 changes: 78 additions & 16 deletions src/main/kotlin/org/team5499/dashboard/Dashboard.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ typealias VariableCallback = (String, Any?) -> Unit
*
* Handles starting the server
*/
@SuppressWarnings("ReturnCount", "TooManyFunctions")
@SuppressWarnings("ReturnCount", "TooManyFunctions", "LargeClass")
object Dashboard {
private var config: Config = Config()
public val concurrentCallbacks: ConcurrentHashMap<String, MutableList<VariableCallback>> = ConcurrentHashMap()
public var inlineCallbacks: List<String> = listOf()
public var inlineCallbackUpdates: MutableList<String> = mutableListOf()
public var inlineCallbackLambdas: ConcurrentHashMap<String, MutableList<VariableCallback>> = ConcurrentHashMap()
public var inlineLock = ReentrantLock()
public var variableLock = ReentrantLock()
public var variables: JSONObject = JSONObject()
get() {
synchronized(field) {
Expand Down Expand Up @@ -124,7 +126,6 @@ object Dashboard {
request: Request, response: Response ->
val pagename: String = request.queryParams("pagename")
val pagetitle: String = request.queryParams("pagetitle")
println("Attempting to create page with name: $pagename")
if (config.hasPageWithName(pagename)) {
response.redirect("/utils/newpage?pageexists=true")
} else {
Expand Down Expand Up @@ -156,7 +157,12 @@ object Dashboard {
* @param value The new value for the variable
*/
fun setVariable(key: String, value: Any) {
variableUpdates.put(key, value)
variableLock.lock()
try {
variableUpdates.put(key, value)
} finally {
variableLock.unlock()
}
}

/**
Expand All @@ -166,12 +172,17 @@ object Dashboard {
* @return The value of the specified variable
*/
fun <T> getVariable(key: String): T {
if ((!variables.has(key)) && (!variableUpdates.has(key))) {
throw DashboardException("The variable with name " + key + " was not found.")
} else if (variableUpdates.has(key)) {
return variableUpdates.get(key) as T
} else {
return variables.get(key) as T
variableLock.lock()
try {
if ((!variables.has(key)) && (!variableUpdates.has(key))) {
throw DashboardException("The variable with name " + key + " was not found.")
} else if (variableUpdates.has(key)) {
return variableUpdates.get(key) as T
} else {
return variables.get(key) as T
}
} finally {
variableLock.unlock()
}
}

Expand Down Expand Up @@ -247,7 +258,7 @@ object Dashboard {
* @param callback The lambda to call when the specified variable is updated
* @return The ID of the listener, which can be used later to remove the listener (See [removeVarListener])
*/
fun addVarListener(key: String, callback: (String, Any?) -> Unit): Int {
fun addVarListener(key: String, callback: VariableCallback): Int {
if (concurrentCallbacks.containsKey(key)) {
if (!concurrentCallbacks.get(key)!!.contains(callback)) {
val tmp = concurrentCallbacks.get(key)
Expand All @@ -271,7 +282,7 @@ object Dashboard {
*
* @return Whether the lambda was called or not
*/
fun runIfUpdate(key: String, callback: (String, Any?) -> Unit): Boolean {
fun runIfUpdate(key: String, callback: VariableCallback): Boolean {
var shouldUpdate = false
if (inlineCallbacks.contains(key)) {
shouldUpdate = true
Expand All @@ -284,6 +295,38 @@ object Dashboard {
return shouldUpdate
}

/**
* Add a lambda to run if the specified variable has been updated. Gets called when the update function is called
*
* @param key The variable to listen for
* @param callback The lambda to call if the variable is updated
*/
fun addInlineListener(key: String, callback: VariableCallback): Int {
var tmpList = mutableListOf<VariableCallback>()
if (inlineCallbackLambdas.containsKey(key)) {
tmpList = inlineCallbackLambdas.get(key)!!
}
tmpList.add(callback)
inlineCallbackLambdas.put(key, tmpList)
return tmpList.size - 1
}

/**
* Remove an inline listener with the specified key and ID
*
* @param key The variable that the lambda is attached to
* @param id The callback id returned by [addInlineListener]
*/
fun removeInlineListener(key: String, id: Int): Boolean {
if (inlineCallbackLambdas.containsKey(key)) {
val tmpList = inlineCallbackLambdas.get(key)!!
tmpList.removeAt(id)
inlineCallbackLambdas.put(key, tmpList)
return true
}
return false
}

/**
* Should be called if [inline callbacks][runIfUpdate] are used.
* Put this function at the beginning of `robotPeriodic`.
Expand All @@ -296,6 +339,15 @@ object Dashboard {
} finally {
inlineLock.unlock()
}
inlineCallbacks.forEach({
if (inlineCallbackLambdas.containsKey(it)) {
val tmpList = inlineCallbackLambdas.get(it)!!
val key = it
tmpList.forEach({
it(key, Dashboard.getVariable(key))
})
}
})
}

/**
Expand All @@ -318,15 +370,25 @@ object Dashboard {
}

fun mergeVariableUpdates() {
for (u in variableUpdates.keys()) {
variables.put(u, variableUpdates.get(u))
variableLock.lock()
try {
for (u in variableUpdates.keys()) {
variables.put(u, variableUpdates.get(u))
}
variableUpdates = JSONObject()
} finally {
variableLock.unlock()
}
variableUpdates = JSONObject()
}

fun mergeVariableUpdates(json: JSONObject) {
for (u in json.keys()) {
variables.put(u, json.get(u))
variableLock.lock()
try {
for (u in json.keys()) {
variables.put(u, json.get(u))
}
} finally {
variableLock.unlock()
}
}

Expand Down
11 changes: 9 additions & 2 deletions src/main/kotlin/org/team5499/dashboard/SocketHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,15 @@ class SocketHandler {
sendJSON(session, Dashboard.variables, "variables")
}
public fun broadcastUpdates() {
if (Dashboard.variableUpdates.keySet().size > 0) {
broadcastJSON(Dashboard.variableUpdates, "updates")
var updates = JSONObject()
Dashboard.variableLock.lock()
try {
updates = Dashboard.variableUpdates
} finally {
Dashboard.variableLock.unlock()
}
if (updates.keySet().size > 0) {
broadcastJSON(updates, "updates")
Dashboard.mergeVariableUpdates()
}
}
Expand Down
45 changes: 45 additions & 0 deletions src/test/kotlin/tests/WebdriverTest.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tests

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.fail
import org.junit.jupiter.api.RepeatedTest
import org.junit.jupiter.api.RepetitionInfo
import org.junit.jupiter.api.Tag
Expand Down Expand Up @@ -142,6 +143,50 @@ class WebdriverTest {
assert(callCount == 1)
}

@RepeatedTest(10)
fun onetimeInlineCallbackTest(repInfo: RepetitionInfo) {
if (repInfo.currentRepetition == 1) {
// First repetition - setup
Dashboard.update()
driver.get("localhost:5800/page/widgettest")
Thread.sleep(1000)
}
Dashboard.setVariable("TEST", "onetimebefore${repInfo.currentRepetition}")
Thread.sleep(100)
val widgets = driver.findElements(By.className("card-body"))
val input = widgets.get(0).findElement(By.className("form-control"))
val submit = widgets.get(0).findElement(By.className("btn"))
var callCount = 0
val pollThread = Thread({
actions.doubleClick(input).perform()
input.sendKeys("onetimeafter${repInfo.currentRepetition}")
submit.click()
})
val callbackId = Dashboard.addInlineListener("TEST") {
key: String, value: Any? ->
println(key)
println(value)
assert(value == "onetimeafter${repInfo.currentRepetition}")
assert(key == "TEST")
callCount++
println(callCount)
}
pollThread.start()
val startTime = System.currentTimeMillis()
while (1000 > System.currentTimeMillis() - startTime) {
try {
Dashboard.update()
} catch (e: AssertionError) {
Dashboard.removeInlineListener("TEST", callbackId)
fail(e)
}
}
pollThread.interrupt()
Dashboard.removeInlineListener("TEST", callbackId)
println(callCount)
assert(callCount == 1)
}

@AfterEach
fun cleanup() {
}
Expand Down

0 comments on commit e6401c7

Please sign in to comment.