Skip to content

Commit

Permalink
[api][desktop]: Refactor command line
Browse files Browse the repository at this point in the history
  • Loading branch information
tiagohm committed Oct 15, 2024
1 parent f256f08 commit b1b7278
Show file tree
Hide file tree
Showing 79 changed files with 868 additions and 932 deletions.
5 changes: 5 additions & 0 deletions api/src/main/kotlin/nebulosa/api/image/ImageSolved.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,9 @@ data class ImageSolved(
solution.width.toArcmin, solution.height.toArcmin,
solution.radius.toDegrees,
)

companion object {

@JvmStatic val NO_SOLUTION = ImageSolved(PlateSolution.NO_SOLUTION)
}
}
2 changes: 1 addition & 1 deletion api/src/main/kotlin/nebulosa/api/inject/Inject.kt
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ fun servicesModule() = module {
single { CalibrationFrameService(get()) }
single { FramingService(get(), get()) }
single { ImageService(get(), get(), get(), get(), get(), get(), get(), get(), get()) }
single { PlateSolverService(get()) }
single { PlateSolverService(get(), get()) }
single { FlatWizardExecutor(get(), get(), get()) }
single { FlatWizardService(get(Named.capturesDir), get()) }
single { StarDetectionService() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ class PlateSolverController(

private fun start(ctx: Context) {
val path = ctx.queryParam("path").notNullOrBlank().path().exists()
val key = ctx.queryParam("key").notNullOrBlank()
val solver = ctx.bodyAsClass<PlateSolverRequest>().valid()
ctx.json(plateSolverService.solveImage(solver, path))
ctx.json(plateSolverService.start(solver, path, key))
}

@Suppress("UNUSED_PARAMETER")
private fun stop(ctx: Context) {
plateSolverService.stopSolver()
plateSolverService.stop(ctx.queryParam("key").notNullOrBlank())
}
}
36 changes: 25 additions & 11 deletions api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,45 @@ package nebulosa.api.platesolver

import nebulosa.api.image.ImageBucket
import nebulosa.api.image.ImageSolved
import nebulosa.util.concurrency.cancellation.CancellationToken
import nebulosa.platesolver.PlateSolution
import java.nio.file.Path
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.*

class PlateSolverService(
private val imageBucket: ImageBucket,
private val executor: ExecutorService,
) {

private val cancellationToken = AtomicReference<CancellationToken>()
private val tasks = ConcurrentHashMap<String, Future<PlateSolution>>(4)

fun start(request: PlateSolverRequest, path: Path, key: String): ImageSolved {
val calibration = try {
solve(request, path, key).get()
} catch (e: CancellationException) {
return ImageSolved.NO_SOLUTION
}

fun solveImage(request: PlateSolverRequest, path: Path): ImageSolved {
val calibration = solve(request, path)
imageBucket.put(path, calibration)

if (!calibration.solved) {
throw RuntimeException("no solution found!")
}

return ImageSolved(calibration)
}

@Synchronized
fun solve(request: PlateSolverRequest, path: Path) = CancellationToken().use {
cancellationToken.set(it)
fun solve(request: PlateSolverRequest, path: Path, key: String): Future<PlateSolution> {
val solver = request.get()
val radius = if (request.blind) 0.0 else request.radius
solver.solve(path, null, request.centerRA, request.centerDEC, radius, request.downsampleFactor, request.timeout, it)

val task = FutureTask { solver.solve(path, null, request.centerRA, request.centerDEC, radius, request.downsampleFactor, request.timeout) }

tasks[key] = task
executor.submit(task)
return task
}

fun stopSolver() {
cancellationToken.get()?.cancel()
fun stop(key: String) {
tasks.remove(key)?.cancel(true)
}
}
26 changes: 7 additions & 19 deletions api/src/main/kotlin/nebulosa/api/stacker/StackerController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,13 @@ import io.javalin.Javalin
import io.javalin.http.Context
import io.javalin.http.bodyAsClass
import nebulosa.api.core.Controller
import nebulosa.api.validators.exists
import nebulosa.api.validators.notNull
import nebulosa.api.validators.path
import nebulosa.api.validators.valid
import nebulosa.util.concurrency.cancellation.CancellationToken
import java.util.concurrent.atomic.AtomicReference
import nebulosa.api.validators.*

class StackerController(
override val app: Javalin,
private val stackerService: StackerService,
) : Controller {

private val cancellationToken = AtomicReference<CancellationToken>()

init {
app.put("stacker/start", ::start)
app.get("stacker/running", ::isRunning)
Expand All @@ -26,24 +19,19 @@ class StackerController(
}

private fun start(ctx: Context) {
val key = ctx.queryParam("key").notNullOrBlank()
val body = ctx.bodyAsClass<StackingRequest>().valid()

if (cancellationToken.compareAndSet(null, CancellationToken())) {
try {
stackerService.stack(body, cancellationToken.get())?.also(ctx::json)
} finally {
cancellationToken.getAndSet(null)?.unlistenAll()
}
}
stackerService.stack(body, key)?.also(ctx::json)
}

private fun isRunning(ctx: Context) {
ctx.json(cancellationToken.get() != null)
val key = ctx.queryParam("key").notNullOrBlank()
ctx.json(stackerService.isRunning(key))
}

@Suppress("UNUSED_PARAMETER")
private fun stop(ctx: Context) {
cancellationToken.get()?.cancel()
val key = ctx.queryParam("key").notNullOrBlank()
stackerService.stop(key)
}

private fun analyze(ctx: Context) {
Expand Down
61 changes: 32 additions & 29 deletions api/src/main/kotlin/nebulosa/api/stacker/StackerService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@ import nebulosa.fits.isFits
import nebulosa.image.format.ImageHdu
import nebulosa.stacker.AutoStacker
import nebulosa.stacker.AutoStackerListener
import nebulosa.util.concurrency.cancellation.CancellationToken
import nebulosa.xisf.isXisf
import nebulosa.xisf.xisf
import java.nio.file.Path
import java.util.concurrent.ConcurrentHashMap
import kotlin.io.path.exists
import kotlin.io.path.isDirectory
import kotlin.io.path.isRegularFile

class StackerService(private val messageService: MessageService?) {

@Synchronized
fun stack(request: StackingRequest, cancellationToken: CancellationToken = CancellationToken.NONE): Path? {
private val stackers = ConcurrentHashMap<String, AutoStacker>(1)

fun stack(request: StackingRequest, key: String): Path? {
require(request.outputDirectory != null && request.outputDirectory.exists() && request.outputDirectory.isDirectory())
require(!stackers.containsKey(key)) { "stacking is already in progress" }

val luminance = request.targets.filter { it.enabled && it.group == StackerGroupType.LUMINANCE }
val red = request.targets.filter { it.enabled && it.group == StackerGroupType.RED }
Expand All @@ -32,23 +34,20 @@ class StackerService(private val messageService: MessageService?) {
// Combined LRGB
return if (red.size + green.size + blue.size >= 1) {
val stacker = request.get()
stackers[key] = stacker
val autoStackerMessageHandler = AutoStackerMessageHandler(luminance, red, green, blue)

try {
stacker.registerAutoStackerListener(autoStackerMessageHandler)

val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE, cancellationToken)
val stackedRedPath = red.stack(request, stacker, name, StackerGroupType.RED, cancellationToken)
val stackedGreenPath = green.stack(request, stacker, name, StackerGroupType.GREEN, cancellationToken)
val stackedBluePath = blue.stack(request, stacker, name, StackerGroupType.BLUE, cancellationToken)
val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE)
val stackedRedPath = red.stack(request, stacker, name, StackerGroupType.RED)
val stackedGreenPath = green.stack(request, stacker, name, StackerGroupType.GREEN)
val stackedBluePath = blue.stack(request, stacker, name, StackerGroupType.BLUE)

if (cancellationToken.isCancelled) {
null
} else {
val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits")
stacker.combineLRGB(combinedPath, stackedLuminancePath, stackedRedPath, stackedGreenPath, stackedBluePath)
combinedPath
}
val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits")
stacker.combineLRGB(combinedPath, stackedLuminancePath, stackedRedPath, stackedGreenPath, stackedBluePath)
combinedPath
} finally {
messageService?.sendMessage(StackerEvent.IDLE)
stacker.unregisterAutoStackerListener(autoStackerMessageHandler)
Expand All @@ -57,17 +56,16 @@ class StackerService(private val messageService: MessageService?) {
// LRGB
else if (rgb.isNotEmpty()) {
val stacker = request.get()
stackers[key] = stacker
val autoStackerMessageHandler = AutoStackerMessageHandler(luminance, rgb = rgb)

try {
stacker.registerAutoStackerListener(autoStackerMessageHandler)

val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE, cancellationToken)
val stackedRGBPath = rgb.stack(request, stacker, name, StackerGroupType.RGB, cancellationToken)
val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE)
val stackedRGBPath = rgb.stack(request, stacker, name, StackerGroupType.RGB)

if (cancellationToken.isCancelled) {
null
} else if (stackedLuminancePath != null && stackedRGBPath != null) {
if (stackedLuminancePath != null && stackedRGBPath != null) {
val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits")
stacker.combineLuminance(combinedPath, stackedLuminancePath, stackedRGBPath, false)
combinedPath
Expand All @@ -82,17 +80,16 @@ class StackerService(private val messageService: MessageService?) {
// MONO
else if (mono.isNotEmpty() || luminance.isNotEmpty()) {
val stacker = request.get()
stackers[key] = stacker
val autoStackerMessageHandler = AutoStackerMessageHandler(luminance, mono = mono)

try {
stacker.registerAutoStackerListener(autoStackerMessageHandler)

val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE, cancellationToken)
val stackedMonoPath = mono.stack(request, stacker, name, StackerGroupType.MONO, cancellationToken)
val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE)
val stackedMonoPath = mono.stack(request, stacker, name, StackerGroupType.MONO)

if (cancellationToken.isCancelled) {
null
} else if (stackedLuminancePath != null && stackedMonoPath != null) {
if (stackedLuminancePath != null && stackedMonoPath != null) {
val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits")
stacker.combineLuminance(combinedPath, stackedLuminancePath, stackedMonoPath, true)
combinedPath
Expand All @@ -108,15 +105,21 @@ class StackerService(private val messageService: MessageService?) {
}
}

fun isRunning(key: String): Boolean {
return stackers.containsKey(key)
}

fun stop(key: String) {
stackers.remove(key)?.stop()
}

private fun List<StackingTarget>.stack(
request: StackingRequest, stacker: AutoStacker,
name: String, group: StackerGroupType, cancellationToken: CancellationToken,
name: String, group: StackerGroupType
): Path? {
return if (cancellationToken.isCancelled) {
null
} else if (size > 1) {
return if (size > 1) {
val outputPath = Path.of("${request.outputDirectory}", "$name.$group.fits")
if (stacker.stack(map { it.path!! }, outputPath, request.referencePath!!, cancellationToken)) outputPath else null
if (stacker.stack(map { it.path!! }, outputPath, request.referencePath!!)) outputPath else null
} else if (isNotEmpty()) {
val outputPath = Path.of("${request.outputDirectory}", "$name.$group.fits")
if (stacker.align(request.referencePath!!, this[0].path!!, outputPath)) outputPath else null
Expand Down
4 changes: 4 additions & 0 deletions desktop/src/app/atlas/atlas.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@
[max]="100"
class="w-full"
[step]="0.1"
[minFractionDigits]="1"
[showButtons]="true"
inputStyleClass="p-inputtext-sm border-0 w-full"
locale="en"
Expand Down Expand Up @@ -746,6 +747,7 @@
[max]="90"
class="w-full"
[step]="0.1"
[minFractionDigits]="1"
[maxFractionDigits]="1"
[showButtons]="true"
inputStyleClass="p-inputtext-sm border-0 w-full"
Expand Down Expand Up @@ -792,6 +794,7 @@
[max]="30"
class="w-full"
[step]="0.1"
[minFractionDigits]="1"
[maxFractionDigits]="1"
[showButtons]="true"
inputStyleClass="p-inputtext-sm border-0 w-full"
Expand All @@ -808,6 +811,7 @@
[max]="30"
class="w-full"
[step]="0.1"
[minFractionDigits]="1"
[maxFractionDigits]="1"
[showButtons]="true"
inputStyleClass="p-inputtext-sm border-0 w-full"
Expand Down
1 change: 1 addition & 0 deletions desktop/src/app/autofocus/autofocus.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@
[min]="0"
[max]="1"
[step]="0.1"
[minFractionDigits]="1"
(ngModelChange)="savePreference()"
locale="en"
spinnableNumber />
Expand Down
9 changes: 5 additions & 4 deletions desktop/src/app/image/image.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@
[min]="0"
[max]="100"
[step]="0.01"
[minFractionDigits]="1"
styleClass="p-inputtext-sm border-0 w-full"
[showButtons]="true"
[(ngModel)]="solver.request.pixelSize"
Expand Down Expand Up @@ -1169,7 +1170,7 @@
[min]="0"
[max]="99.99"
[step]="0.01"
[minFractionDigits]="0"
[minFractionDigits]="1"
[maxFractionDigits]="2"
[(ngModel)]="fov.selected.pixelSize.width"
(ngModelChange)="saveFOV()"
Expand All @@ -1186,7 +1187,7 @@
[min]="0"
[max]="99.99"
[step]="0.01"
[minFractionDigits]="0"
[minFractionDigits]="1"
[maxFractionDigits]="2"
[(ngModel)]="fov.selected.pixelSize.height"
(ngModelChange)="saveFOV()"
Expand All @@ -1203,7 +1204,7 @@
[min]="0.01"
[max]="5"
[step]="0.01"
[minFractionDigits]="0"
[minFractionDigits]="1"
[maxFractionDigits]="2"
[(ngModel)]="fov.selected.barlowReducer"
(ngModelChange)="saveFOV()"
Expand Down Expand Up @@ -1233,7 +1234,7 @@
[min]="0"
[max]="360"
[step]="0.01"
[minFractionDigits]="0"
[minFractionDigits]="1"
[maxFractionDigits]="2"
[(ngModel)]="fov.rotation"
(ngModelChange)="saveFOV(false)"
Expand Down
8 changes: 6 additions & 2 deletions desktop/src/app/image/image.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AfterViewInit, Component, ElementRef, HostListener, NgZone, OnDestroy,
import { ActivatedRoute } from '@angular/router'
import hotkeys from 'hotkeys-js'
import { NgxLegacyMoveableComponent, OnDrag, OnResize, OnRotate } from 'ngx-moveable'
import { nuid } from 'nuid'
import createPanZoom from 'panzoom'
import { basename, dirname, extname } from 'path'
import { ContextMenu } from 'primeng/contextmenu'
Expand Down Expand Up @@ -516,6 +517,8 @@ export class ImageComponent implements AfterViewInit, OnDestroy {
})

this.loadPreference()

this.solver.key = nuid.next()
}

async ngAfterViewInit() {
Expand Down Expand Up @@ -1125,9 +1128,10 @@ export class ImageComponent implements AfterViewInit, OnDestroy {
const request: PlateSolverRequest = {
...this.solver.request,
...this.preferenceService.settings.get().plateSolver[this.solver.request.type],
type: this.solver.request.type,
}

const solved = await this.api.solverStart(request, path)
const solved = await this.api.solverStart(request, path, this.solver.key)

this.updateImageSolved(solved)
} catch {
Expand All @@ -1143,7 +1147,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy {
}

protected solverStop() {
return this.api.solverStop()
return this.api.solverStop(this.solver.key)
}

private updateImageSolved(solved?: ImageSolved) {
Expand Down
Loading

0 comments on commit b1b7278

Please sign in to comment.