This commit is contained in:
Moritz Ruth 2023-05-26 09:36:41 +02:00
parent 745a83d757
commit 379fc8eebb
Signed by: moritzruth
GPG key ID: C9BBAB79405EE56D
7 changed files with 160 additions and 1 deletions

View file

@ -15,6 +15,7 @@ allprojects {
kotlinOptions.jvmTarget = "19" kotlinOptions.jvmTarget = "19"
kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.ExperimentalUnsignedTypes" kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.ExperimentalUnsignedTypes"
kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.time.ExperimentalTime"
kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.contracts.ExperimentalContracts" kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.contracts.ExperimentalContracts"
kotlinOptions.freeCompilerArgs += "-Xcontext-receivers" kotlinOptions.freeCompilerArgs += "-Xcontext-receivers"
} }

View file

@ -28,4 +28,6 @@ object Washs {
val bar = StairvilleTlb(DmxAddress(121u)) val bar = StairvilleTlb(DmxAddress(121u))
val sideLight = StairvilleClb4(DmxAddress(126u)) val sideLight = StairvilleClb4(DmxAddress(126u))
val devices = persistentSetOf(*FrontLights.all.toTypedArray(), spotLeft, spotRight, *Tops.both.toTypedArray(), *Washs.both.toTypedArray(), bar) val fogMachine = FogMachine(DmxAddress(200u)) // TODO: Adresse korrigieren
val devices = persistentSetOf(*FrontLights.all.toTypedArray(), spotLeft, spotRight, *Tops.both.toTypedArray(), *Washs.both.toTypedArray(), bar, fogMachine)

View file

@ -0,0 +1,23 @@
package de.moritzruth.lampenfieber.device
import de.moritzruth.theaterdsl.device.Device
import de.moritzruth.theaterdsl.device.PercentageDV
import de.moritzruth.theaterdsl.dmx.DmxAddress
import de.moritzruth.theaterdsl.dmx.DmxDataWriter
import de.moritzruth.theaterdsl.dmx.DmxValue
import de.moritzruth.theaterdsl.value.percent
import kotlinx.collections.immutable.persistentSetOf
class FogMachine(override val firstChannel: DmxAddress) : Device {
override val numberOfChannels = 4u
override fun writeDmxData(writer: DmxDataWriter) {
writer.writePercentage(10.percent)
writer.writePercentage(power.getCurrentValue())
writer.writeRaw(DmxValue(0u)) // ignored
writer.writeRaw(DmxValue(0u)) // ignored
}
val power = PercentageDV()
override val dvs = persistentSetOf(power)
}

View file

@ -1,11 +1,14 @@
package de.moritzruth.theaterdsl.show package de.moritzruth.theaterdsl.show
import de.moritzruth.lampenfieber.device.fogMachine
import de.moritzruth.theaterdsl.device.Device import de.moritzruth.theaterdsl.device.Device
import de.moritzruth.theaterdsl.device.DynamicValue import de.moritzruth.theaterdsl.device.DynamicValue
import de.moritzruth.theaterdsl.dmx.EnttecOpenDmxUsb import de.moritzruth.theaterdsl.dmx.EnttecOpenDmxUsb
import de.moritzruth.theaterdsl.dmx.PerDeviceDmxDataWriter import de.moritzruth.theaterdsl.dmx.PerDeviceDmxDataWriter
import de.moritzruth.theaterdsl.util.InstantAsEpochMillisecondsSerializer import de.moritzruth.theaterdsl.util.InstantAsEpochMillisecondsSerializer
import de.moritzruth.theaterdsl.util.mapState import de.moritzruth.theaterdsl.util.mapState
import de.moritzruth.theaterdsl.value.Percentage
import de.moritzruth.theaterdsl.value.percent
import io.github.oshai.KLogger import io.github.oshai.KLogger
import io.github.oshai.KotlinLogging import io.github.oshai.KotlinLogging
import io.ktor.http.* import io.ktor.http.*
@ -32,13 +35,21 @@ import java.util.concurrent.atomic.AtomicInteger
import kotlin.math.roundToLong import kotlin.math.roundToLong
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
import kotlin.time.TimeSource
import kotlin.time.toJavaDuration import kotlin.time.toJavaDuration
data class FogState(val power: Percentage, val time: TimeSource.Monotonic.ValueTimeMark = TimeSource.Monotonic.markNow()) {
companion object {
fun off() = FogState(0.percent)
}
}
class ShowContext( class ShowContext(
val devices: ImmutableSet<Device>, val devices: ImmutableSet<Device>,
val show: Show, val show: Show,
val logger: KLogger, val logger: KLogger,
val stateFlow: MutableStateFlow<ShowState>, val stateFlow: MutableStateFlow<ShowState>,
val fogState: MutableStateFlow<FogState>,
val outputDataFreeze: AtomicInteger val outputDataFreeze: AtomicInteger
) { ) {
val stepFlow = stateFlow.mapState { val stepFlow = stateFlow.mapState {
@ -92,11 +103,13 @@ suspend fun runShow(show: Show, devices: ImmutableSet<Device>) = coroutineScope
show, show,
logger, logger,
stateFlow, stateFlow,
MutableStateFlow(FogState.off()),
AtomicInteger(0) AtomicInteger(0)
) )
startDataWriting(context) startDataWriting(context)
startStepRunning(context) startStepRunning(context)
startFogHandling(context)
startWebsocketServer(context) startWebsocketServer(context)
launch { launch {
@ -124,6 +137,15 @@ fun CoroutineScope.startDataWriting(context: ShowContext) = launch {
} }
} }
fun CoroutineScope.startFogHandling(context: ShowContext) = launch {
context.fogState.collectLatest { state ->
fogMachine.power.static(state.power)
delay(1000)
fogMachine.power.off()
}
}
fun CoroutineScope.startStepRunning(context: ShowContext) = launch { fun CoroutineScope.startStepRunning(context: ShowContext) = launch {
var lastPosition = ShowPosition.START var lastPosition = ShowPosition.START
var lastStepJob: Job? = null var lastStepJob: Job? = null
@ -197,6 +219,12 @@ private fun CoroutineScope.startWebsocketServer(context: ShowContext) = launch(D
call.respond(HttpStatusCode.NoContent) call.respond(HttpStatusCode.NoContent)
} }
post("/fog") {
val power = call.receive<Percentage>()
context.fogState.value = FogState(power)
call.respond(HttpStatusCode.NoContent)
}
get("/show") { get("/show") {
call.respond(context.show.acts.toMutableList()) call.respond(context.show.acts.toMutableList())
} }

View file

@ -1,5 +1,8 @@
package de.moritzruth.theaterdsl.value package de.moritzruth.theaterdsl.value
import kotlinx.serialization.Serializable
@Serializable
@JvmInline @JvmInline
value class Percentage(val value: Double) : Comparable<Percentage> { value class Percentage(val value: Double) : Comparable<Percentage> {
companion object { companion object {

View file

@ -0,0 +1,21 @@
<template>
<button
class="px-5 py-2 active:bg-green-800 transition duration-200 font-bold text-5"
:class="isActive ? 'bg-green-800' : 'bg-green-600'"
@click="e => emit('click', e)"
>
<slot/>
</button>
</template>
<style module>
</style>
<script setup lang="ts">
const props = defineProps<{
isActive?: boolean
}>()
const emit = defineEmits(["click"])
</script>

View file

@ -0,0 +1,81 @@
<template>
<div class="flex items-center gap-3">
<span>Nebel: </span>
<Button
v-for="p in buttonPowers"
@mousedown="onButtonActive(p[0])"
@mouseup="onButtonInactive(p[0])"
:is-active="isActive && p[0] === power"
>
{{ p[0] * 100 }}%
</Button>
</div>
</template>
<style module>
</style>
<script setup lang="ts">
import Button from "./Button.vue"
import { computed, ref, watch } from "vue"
import { onKeyDown, onKeyUp, useEventListener, useIntervalFn } from "@vueuse/core"
const activation = ref<"button" | "key" | null>(null)
const isActive = computed(() => activation.value !== null)
const power = ref(0)
function onButtonActive(p: number) {
activation.value = "button"
power.value = p
}
function onButtonInactive(p: number) {
if (activation.value === "button" && power.value === p) {
activation.value = null
power.value = 0
}
}
useEventListener(document.body, "mouseup", () => {
if (activation.value === "button") activation.value = null
})
const buttonPowers: Array<[number, string]> = [[0.1, "7"], [0.25, "8"], [0.5, "9"], [1, "0"]]
for (const p of buttonPowers) {
onKeyDown(p[1], () => {
activation.value = "key"
power.value = p[0]
})
onKeyUp(p[1], () => {
if (activation.value === "key" && power.value === p[0]) {
activation.value = null
power.value = 0
}
})
}
function send() {
fetch("/api/fog", {
method: "POST",
body: JSON.stringify(power.value),
headers: {
"Content-Type": "application/json"
}
})
}
const sendTimer = useIntervalFn(send, 200, { immediate: false })
watch(power, () => {
if (activation.value === null) {
sendTimer.pause()
send()
} else {
send()
sendTimer.resume()
}
})
</script>