Skip to content

Commit

Permalink
[api]: Improve Siril command line
Browse files Browse the repository at this point in the history
  • Loading branch information
tiagohm committed Jun 12, 2024
1 parent 220a3f7 commit 4136c21
Show file tree
Hide file tree
Showing 21 changed files with 440 additions and 187 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package nebulosa.api.livestacker

import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import nebulosa.api.beans.converters.angle.DegreesDeserializer
import nebulosa.livestacker.LiveStacker
import nebulosa.pixinsight.livestacker.PixInsightLiveStacker
import nebulosa.pixinsight.script.PixInsightIsRunning
Expand All @@ -20,7 +18,6 @@ data class LiveStackingRequest(
@JvmField val dark: Path? = null,
@JvmField val flat: Path? = null,
@JvmField val bias: Path? = null,
@JvmField @field:JsonDeserialize(using = DegreesDeserializer::class) val rotate: Double = 0.0,
@JvmField val use32Bits: Boolean = false,
@JvmField val slot: Int = 1,
) : Supplier<LiveStacker> {
Expand All @@ -29,7 +26,7 @@ data class LiveStackingRequest(
val workingDirectory = Files.createTempDirectory("ls-")

return when (type) {
LiveStackerType.SIRIL -> SirilLiveStacker(executablePath!!, workingDirectory, dark, flat, rotate, use32Bits)
LiveStackerType.SIRIL -> SirilLiveStacker(executablePath!!, workingDirectory, dark, flat, use32Bits)
LiveStackerType.PIXINSIGHT -> {
val runner = PixInsightScriptRunner(executablePath!!)

Expand Down
28 changes: 0 additions & 28 deletions api/src/test/kotlin/SirilLiveStackerTest.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,18 @@ class CountUpDownLatch(initialCount: Int = 0) : Supplier<Boolean>, CancellationL
}

fun await(n: Int = 0) {
if (n >= 0) sync.acquireSharedInterruptibly(n)
require(n >= 0) { "n must be greater or equal to 0" }
sync.acquireSharedInterruptibly(n)
}

fun await(timeout: Long, unit: TimeUnit, n: Int = 0): Boolean {
return n >= 0 && sync.tryAcquireSharedNanos(n, unit.toNanos(timeout))
require(n >= 0) { "n must be greater or equal to 0" }
return sync.tryAcquireSharedNanos(n, unit.toNanos(timeout))
}

fun await(timeout: Duration, n: Int = 0): Boolean {
return n >= 0 && sync.tryAcquireSharedNanos(n, timeout.toNanos())
require(n >= 0) { "n must be greater or equal to 0" }
return sync.tryAcquireSharedNanos(n, timeout.toNanos())
}

override fun onCancel(source: CancellationSource) {
Expand Down
97 changes: 58 additions & 39 deletions nebulosa-common/src/main/kotlin/nebulosa/common/exec/CommandLine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package nebulosa.common.exec

import nebulosa.common.concurrency.cancel.CancellationListener
import nebulosa.common.concurrency.cancel.CancellationSource
import nebulosa.log.loggerFor
import java.io.InputStream
import java.io.OutputStream
import java.io.PrintStream
Expand All @@ -17,10 +18,10 @@ inline fun commandLine(action: CommandLine.Builder.() -> Unit): CommandLine {

data class CommandLine internal constructor(
private val builder: ProcessBuilder,
private val listeners: HashSet<LineReadListener>,
private val listeners: LinkedHashSet<CommandLineListener>,
) : CompletableFuture<Int>(), CancellationListener {

@Volatile private var process: Process? = null
@Volatile private lateinit var process: Process
@Volatile private var waiter: ProcessWaiter? = null
@Volatile private var inputReader: StreamLineReader? = null
@Volatile private var errorReader: StreamLineReader? = null
Expand All @@ -29,63 +30,65 @@ data class CommandLine internal constructor(
get() = builder.command()

val pid
get() = process?.pid() ?: -1L
get() = process.pid()

val exitCode
get() = process?.takeIf { !it.isAlive }?.exitValue() ?: -1
get() = process.takeIf { !it.isAlive }?.exitValue() ?: -1

val isRunning
get() = ::process.isInitialized && process.isAlive

val writer = PrintStream(object : OutputStream() {

override fun write(b: Int) {
process?.outputStream?.write(b)
process.outputStream?.write(b)
}

override fun write(b: ByteArray) {
process?.outputStream?.write(b)
process.outputStream?.write(b)
}

override fun write(b: ByteArray, off: Int, len: Int) {
process?.outputStream?.write(b, off, len)
process.outputStream?.write(b, off, len)
}

override fun flush() {
process?.outputStream?.flush()
process.outputStream?.flush()
}

override fun close() {
process?.outputStream?.close()
process.outputStream?.close()
}
}, true)

fun registerLineReadListener(listener: LineReadListener) {
listeners.add(listener)
fun registerCommandLineListener(listener: CommandLineListener) {
synchronized(listeners) { listeners.add(listener) }
}

fun unregisterLineReadListener(listener: LineReadListener) {
listeners.remove(listener)
fun unregisterCommandLineListener(listener: CommandLineListener) {
synchronized(listeners) { listeners.remove(listener) }
}

@Synchronized
fun start(timeout: Duration = Duration.ZERO): CommandLine {
if (process == null) {
process = try {
builder.start()
} catch (e: Throwable) {
completeExceptionally(e)
return this
}
require(!::process.isInitialized) { "process has already executed" }

process = try {
builder.start()
} catch (e: Throwable) {
completeExceptionally(e)
listeners.forEach { it.onExit(-1, e) }
return this
}

if (listeners.isNotEmpty()) {
inputReader = StreamLineReader(process!!.inputStream, false)
inputReader!!.start()
inputReader = StreamLineReader(process.inputStream, false)
inputReader!!.start()

errorReader = StreamLineReader(process!!.errorStream, true)
errorReader!!.start()
}
errorReader = StreamLineReader(process.errorStream, true)
errorReader!!.start()

waiter = ProcessWaiter(process!!, timeout.toMillis())
waiter!!.start()
}
waiter = ProcessWaiter(process, timeout.toMillis())
waiter!!.start()

return this
}
Expand All @@ -101,9 +104,8 @@ data class CommandLine internal constructor(
errorReader?.interrupt()
errorReader = null

process?.destroyForcibly()
process?.waitFor()
process = null
process.destroyForcibly()
process.waitFor()
}

fun get(timeout: Duration): Int {
Expand Down Expand Up @@ -140,7 +142,14 @@ data class CommandLine internal constructor(
process.waitFor()
}

complete(process.exitValue())
with(process.exitValue()) {
complete(this)
synchronized(listeners) { listeners.forEach { it.onExit(this, null) } }
}

waiter = null
inputReader = null
errorReader = null
}
}
}
Expand All @@ -161,10 +170,15 @@ data class CommandLine internal constructor(
try {
while (true) {
val line = reader.readLine() ?: break
if (isError) listeners.forEach { it.onErrorRead(line) }
else listeners.forEach { it.onInputRead(line) }

synchronized(listeners) {
listeners.forEach { it.onLineRead(line) }
}
}
} catch (ignored: Throwable) {
} catch (e: InterruptedException) {
LOG.error("command line interrupted")
} catch (e: Throwable) {
LOG.error("command line failed", e)
} finally {
completable.complete(Unit)
reader.close()
Expand All @@ -182,7 +196,7 @@ data class CommandLine internal constructor(
private val environment by lazy { builder.environment() }
private val arguments = mutableMapOf<String, Any?>()
private var executable = ""
private val listeners = HashSet<LineReadListener>(1)
private val listeners = LinkedHashSet<CommandLineListener>(1)

fun executablePath(path: Path) = executable("$path")

Expand All @@ -208,9 +222,9 @@ data class CommandLine internal constructor(

fun workingDirectory(path: Path): Unit = run { builder.directory(path.toFile()) }

fun registerLineReadListener(listener: LineReadListener) = listeners.add(listener)
fun registerCommandLineListener(listener: CommandLineListener) = listeners.add(listener)

fun unregisterLineReadListener(listener: LineReadListener) = listeners.remove(listener)
fun unregisterCommandLineListener(listener: CommandLineListener) = listeners.remove(listener)

override fun get(): CommandLine {
val args = ArrayList<String>(1 + arguments.size * 2)
Expand All @@ -229,4 +243,9 @@ data class CommandLine internal constructor(
return CommandLine(builder, listeners)
}
}

companion object {

@JvmStatic private val LOG = loggerFor<CommandLine>()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package nebulosa.common.exec

interface CommandLineListener {

fun onLineRead(line: String) = Unit

fun onExit(exitCode: Int, exception: Throwable?) = Unit

fun interface OnLineRead : CommandLineListener {

override fun onLineRead(line: String)
}

fun interface OnExit : CommandLineListener {

override fun onExit(exitCode: Int, exception: Throwable?)
}
}

This file was deleted.

8 changes: 4 additions & 4 deletions nebulosa-common/src/test/kotlin/CommandLineTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import io.kotest.matchers.ints.shouldBeExactly
import io.kotest.matchers.ints.shouldNotBeExactly
import io.kotest.matchers.longs.shouldBeGreaterThanOrEqual
import io.kotest.matchers.longs.shouldBeLessThan
import nebulosa.common.exec.LineReadListener
import nebulosa.common.exec.CommandLineListener
import nebulosa.common.exec.commandLine
import nebulosa.test.NonGitHubOnlyCondition
import java.nio.file.Path
Expand Down Expand Up @@ -51,17 +51,17 @@ class CommandLineTest : StringSpec() {
} shouldBeGreaterThanOrEqual 2000 shouldBeLessThan 10000
}
"ls" {
val lineReadListener = object : LineReadListener.OnInput, ArrayList<String>() {
val lineReadListener = object : CommandLineListener.OnLineRead, ArrayList<String>(64) {

override fun onInputRead(line: String) {
override fun onLineRead(line: String) {
add(line)
}
}

val cmd = commandLine {
executable("ls")
workingDirectory(Path.of("../"))
registerLineReadListener(lineReadListener)
registerCommandLineListener(lineReadListener)
}

cmd.start().get() shouldBeExactly 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ package nebulosa.pixinsight.script
import com.fasterxml.jackson.module.kotlin.jsonMapper
import com.fasterxml.jackson.module.kotlin.kotlinModule
import nebulosa.common.exec.CommandLine
import nebulosa.common.exec.LineReadListener
import nebulosa.common.exec.CommandLineListener
import nebulosa.common.json.PathDeserializer
import nebulosa.common.json.PathSerializer
import nebulosa.log.loggerFor
import org.apache.commons.codec.binary.Hex
import java.nio.file.Path
import java.util.concurrent.CompletableFuture

abstract class AbstractPixInsightScript<T> : PixInsightScript<T>, LineReadListener, CompletableFuture<T>() {
abstract class AbstractPixInsightScript<T> : PixInsightScript<T>, CommandLineListener, CompletableFuture<T>() {

override fun onInputRead(line: String) = Unit
override fun onLineRead(line: String) = Unit

override fun onErrorRead(line: String) = Unit
override fun onExit(exitCode: Int, exception: Throwable?) = Unit

protected open fun beforeRun() = Unit

Expand All @@ -36,11 +36,11 @@ abstract class AbstractPixInsightScript<T> : PixInsightScript<T>, LineReadListen
else if (exception != null) completeExceptionally(exception)
else complete(processOnComplete(exitCode).also { LOG.info("script processed. output={}", it) })
} finally {
commandLine.unregisterLineReadListener(this)
commandLine.unregisterCommandLineListener(this)
}
}

commandLine.registerLineReadListener(this)
commandLine.registerCommandLineListener(this)
beforeRun()
commandLine.start()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@ data class PixInsightIsRunning(private val slot: Int) : AbstractPixInsightScript
private val slotCrashed = "The requested application instance #$slot has crashed"
private val yieldedExecutionInstance = "$YIELDED_EXECUTION_INSTANCE$slot"

override fun onInputRead(line: String) {
processLine(line)
}

override fun onErrorRead(line: String) {
override fun onLineRead(line: String) {
processLine(line)
}

Expand Down
1 change: 1 addition & 0 deletions nebulosa-siril/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dependencies {
api(project(":nebulosa-math"))
api(project(":nebulosa-livestacker"))
implementation(project(":nebulosa-log"))
testImplementation(project(":nebulosa-test"))
}

publishing {
Expand Down
Loading

0 comments on commit 4136c21

Please sign in to comment.