diff --git a/src/api/autogen/AlpacaDeviceAPI_v1.yaml b/src/api/autogen/AlpacaDeviceAPI_v1.yaml index 4c00e5c..a0493e4 100644 --- a/src/api/autogen/AlpacaDeviceAPI_v1.yaml +++ b/src/api/autogen/AlpacaDeviceAPI_v1.yaml @@ -2033,7 +2033,7 @@ paths: get: summary: Returns the state of the calibration device description: >- - Returns the state of the calibration device, if present, otherwise returns "NotPresent". + Returns the state of the calibration device, if present, otherwise returns "NotPresent". The calibrator state mode is specified as an integer value from the CalibratorStatus Enum. parameters: @@ -2044,7 +2044,7 @@ paths: - CoverCalibrator Specific Methods responses: '200': - $ref: '#/components/responses/CalibratorStateResponse' + $ref: '#/components/responses/CalibratorStatusResponse' '400': $ref: '#/components/responses/400' '500': @@ -2053,7 +2053,7 @@ paths: get: summary: Returns the state of the device cover" description: >- - Returns the state of the device cover, if present, otherwise returns "NotPresent". + Returns the state of the device cover, if present, otherwise returns "NotPresent". The cover state mode is specified as an integer value from the CoverStatus Enum. parameters: @@ -2064,7 +2064,7 @@ paths: - CoverCalibrator Specific Methods responses: '200': - $ref: '#/components/responses/CoverStateResponse' + $ref: '#/components/responses/CoverStatusResponse' '400': $ref: '#/components/responses/400' '500': @@ -2415,7 +2415,7 @@ paths: - Dome Specific Methods responses: '200': - $ref: '#/components/responses/ShutterStatusResponse' + $ref: '#/components/responses/ShutterStateResponse' '400': $ref: '#/components/responses/400' '500': @@ -4472,7 +4472,7 @@ paths: - Telescope Specific Methods responses: '200': - $ref: '#/components/responses/IntResponse' + $ref: '#/components/responses/EquatorialCoordinateTypeResponse' '400': $ref: '#/components/responses/400' '500': @@ -5246,8 +5246,7 @@ paths: given coordinates. description: >- Predicts the pointing state that a German equatorial mount will be in if - it slews to the given coordinates. The return value will be one of - 0 - = pierEast, 1 = pierWest, -1 = pierUnknown + it slews to the given coordinates. parameters: - $ref: '#/components/parameters/device_number' - $ref: '#/components/parameters/RightAscensionQuery' @@ -5956,7 +5955,7 @@ components: - properties: Value: $ref: '#/components/schemas/SensorType' - ShutterStatusResponse: + ShutterStateResponse: description: Transaction complete or exception. content: application/json: @@ -5965,8 +5964,8 @@ components: - $ref: '#/components/schemas/AlpacaResponse' - properties: Value: - $ref: '#/components/schemas/ShutterStatus' - CalibratorStateResponse: + $ref: '#/components/schemas/ShutterState' + CalibratorStatusResponse: description: Transaction complete or exception. content: application/json: @@ -5975,8 +5974,8 @@ components: - $ref: '#/components/schemas/AlpacaResponse' - properties: Value: - $ref: '#/components/schemas/CalibratorState' - CoverStateResponse: + $ref: '#/components/schemas/CalibratorStatus' + CoverStatusResponse: description: Transaction complete or exception. content: application/json: @@ -5985,7 +5984,7 @@ components: - $ref: '#/components/schemas/AlpacaResponse' - properties: Value: - $ref: '#/components/schemas/CoverState' + $ref: '#/components/schemas/CoverStatus' AlignmentModeResponse: description: Transaction complete or exception. content: @@ -5996,6 +5995,16 @@ components: - properties: Value: $ref: '#/components/schemas/AlignmentMode' + EquatorialCoordinateTypeResponse: + description: Transaction complete or exception. + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/AlpacaResponse' + - properties: + Value: + $ref: '#/components/schemas/EquatorialCoordinateType' schemas: AxisRate: description: Axis rate object @@ -6078,17 +6087,13 @@ components: format: int32 minimum: 0 maximum: 2 - schema: - type: integer - format: int32 - default: 0 - oneOf: - - title: Primary - const: 0 - - title: Secondary - const: 1 - - title: Tertiary - const: 2 + oneOf: + - title: Primary + const: 0 + - title: Secondary + const: 1 + - title: Tertiary + const: 2 RightAscension: type: number description: Right Ascension coordinate (0.0 to 23.99999999 hours) @@ -6128,6 +6133,8 @@ components: Please note the compulsary trailing Z indicating the 'Zulu', UTC time zone. LastExposureStartTime: type: string + # Custom format for FITS standard date-time (similar to ISO 8601 but without the trailing Z). + format: date-time-fits pattern: '^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?$' description: >- The UTC date/time of exposure start in the FITS-standard CCYY-MM-DDThh:mm:ss[.sss...] format. @@ -6150,7 +6157,7 @@ components: - title: Reading description: Sensor array is being read out (digitized). const: 3 - - title: Downloading + - title: Download description: Downloading data to host. const: 4 - title: Error @@ -6196,7 +6203,7 @@ components: - title: LRGB description: Single-plane Bayer matrix LRGB sensor. const: 5 - ShutterStatus: + ShutterState: description: Indicates the current state of the shutter or roof. type: integer format: int32 @@ -6218,7 +6225,7 @@ components: - title: Error description: The shutter or roof has encountered a problem. const: 4 - CalibratorState: + CalibratorStatus: description: Describes the state of a calibration device. type: integer format: int32 @@ -6243,7 +6250,7 @@ components: - title: Error description: The calibrator encountered an error when changing state. const: 5 - CoverState: + CoverStatus: description: Describes the state of a telescope cover. type: integer format: int32 diff --git a/src/api/autogen/index.ts b/src/api/autogen/index.ts index 29d2c67..aab7204 100644 --- a/src/api/autogen/index.ts +++ b/src/api/autogen/index.ts @@ -207,7 +207,7 @@ interface EnumType extends RegisteredTypeBase { interface DateType extends RegisteredTypeBase { kind: 'Date'; - format: string; + trailingZ: boolean; } interface DeviceMethod { @@ -324,12 +324,12 @@ function handleType( case 'number': return rusty('f64'); case 'string': - if (schema.format === 'date') { + if (schema.format === 'date-time' || schema.format === 'date-time-fits') { let formatter = registerType(devicePath, schema, schema => ({ name, doc: getDoc(schema), kind: 'Date', - format: (schema as any)['date-format'] + trailingZ: schema.format === 'date-time', })); return rusty('std::time::SystemTime', `${formatter}`); } @@ -663,6 +663,7 @@ use macro_rules_attribute::apply; use num_enum::{IntoPrimitive, TryFromPrimitive}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; +use time::format_description::well_known::Iso8601; pub use server_info::*; @@ -744,6 +745,8 @@ ${stringifyIter(types, ({ features, type }) => { `; } case 'Date': { + let format = `Iso8601::DATE_TIME${type.trailingZ ? '_OFFSET' : ''}`; + return ` ${stringifyDoc(type.doc)} ${cfg} @@ -769,14 +772,10 @@ ${stringifyIter(types, ({ features, type }) => { ${cfg} impl ${type.name} { - const FORMAT: &'static [time::format_description::FormatItem<'static>] = time::macros::format_description!("${ - type.format - }"); - fn serialize(value: &time::OffsetDateTime, serializer: S) -> Result { value .to_offset(time::UtcOffset::UTC) - .format(Self::FORMAT) + .format(&${format}) .map_err(serde::ser::Error::custom)? .serialize(serializer) } @@ -792,9 +791,7 @@ ${stringifyIter(types, ({ features, type }) => { } fn visit_str(self, value: &str) -> Result { - match time::PrimitiveDateTime::parse(value, ${ - type.name - }::FORMAT) { + match time::PrimitiveDateTime::parse(value, &${format}) { Ok(time) => Ok(time.assume_utc()), Err(err) => Err(serde::de::Error::custom(err)), } diff --git a/src/api/image_array/client.rs b/src/api/image_array/client.rs index 84a5bf8..2bbc617 100644 --- a/src/api/image_array/client.rs +++ b/src/api/image_array/client.rs @@ -64,11 +64,11 @@ impl<'de> Visitor<'de> for ResponseVisitor { expect_key(&mut map, KnownKey::Value)?; let data = match rank { - ImageArrayRank::Rank2 => map + ImageArrayRank::SinglePlane => map .next_value::>>()? .0 .insert_axis(COLOUR_AXIS), - ImageArrayRank::Rank3 => map.next_value::>>()?.0, + ImageArrayRank::MultiPlane => map.next_value::>>()?.0, }; // Consume leftover fields. @@ -140,7 +140,7 @@ impl Response for ASCOMResult { usize::try_from(metadata.dimension_1)?, usize::try_from(metadata.dimension_2)?, match ImageArrayRank::try_from_primitive(metadata.rank)? { - ImageArrayRank::Rank2 => { + ImageArrayRank::SinglePlane => { eyre::ensure!( metadata.dimension_3 == 0_i32, "dimension 3 must be 0 for rank 2, got {}", @@ -148,7 +148,7 @@ impl Response for ASCOMResult { ); 1 } - ImageArrayRank::Rank3 => usize::try_from(metadata.dimension_3)?, + ImageArrayRank::MultiPlane => usize::try_from(metadata.dimension_3)?, }, ); Ok(ndarray::Array::from_shape_vec(shape, data)?.into()) diff --git a/src/api/image_array/mod.rs b/src/api/image_array/mod.rs index 37c95b8..cccdf0a 100644 --- a/src/api/image_array/mod.rs +++ b/src/api/image_array/mod.rs @@ -6,6 +6,7 @@ mod server; #[cfg(feature = "server")] pub(crate) use server::ImageBytesResponse; +use super::ImageArrayRank; use bytemuck::{Pod, Zeroable}; use ndarray::{Array2, Array3, ArrayView2, ArrayView3, Axis}; use num_enum::{IntoPrimitive, TryFromPrimitive}; @@ -16,27 +17,6 @@ use std::ops::Deref; // Missing alias in ndarray. type ArcArray3 = ndarray::ArcArray; -/// Rank of an image array. -#[derive( - Debug, - PartialEq, - Eq, - Clone, - Copy, - Serialize_repr, - Deserialize_repr, - TryFromPrimitive, - IntoPrimitive, -)] -#[repr(i32)] -#[allow(clippy::default_numeric_fallback)] // false positive https://github.com/rust-lang/rust-clippy/issues/9656 -pub enum ImageArrayRank { - /// 2D - Rank2 = 2_i32, - /// 3D - Rank3 = 3_i32, -} - #[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)] #[repr(i32)] #[allow(clippy::default_numeric_fallback)] // false positive https://github.com/rust-lang/rust-clippy/issues/9656 @@ -146,8 +126,8 @@ impl ImageArray { /// Retrieve actual rank of the image. pub fn rank(&self) -> ImageArrayRank { match self.data.len_of(COLOUR_AXIS) { - 1 => ImageArrayRank::Rank2, - _ => ImageArrayRank::Rank3, + 1 => ImageArrayRank::SinglePlane, + _ => ImageArrayRank::MultiPlane, } } } diff --git a/src/api/image_array/server.rs b/src/api/image_array/server.rs index c013b23..bbe3479 100644 --- a/src/api/image_array/server.rs +++ b/src/api/image_array/server.rs @@ -33,10 +33,10 @@ impl Response for ASCOMResult { metadata.dimension_1 = dims[0]; metadata.dimension_2 = dims[1]; metadata.rank = match dims[2] { - 1_i32 => ImageArrayRank::Rank2, + 1_i32 => ImageArrayRank::SinglePlane, n => { metadata.dimension_3 = n; - ImageArrayRank::Rank3 + ImageArrayRank::MultiPlane } } .into(); @@ -105,8 +105,8 @@ impl Serialize for ImageArray { type_: ImageElementType::I32, rank: self.rank(), value: match self.rank() { - ImageArrayRank::Rank2 => Value::Rank2(view.remove_axis(COLOUR_AXIS)), - ImageArrayRank::Rank3 => Value::Rank3(view), + ImageArrayRank::SinglePlane => Value::Rank2(view.remove_axis(COLOUR_AXIS)), + ImageArrayRank::MultiPlane => Value::Rank3(view), }, } .serialize(serializer) diff --git a/src/api/mod.rs b/src/api/mod.rs index c060508..1c2e66c 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -55,6 +55,7 @@ use macro_rules_attribute::apply; use num_enum::{IntoPrimitive, TryFromPrimitive}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; +use time::format_description::well_known::Iso8601; pub use server_info::*; @@ -88,7 +89,7 @@ pub enum CameraState { Reading = 3, /// Downloading data to host. - Downloading = 4, + Download = 4, /// Camera error condition serious enough to prevent further operations. Error = 5, @@ -124,6 +125,67 @@ mod image_array; #[cfg(feature = "camera")] pub use image_array::*; +/// The UTC date/time of exposure start in the FITS-standard CCYY-MM-DDThh:mm:ss[.sss...] format. +#[cfg(feature = "camera")] +#[derive(Debug, Serialize, Deserialize)] +pub(crate) struct LastExposureStartTime { + #[serde(rename = "Value", with = "LastExposureStartTime")] + pub(crate) value: time::OffsetDateTime, +} + +#[cfg(feature = "camera")] +impl From for LastExposureStartTime { + fn from(value: std::time::SystemTime) -> Self { + Self { + value: value.into(), + } + } +} + +#[cfg(feature = "camera")] +impl From for std::time::SystemTime { + fn from(wrapper: LastExposureStartTime) -> Self { + wrapper.value.into() + } +} + +#[cfg(feature = "camera")] +impl LastExposureStartTime { + fn serialize( + value: &time::OffsetDateTime, + serializer: S, + ) -> Result { + value + .to_offset(time::UtcOffset::UTC) + .format(&Iso8601::DATE_TIME) + .map_err(serde::ser::Error::custom)? + .serialize(serializer) + } + + fn deserialize<'de, D: serde::Deserializer<'de>>( + deserializer: D, + ) -> Result { + struct Visitor; + + impl serde::de::Visitor<'_> for Visitor { + type Value = time::OffsetDateTime; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("a date string") + } + + fn visit_str(self, value: &str) -> Result { + match time::PrimitiveDateTime::parse(value, &Iso8601::DATE_TIME) { + Ok(time) => Ok(time.assume_utc()), + Err(err) => Err(serde::de::Error::custom(err)), + } + } + } + + deserializer.deserialize_str(Visitor) + } +} + /// The type of sensor in the camera. #[cfg(feature = "camera")] #[derive( @@ -202,7 +264,7 @@ pub enum GuideDirection { #[repr(i32)] #[allow(clippy::default_numeric_fallback)] // false positive https://github.com/rust-lang/rust-clippy/issues/9656 #[allow(missing_docs)] // some enum variants might not have docs and that's okay -pub enum CalibratorState { +pub enum CalibratorStatus { /// This device does not have a calibration capability. NotPresent = 0, @@ -238,7 +300,7 @@ pub enum CalibratorState { #[repr(i32)] #[allow(clippy::default_numeric_fallback)] // false positive https://github.com/rust-lang/rust-clippy/issues/9656 #[allow(missing_docs)] // some enum variants might not have docs and that's okay -pub enum CoverState { +pub enum CoverStatus { /// This device does not have a cover that can be closed independently. NotPresent = 0, @@ -274,7 +336,7 @@ pub enum CoverState { #[repr(i32)] #[allow(clippy::default_numeric_fallback)] // false positive https://github.com/rust-lang/rust-clippy/issues/9656 #[allow(missing_docs)] // some enum variants might not have docs and that's okay -pub enum ShutterStatus { +pub enum ShutterState { /// The shutter or roof is open. Open = 0, @@ -318,6 +380,39 @@ pub enum AlignmentMode { GermanPolar = 2, } +/// The equatorial coordinate system used by the mount. +#[cfg(feature = "telescope")] +#[derive( + Debug, + PartialEq, + Eq, + Clone, + Copy, + Serialize_repr, + Deserialize_repr, + TryFromPrimitive, + IntoPrimitive, +)] +#[repr(i32)] +#[allow(clippy::default_numeric_fallback)] // false positive https://github.com/rust-lang/rust-clippy/issues/9656 +#[allow(missing_docs)] // some enum variants might not have docs and that's okay +pub enum EquatorialCoordinateType { + /// Custom or unknown equinox and/or reference frame. + Other = 0, + + /// Topocentric coordinates. + Topocentric = 1, + + /// J2000 equator/equinox. + J2000 = 2, + + /// J2050 equator/equinox. + J2050 = 3, + + /// B1950 equinox, FK4 reference frame. + B1950 = 4, +} + /// Returned side of pier #[cfg(feature = "telescope")] #[derive( @@ -375,6 +470,91 @@ pub enum DriveRate { King = 3, } +/// The UTC date/time of the telescope's internal clock in ISO 8601 format including fractional seconds. The general format (in Microsoft custom date format style) is yyyy-MM-ddTHH:mm:ss.fffffffZ, e.g. 2016-03-04T17:45:31.1234567Z or 2016-11-14T07:03:08.1234567Z. Please note the compulsary trailing Z indicating the 'Zulu', UTC time zone. +#[cfg(feature = "telescope")] +#[derive(Debug, Serialize, Deserialize)] +pub(crate) struct TelescopeUtcdate { + #[serde(rename = "Value", with = "TelescopeUtcdate")] + pub(crate) value: time::OffsetDateTime, +} + +#[cfg(feature = "telescope")] +impl From for TelescopeUtcdate { + fn from(value: std::time::SystemTime) -> Self { + Self { + value: value.into(), + } + } +} + +#[cfg(feature = "telescope")] +impl From for std::time::SystemTime { + fn from(wrapper: TelescopeUtcdate) -> Self { + wrapper.value.into() + } +} + +#[cfg(feature = "telescope")] +impl TelescopeUtcdate { + fn serialize( + value: &time::OffsetDateTime, + serializer: S, + ) -> Result { + value + .to_offset(time::UtcOffset::UTC) + .format(&Iso8601::DATE_TIME_OFFSET) + .map_err(serde::ser::Error::custom)? + .serialize(serializer) + } + + fn deserialize<'de, D: serde::Deserializer<'de>>( + deserializer: D, + ) -> Result { + struct Visitor; + + impl serde::de::Visitor<'_> for Visitor { + type Value = time::OffsetDateTime; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("a date string") + } + + fn visit_str(self, value: &str) -> Result { + match time::PrimitiveDateTime::parse(value, &Iso8601::DATE_TIME_OFFSET) { + Ok(time) => Ok(time.assume_utc()), + Err(err) => Err(serde::de::Error::custom(err)), + } + } + } + + deserializer.deserialize_str(Visitor) + } +} + +/// The axis about which rate information is desired. +#[cfg(feature = "telescope")] +#[derive( + Debug, + PartialEq, + Eq, + Clone, + Copy, + Serialize_repr, + Deserialize_repr, + TryFromPrimitive, + IntoPrimitive, +)] +#[repr(i32)] +#[allow(clippy::default_numeric_fallback)] // false positive https://github.com/rust-lang/rust-clippy/issues/9656 +#[allow(missing_docs)] // some enum variants might not have docs and that's okay +pub enum Axis { + Primary = 0, + + Secondary = 1, + + Tertiary = 2, +} + /// Axis rate object #[cfg(feature = "telescope")] #[allow(missing_copy_implementations)] @@ -812,8 +992,8 @@ pub trait Camera: Device + Send + Sync { } /// Reports the actual exposure start in the FITS-standard CCYY-MM-DDThh:mm:ss[.sss...] format. - #[http("lastexposurestarttime", method = Get, via = ValueResponse)] - async fn last_exposure_start_time(&self) -> ASCOMResult { + #[http("lastexposurestarttime", method = Get, via = LastExposureStartTime)] + async fn last_exposure_start_time(&self) -> ASCOMResult { Err(ASCOMError::NOT_IMPLEMENTED) } @@ -1040,15 +1220,15 @@ pub trait CoverCalibrator: Device + Send + Sync { Err(ASCOMError::NOT_IMPLEMENTED) } - /// Returns the state of the calibration device, if present, otherwise returns "NotPresent". The calibrator state mode is specified as an integer value from the CalibratorStatus Enum. + /// Returns the state of the calibration device, if present, otherwise returns "NotPresent". The calibrator state mode is specified as an integer value from the CalibratorStatus Enum. #[http("calibratorstate", method = Get, via = ValueResponse)] - async fn calibrator_state(&self) -> ASCOMResult { + async fn calibrator_state(&self) -> ASCOMResult { Err(ASCOMError::NOT_IMPLEMENTED) } - /// Returns the state of the device cover, if present, otherwise returns "NotPresent". The cover state mode is specified as an integer value from the CoverStatus Enum. + /// Returns the state of the device cover, if present, otherwise returns "NotPresent". The cover state mode is specified as an integer value from the CoverStatus Enum. #[http("coverstate", method = Get, via = ValueResponse)] - async fn cover_state(&self) -> ASCOMResult { + async fn cover_state(&self) -> ASCOMResult { Err(ASCOMError::NOT_IMPLEMENTED) } @@ -1167,7 +1347,7 @@ pub trait Dome: Device + Send + Sync { /// Returns the status of the dome shutter or roll-off roof. #[http("shutterstatus", method = Get, via = ValueResponse)] - async fn shutter_status(&self) -> ASCOMResult { + async fn shutter_status(&self) -> ASCOMResult { Err(ASCOMError::NOT_IMPLEMENTED) } @@ -1850,7 +2030,7 @@ pub trait Telescope: Device + Send + Sync { /// Returns the current equatorial coordinate system used by this telescope (e.g. Topocentric or J2000). #[http("equatorialsystem", method = Get, via = ValueResponse)] - async fn equatorial_system(&self) -> ASCOMResult { + async fn equatorial_system(&self) -> ASCOMResult { Err(ASCOMError::NOT_IMPLEMENTED) } @@ -2075,14 +2255,18 @@ pub trait Telescope: Device + Send + Sync { } /// Returns the UTC date/time of the telescope's internal clock. - #[http("utcdate", method = Get, via = ValueResponse)] - async fn utc_date(&self) -> ASCOMResult { + #[http("utcdate", method = Get, via = TelescopeUtcdate)] + async fn utc_date(&self) -> ASCOMResult { Err(ASCOMError::NOT_IMPLEMENTED) } /// Sets the UTC date/time of the telescope's internal clock. #[http("utcdate", method = Put)] - async fn set_utc_date(&self, #[http("UTCDate")] utc_date: String) -> ASCOMResult { + async fn set_utc_date( + &self, + + #[http("UTCDate", via = TelescopeUtcdate)] utc_date: std::time::SystemTime, + ) -> ASCOMResult { Err(ASCOMError::NOT_IMPLEMENTED) } @@ -2094,17 +2278,17 @@ pub trait Telescope: Device + Send + Sync { /// The rates at which the telescope may be moved about the specified axis by the MoveAxis(TelescopeAxes, Double) method. #[http("axisrates", method = Get, via = ValueResponse)] - async fn axis_rates(&self, #[http("Axis")] axis: i32) -> ASCOMResult> { + async fn axis_rates(&self, #[http("Axis")] axis: Axis) -> ASCOMResult> { Err(ASCOMError::NOT_IMPLEMENTED) } /// True if this telescope can move the requested axis. #[http("canmoveaxis", method = Get, via = ValueResponse)] - async fn can_move_axis(&self, #[http("Axis")] axis: i32) -> ASCOMResult { + async fn can_move_axis(&self, #[http("Axis")] axis: Axis) -> ASCOMResult { Ok(false) } - /// Predicts the pointing state that a German equatorial mount will be in if it slews to the given coordinates. The return value will be one of - 0 = pierEast, 1 = pierWest, -1 = pierUnknown + /// Predicts the pointing state that a German equatorial mount will be in if it slews to the given coordinates. #[http("destinationsideofpier", method = Get, via = ValueResponse)] async fn destination_side_of_pier( &self, @@ -2124,7 +2308,13 @@ pub trait Telescope: Device + Send + Sync { /// Move the telescope in one axis at the given rate. #[http("moveaxis", method = Put)] - async fn move_axis(&self, #[http("Axis")] axis: i32, #[http("Rate")] rate: f64) -> ASCOMResult { + async fn move_axis( + &self, + + #[http("Axis")] axis: Axis, + + #[http("Rate")] rate: f64, + ) -> ASCOMResult { Err(ASCOMError::NOT_IMPLEMENTED) }