diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraController.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraController.kt index 86c25dc79..7e64d19d7 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraController.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraController.kt @@ -6,6 +6,7 @@ import nebulosa.indi.device.camera.Camera import nebulosa.indi.device.filterwheel.FilterWheel import nebulosa.indi.device.focuser.Focuser import nebulosa.indi.device.mount.Mount +import nebulosa.indi.device.rotator.Rotator import org.hibernate.validator.constraints.Range import org.springframework.web.bind.annotation.* @@ -39,8 +40,8 @@ class CameraController( @PutMapping("{camera}/snoop") fun snoop( camera: Camera, - mount: Mount?, wheel: FilterWheel?, focuser: Focuser?, - ) = cameraService.snoop(camera, mount, wheel, focuser) + mount: Mount?, wheel: FilterWheel?, focuser: Focuser?, rotator: Rotator? + ) = cameraService.snoop(camera, mount, wheel, focuser, rotator) @PutMapping("{camera}/cooler") fun cooler( diff --git a/desktop/src/app/camera/camera.component.ts b/desktop/src/app/camera/camera.component.ts index c4b775df8..0bf5944ec 100644 --- a/desktop/src/app/camera/camera.component.ts +++ b/desktop/src/app/camera/camera.component.ts @@ -10,9 +10,11 @@ import { BrowserWindowService } from '../../shared/services/browser-window.servi import { ElectronService } from '../../shared/services/electron.service' import { PreferenceService } from '../../shared/services/preference.service' import { Camera, CameraDialogInput, CameraDialogMode, CameraPreference, CameraStartCapture, EMPTY_CAMERA, EMPTY_CAMERA_START_CAPTURE, ExposureMode, ExposureTimeUnit, FrameType, updateCameraStartCaptureFromCamera } from '../../shared/types/camera.types' +import { Device } from '../../shared/types/device.types' import { Focuser } from '../../shared/types/focuser.types' import { Equipment } from '../../shared/types/home.types' import { Mount } from '../../shared/types/mount.types' +import { Rotator } from '../../shared/types/rotator.types' import { FilterWheel } from '../../shared/types/wheel.types' import { AppComponent } from '../app.component' @@ -103,6 +105,11 @@ export class CameraComponent implements AfterContentInit, OnDestroy { label: 'Focuser', menu: [], }, + { + icon: 'mdi mdi-rotate-right', + label: 'Rotator', + menu: [], + }, ] }, ] @@ -215,6 +222,14 @@ export class CameraComponent implements AfterContentInit, OnDestroy { } }) + electron.on('ROTATOR.UPDATED', event => { + if (event.device.id === this.equipment.rotator?.id) { + ngZone.run(() => { + Object.assign(this.equipment.rotator!, event.device) + }) + } + }) + electron.on('CALIBRATION.CHANGED', () => { ngZone.run(() => this.loadCalibrationGroups()) }) @@ -289,23 +304,21 @@ export class CameraComponent implements AfterContentInit, OnDestroy { } private async loadEquipment() { - const mounts = await this.api.mounts() - this.equipment.mount = mounts.find(e => e.name === this.equipment.mount?.name) - const buildStartTooltip = () => { this.startTooltip = `MOUNT: ${this.equipment.mount?.name ?? 'None'} FILTER WHEEL: ${this.equipment.wheel?.name ?? 'None'} - FOCUSER: ${this.equipment.focuser?.name ?? 'None'}` + FOCUSER: ${this.equipment.focuser?.name ?? 'None'} + ROTATOR: ${this.equipment.rotator?.name ?? 'None'}` } - const makeMountItem = (mount?: Mount) => { + const makeItem = (checked: boolean, command: () => void, device?: Device) => { return { - icon: mount ? 'mdi mdi-connection' : 'mdi mdi-close', - label: mount?.name ?? 'None', - checked: this.equipment.mount?.name === mount?.name, + icon: device ? 'mdi mdi-connection' : 'mdi mdi-close', + label: device?.name ?? 'None', + checked, command: async (event: SlideMenuItemCommandEvent) => { - this.equipment.mount = mount + command() buildStartTooltip() this.preference.equipmentForDevice(this.camera).set(this.equipment) event.parent?.menu?.forEach(item => item.checked = item === event.item) @@ -313,27 +326,28 @@ export class CameraComponent implements AfterContentInit, OnDestroy { } } + // MOUNT + + const mounts = await this.api.mounts() + this.equipment.mount = mounts.find(e => e.name === this.equipment.mount?.name) + + const makeMountItem = (mount?: Mount) => { + return makeItem(this.equipment.mount?.name === mount?.name, () => this.equipment.mount = mount, mount) + } + this.cameraModel[1].menu![0].menu!.push(makeMountItem()) for (const mount of mounts) { this.cameraModel[1].menu![0].menu!.push(makeMountItem(mount)) } + // FILTER WHEEL + const wheels = await this.api.wheels() this.equipment.wheel = wheels.find(e => e.name === this.equipment.wheel?.name) const makeWheelItem = (wheel?: FilterWheel) => { - return { - icon: wheel ? 'mdi mdi-connection' : 'mdi mdi-close', - label: wheel?.name ?? 'None', - checked: this.equipment.wheel?.name === wheel?.name, - command: async (event: SlideMenuItemCommandEvent) => { - this.equipment.wheel = wheel - buildStartTooltip() - this.preference.equipmentForDevice(this.camera).set(this.equipment) - event.parent?.menu?.forEach(item => item.checked = item === event.item) - }, - } + return makeItem(this.equipment.wheel?.name === wheel?.name, () => this.equipment.wheel = wheel, wheel) } this.cameraModel[1].menu![1].menu!.push(makeWheelItem()) @@ -342,21 +356,13 @@ export class CameraComponent implements AfterContentInit, OnDestroy { this.cameraModel[1].menu![1].menu!.push(makeWheelItem(wheel)) } + // FOCUSER + const focusers = await this.api.focusers() this.equipment.focuser = focusers.find(e => e.name === this.equipment.focuser?.name) const makeFocuserItem = (focuser?: Focuser) => { - return { - icon: focuser ? 'mdi mdi-connection' : 'mdi mdi-close', - label: focuser?.name ?? 'None', - checked: this.equipment.focuser?.name === focuser?.name, - command: async (event: SlideMenuItemCommandEvent) => { - this.equipment.focuser = focuser - buildStartTooltip() - this.preference.equipmentForDevice(this.camera).set(this.equipment) - event.parent?.menu?.forEach(item => item.checked = item === event.item) - }, - } + return makeItem(this.equipment.focuser?.name === focuser?.name, () => this.equipment.focuser = focuser, focuser) } this.cameraModel[1].menu![2].menu!.push(makeFocuserItem()) @@ -365,6 +371,21 @@ export class CameraComponent implements AfterContentInit, OnDestroy { this.cameraModel[1].menu![2].menu!.push(makeFocuserItem(focuser)) } + // ROTATOR + + const rotators = await this.api.rotators() + this.equipment.rotator = rotators.find(e => e.name === this.equipment.rotator?.name) + + const makeRotatorItem = (rotator?: Rotator) => { + return makeItem(this.equipment.rotator?.name === rotator?.name, () => this.equipment.rotator = rotator, rotator) + } + + this.cameraModel[1].menu![3].menu!.push(makeRotatorItem()) + + for (const rotator of rotators) { + this.cameraModel[1].menu![3].menu!.push(makeRotatorItem(rotator)) + } + buildStartTooltip() } diff --git a/desktop/src/app/rotator/rotator.component.ts b/desktop/src/app/rotator/rotator.component.ts index d0bd81edd..20843f897 100644 --- a/desktop/src/app/rotator/rotator.component.ts +++ b/desktop/src/app/rotator/rotator.component.ts @@ -109,7 +109,7 @@ export class RotatorComponent implements AfterViewInit, OnDestroy { } private update() { - if (!this.rotator.id) { + if (this.rotator.id) { this.moving = this.rotator.moving this.reversed = this.rotator.reversed } diff --git a/desktop/src/shared/services/api.service.ts b/desktop/src/shared/services/api.service.ts index 737c8bede..426feeabc 100644 --- a/desktop/src/shared/services/api.service.ts +++ b/desktop/src/shared/services/api.service.ts @@ -70,8 +70,8 @@ export class ApiService { // TODO: Rotator cameraSnoop(camera: Camera, equipment: Equipment) { - const { mount, wheel, focuser } = equipment - const query = this.http.query({ mount: mount?.name, wheel: wheel?.name, focuser: focuser?.name }) + const { mount, wheel, focuser, rotator } = equipment + const query = this.http.query({ mount: mount?.name, wheel: wheel?.name, focuser: focuser?.name, rotator: rotator?.name }) return this.http.put(`cameras/${camera.id}/snoop?${query}`) } diff --git a/desktop/src/shared/types/home.types.ts b/desktop/src/shared/types/home.types.ts index c8f4a28d3..fd22c33fc 100644 --- a/desktop/src/shared/types/home.types.ts +++ b/desktop/src/shared/types/home.types.ts @@ -1,6 +1,7 @@ import { Camera } from './camera.types' import { Focuser } from './focuser.types' import { Mount } from './mount.types' +import { Rotator } from './rotator.types' import { FilterWheel } from './wheel.types' export type HomeWindowType = 'CAMERA' | 'MOUNT' | 'GUIDER' | 'WHEEL' | 'FOCUSER' | 'DOME' | 'ROTATOR' | 'SWITCH' | @@ -45,4 +46,5 @@ export interface Equipment { mount?: Mount focuser?: Focuser wheel?: FilterWheel + rotator?: Rotator } diff --git a/nebulosa-alpaca-api/src/main/kotlin/nebulosa/alpaca/api/AlpacaRotatorService.kt b/nebulosa-alpaca-api/src/main/kotlin/nebulosa/alpaca/api/AlpacaRotatorService.kt new file mode 100644 index 000000000..da6167647 --- /dev/null +++ b/nebulosa-alpaca-api/src/main/kotlin/nebulosa/alpaca/api/AlpacaRotatorService.kt @@ -0,0 +1,48 @@ +package nebulosa.alpaca.api + +import retrofit2.Call +import retrofit2.http.* + +interface AlpacaRotatorService : AlpacaDeviceService { + + @GET("api/v1/rotator/{id}/connected") + override fun isConnected(@Path("id") id: Int): Call + + @FormUrlEncoded + @PUT("api/v1/rotator/{id}/connected") + override fun connect(@Path("id") id: Int, @Field("Connected") connected: Boolean): Call + + @GET("api/v1/rotator/{id}/canreverse") + fun canReverse(@Path("id") id: Int): Call + + @GET("api/v1/rotator/{id}/ismoving") + fun isMoving(@Path("id") id: Int): Call + + @GET("api/v1/rotator/{id}/reverse") + fun isReversed(@Path("id") id: Int): Call + + @GET("api/v1/rotator/{id}/position") + fun position(@Path("id") id: Int): Call + + @GET("api/v1/rotator/{id}/stepsize") + fun stepSize(@Path("id") id: Int): Call + + @FormUrlEncoded + @PUT("api/v1/rotator/{id}/reverse") + fun reverse(@Path("id") id: Int, @Field("Reverse") reverse: Boolean): Call + + @PUT("api/v1/rotator/{id}/halt") + fun halt(@Path("id") id: Int): Call + + @FormUrlEncoded + @PUT("api/v1/rotator/{id}/move") + fun move(@Path("id") id: Int, @Field("Position") position: Double): Call + + @FormUrlEncoded + @PUT("api/v1/rotator/{id}/moveabsolute") + fun moveTo(@Path("id") id: Int, @Field("Position") position: Double): Call + + @FormUrlEncoded + @PUT("api/v1/rotator/{id}/sync") + fun sync(@Path("id") id: Int, @Field("Position") position: Double): Call +} diff --git a/nebulosa-alpaca-api/src/main/kotlin/nebulosa/alpaca/api/AlpacaService.kt b/nebulosa-alpaca-api/src/main/kotlin/nebulosa/alpaca/api/AlpacaService.kt index 94c180c0a..16e9b6bd0 100644 --- a/nebulosa-alpaca-api/src/main/kotlin/nebulosa/alpaca/api/AlpacaService.kt +++ b/nebulosa-alpaca-api/src/main/kotlin/nebulosa/alpaca/api/AlpacaService.kt @@ -24,4 +24,6 @@ class AlpacaService( val filterWheel by lazy { retrofit.create() } val focuser by lazy { retrofit.create() } + + val rotator by lazy { retrofit.create() } } diff --git a/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/client/AlpacaClient.kt b/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/client/AlpacaClient.kt index 91a135312..89eaaf687 100644 --- a/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/client/AlpacaClient.kt +++ b/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/client/AlpacaClient.kt @@ -5,6 +5,7 @@ import nebulosa.alpaca.api.DeviceType import nebulosa.alpaca.indi.device.cameras.ASCOMCamera import nebulosa.alpaca.indi.device.focusers.ASCOMFocuser import nebulosa.alpaca.indi.device.mounts.ASCOMMount +import nebulosa.alpaca.indi.device.rotators.ASCOMRotator import nebulosa.alpaca.indi.device.wheels.ASCOMFilterWheel import nebulosa.indi.device.AbstractINDIDeviceProvider import nebulosa.indi.protocol.INDIProtocol @@ -21,7 +22,7 @@ data class AlpacaClient( override val id = UUID.randomUUID().toString() - override fun sendMessageToServer(message: INDIProtocol) {} + override fun sendMessageToServer(message: INDIProtocol) = Unit fun discovery() { val response = service.management.configuredDevices().execute() @@ -59,7 +60,13 @@ data class AlpacaClient( } } } - DeviceType.ROTATOR -> Unit + DeviceType.ROTATOR -> { + with(ASCOMRotator(device, service.rotator, this)) { + if (registerRotator(this)) { + initialize() + } + } + } DeviceType.DOME -> Unit DeviceType.SWITCH -> Unit DeviceType.COVER_CALIBRATOR -> Unit diff --git a/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/ASCOMDevice.kt b/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/ASCOMDevice.kt index 1eec61374..6a58bb809 100644 --- a/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/ASCOMDevice.kt +++ b/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/ASCOMDevice.kt @@ -7,6 +7,7 @@ import nebulosa.alpaca.indi.client.AlpacaClient import nebulosa.common.Resettable import nebulosa.common.time.Stopwatch import nebulosa.indi.device.* +import nebulosa.log.debug import nebulosa.log.loggerFor import retrofit2.Call import retrofit2.HttpException @@ -84,23 +85,29 @@ abstract class ASCOMDevice : Device, Resettable { protected fun > Call.doRequest(): T? { try { - val response = execute().body() + val request = request() + val response = execute() + val body = response.body() - return if (response == null) { - LOG.warn("response has no body. device={}, url={}", name, request().url) + return if (body == null) { + LOG.debug { "response has no body. device=%s, request=%s %s, response=%s".format(name, request.method, request.url, response) } null - } else if (response.errorNumber != 0) { - val message = response.errorMessage + } else if (body.errorNumber != 0) { + val message = body.errorMessage if (message.isNotEmpty()) { addMessageAndFireEvent("[%s]: %s".format(LocalDateTime.now(), message)) } - // LOG.warn("unsuccessful response. device={}, code={}, message={}", name, response.errorNumber, response.errorMessage) + LOG.debug { + "unsuccessful response. device=%s, request=%s %s, errorNumber=%s, message=%s".format( + name, request.method, request.url, body.errorNumber, body.errorMessage + ) + } null } else { - response + body } } catch (e: HttpException) { LOG.error("unexpected response. device=$name", e) diff --git a/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/mounts/ASCOMMount.kt b/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/mounts/ASCOMMount.kt index 90c54264b..4f7dce29e 100644 --- a/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/mounts/ASCOMMount.kt +++ b/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/mounts/ASCOMMount.kt @@ -224,9 +224,9 @@ data class ASCOMMount( service.utcDate(device.number, dateTime.toInstant()).doRequest() } - override fun snoop(devices: Iterable) {} + override fun snoop(devices: Iterable) = Unit - override fun handleMessage(message: INDIProtocol) {} + override fun handleMessage(message: INDIProtocol) = Unit override fun onConnected() { processCapabilities() @@ -237,7 +237,7 @@ data class ASCOMMount( equatorialSystem = service.equatorialSystem(device.number).doRequest()?.value ?: equatorialSystem } - override fun onDisconnected() {} + override fun onDisconnected() = Unit override fun reset() { super.reset() diff --git a/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/rotators/ASCOMRotator.kt b/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/rotators/ASCOMRotator.kt new file mode 100644 index 000000000..26e372fe6 --- /dev/null +++ b/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/rotators/ASCOMRotator.kt @@ -0,0 +1,112 @@ +package nebulosa.alpaca.indi.device.rotators + +import nebulosa.alpaca.api.AlpacaRotatorService +import nebulosa.alpaca.api.ConfiguredDevice +import nebulosa.alpaca.indi.client.AlpacaClient +import nebulosa.alpaca.indi.device.ASCOMDevice +import nebulosa.indi.device.Device +import nebulosa.indi.device.rotator.* +import nebulosa.indi.protocol.INDIProtocol + +@Suppress("RedundantModalityModifier") +data class ASCOMRotator( + override val device: ConfiguredDevice, + override val service: AlpacaRotatorService, + override val sender: AlpacaClient, +) : ASCOMDevice(), Rotator { + + @Volatile final override var angle = 0.0 + @Volatile final override var minAngle = 0.0 + @Volatile final override var maxAngle = 360.0 + @Volatile final override var moving = false + @Volatile final override var canAbort = true + @Volatile final override var canHome = false + @Volatile final override var canSync = true + @Volatile final override var canReverse = false + @Volatile final override var reversed = false + @Volatile final override var hasBacklashCompensation = false + @Volatile final override var backslash = 0 + + override val snoopedDevices = emptyList() + + override fun onConnected() { + processCapabilities() + processPosition() + } + + override fun onDisconnected() = Unit + + override fun refresh(elapsedTimeInSeconds: Long) { + super.refresh(elapsedTimeInSeconds) + + if (connected) { + processPosition() + processMoving() + processReversed() + } + } + + override fun snoop(devices: Iterable) = Unit + + override fun moveRotator(angle: Double) { + service.moveTo(device.number, angle).doRequest() + } + + override fun syncRotator(angle: Double) { + if (canSync) { + service.sync(device.number, angle).doRequest() + } + } + + override fun homeRotator() = Unit + + override fun reverseRotator(enable: Boolean) { + if (canReverse) { + service.reverse(device.number, enable).doRequest() + } + } + + override fun abortRotator() { + if (canAbort) { + service.halt(device.number).doRequest() + } + } + + override fun handleMessage(message: INDIProtocol) = Unit + + private fun processCapabilities() { + service.canReverse(device.number).doRequest { + if (it.value != canReverse) { + canReverse = it.value + sender.fireOnEventReceived(RotatorCanReverseChanged(this)) + } + } + } + + private fun processPosition() { + service.position(device.number).doRequest { + if (it.value != angle) { + angle = it.value + sender.fireOnEventReceived(RotatorAngleChanged(this)) + } + } + } + + private fun processMoving() { + service.isMoving(device.number).doRequest { + if (it.value != moving) { + moving = it.value + sender.fireOnEventReceived(RotatorMovingChanged(this)) + } + } + } + + private fun processReversed() { + service.isReversed(device.number).doRequest { + if (it.value != reversed) { + reversed = it.value + sender.fireOnEventReceived(RotatorReversedChanged(this)) + } + } + } +} diff --git a/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/wheels/ASCOMFilterWheel.kt b/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/wheels/ASCOMFilterWheel.kt index dc2035dde..d327dcc32 100644 --- a/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/wheels/ASCOMFilterWheel.kt +++ b/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/wheels/ASCOMFilterWheel.kt @@ -29,7 +29,7 @@ data class ASCOMFilterWheel( processNames() } - override fun onDisconnected() {} + override fun onDisconnected() = Unit override fun moveTo(position: Int) { if (position in 1..count && position != this.position) { @@ -55,9 +55,9 @@ data class ASCOMFilterWheel( sender.fireOnEventReceived(FilterWheelNamesChanged(this)) } - override fun snoop(devices: Iterable) {} + override fun snoop(devices: Iterable) = Unit - override fun handleMessage(message: INDIProtocol) {} + override fun handleMessage(message: INDIProtocol) = Unit private fun processPosition() { service.position(device.number).doRequest { diff --git a/nebulosa-guiding-internal/src/main/kotlin/nebulosa/guiding/internal/IdentityGuideAlgorithm.kt b/nebulosa-guiding-internal/src/main/kotlin/nebulosa/guiding/internal/IdentityGuideAlgorithm.kt index 61e7d8a0c..f7fae575e 100644 --- a/nebulosa-guiding-internal/src/main/kotlin/nebulosa/guiding/internal/IdentityGuideAlgorithm.kt +++ b/nebulosa-guiding-internal/src/main/kotlin/nebulosa/guiding/internal/IdentityGuideAlgorithm.kt @@ -6,5 +6,5 @@ class IdentityGuideAlgorithm(override val axis: GuideAxis) : GuideAlgorithm { override fun compute(input: Double) = input - override fun reset() {} + override fun reset() = Unit } diff --git a/nebulosa-guiding-internal/src/main/kotlin/nebulosa/guiding/internal/RandomDither.kt b/nebulosa-guiding-internal/src/main/kotlin/nebulosa/guiding/internal/RandomDither.kt index c24877604..e4fb17c55 100644 --- a/nebulosa-guiding-internal/src/main/kotlin/nebulosa/guiding/internal/RandomDither.kt +++ b/nebulosa-guiding-internal/src/main/kotlin/nebulosa/guiding/internal/RandomDither.kt @@ -10,7 +10,7 @@ class RandomDither(private val random: Random = Random.Default) : Dither { return doubleArrayOf(ra, dec) } - override fun reset() {} + override fun reset() = Unit companion object { diff --git a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/AbstractINDIDeviceProvider.kt b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/AbstractINDIDeviceProvider.kt index 53e55e8b8..235b7e947 100644 --- a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/AbstractINDIDeviceProvider.kt +++ b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/AbstractINDIDeviceProvider.kt @@ -198,35 +198,12 @@ abstract class AbstractINDIDeviceProvider : INDIDeviceProvider { } override fun close() { - cameras().forEach { - it.close() - unregisterCamera(it) - } - - mounts().forEach { - it.close() - unregisterMount(it) - } - - wheels().forEach { - it.close() - unregisterFilterWheel(it) - } - - focusers().forEach { - it.close() - unregisterFocuser(it) - } - - rotators().forEach { - it.close() - unregisterRotator(it) - } - - gps().forEach { - it.close() - unregisterGPS(it) - } + cameras().onEach(Device::close).onEach(::unregisterCamera) + mounts().onEach(Device::close).onEach(::unregisterMount) + wheels().onEach(Device::close).onEach(::unregisterFilterWheel) + focusers().onEach(Device::close).onEach(::unregisterFocuser) + rotators().onEach(Device::close).onEach(::unregisterRotator) + gps().onEach(Device::close).onEach(::unregisterGPS) cameras.clear() mounts.clear() diff --git a/nebulosa-skycatalog/src/main/kotlin/nebulosa/skycatalog/SkyCatalog.kt b/nebulosa-skycatalog/src/main/kotlin/nebulosa/skycatalog/SkyCatalog.kt index dc52ca6fc..66b6f0157 100644 --- a/nebulosa-skycatalog/src/main/kotlin/nebulosa/skycatalog/SkyCatalog.kt +++ b/nebulosa-skycatalog/src/main/kotlin/nebulosa/skycatalog/SkyCatalog.kt @@ -39,7 +39,7 @@ abstract class SkyCatalog(estimatedSize: Int = 0) : Collection return res } - protected fun notifyLoadFinished() {} + protected fun notifyLoadFinished() = Unit override val size get() = data.size