diff --git a/lucuma-schemas/src/clue/resources/lucuma/schemas/ObservationDB.graphql b/lucuma-schemas/src/clue/resources/lucuma/schemas/ObservationDB.graphql index 4b6acb0c..9df7be1c 100644 --- a/lucuma-schemas/src/clue/resources/lucuma/schemas/ObservationDB.graphql +++ b/lucuma-schemas/src/clue/resources/lucuma/schemas/ObservationDB.graphql @@ -194,6 +194,50 @@ AtomId id formatted as `a-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a- """ scalar AtomId +"""An atom as recorded by Observe.""" +type AtomRecord { + """Atom ID.""" + id: AtomId! + + """The instrument associated with this atom.""" + instrument: Instrument! + + """Visit in which this atom was executed.""" + visit: Visit! + + """Created by Observe at this time.""" + created: Timestamp! + + """Sequence type.""" + sequenceType: SequenceType! + + """Number of valid steps required to complete this atom.""" + stepCount: NonNegShort! + + """Recorded steps associated with this atom.""" + steps( + """ + Starts the result set at (or after if not existent) the given step creation + time. + """ + OFFSET: Timestamp + + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): StepRecordSelectResult! +} + +"""AtomRecord query results, limited to a maximum of 1000 entries.""" +type AtomRecordSelectResult { + """Matching atom records up to the return size limit of 1000.""" + matches: [AtomRecord!]! + + """`true` when there were additional matches that were not returned.""" + hasMore: Boolean! +} + """ Create or edit a band brightness value with integrated magnitude units. When creating a new value, all fields except "error" are required. """ @@ -610,6 +654,18 @@ input ExchangeInput { minPercentTime: IntPercent } +"""Execution event types.""" +enum ExecutionEventType { + """Sequence-level event type.""" + SEQUENCE + + """Step-level event type.""" + STEP + + """Dataset-level event type.""" + DATASET +} + """ Exposure time mode input. Specify fixed or signal to noise, but not both """ @@ -990,27 +1046,6 @@ type GmosNorthAtom implements Atom { steps: [GmosNorthStep!]! } -"""GMOS North atom as recorded by Observe.""" -type GmosNorthAtomRecord { - """Atom ID""" - id: AtomId! - - """Visit ID""" - visitId: VisitId! - - """Created by Observe at this time.""" - created: Timestamp! - - """Sequence type.""" - sequenceType: SequenceType! - - """Number of valid steps required to complete this atom.""" - stepCount: NonNegShort! - - """Recorded steps""" - steps: [GmosNorthStepRecord!]! -} - """GmosNorth Detector type""" enum GmosNorthDetector { E2_V @@ -1269,72 +1304,128 @@ type GmosNorthStep implements Step { } """GMOS North step configuration as recorded by Observe.""" -type GmosNorthStepRecord { - """Step id""" +type GmosNorthStepRecord implements StepRecord { + """Step ID.""" id: StepId! - """Atom id""" - atomId: AtomId! + """Instrument associated with the concrete implementation.""" + instrument: Instrument! - """Created by Observe at time""" - created: Timestamp! + """The atom in which the step was executed.""" + atom: AtomRecord! - """Step index relative to the referenced atom.""" - stepIndex: NonNegShort! + """The step was created by Observe at this time.""" + created: Timestamp! - """Started at time""" + """Time of the first associated step event, if any.""" startTime: Timestamp - """Ended at time""" + """Time of the last associated step event, if any.""" endTime: Timestamp - """Step duration""" + """Step duration, the difference between 'endTime' and 'startTime'.""" duration: TimeSpan! - """GmosNorth configuration for this step""" - instrumentConfig: GmosNorthDynamic! - - """The executed step itself""" + """ + The step configuration. apart from instrument details in 'instrumentConfig'. + """ stepConfig: StepConfig! - """Step events associated with this step""" - stepEvents: [StepEvent!]! - - """Step QA state based on a combination of dataset QA states""" + """Step QA state based on a combination of dataset QA states.""" stepQaState: StepQaState - """Dataset events associated with this step""" - datasetEvents: [DatasetEvent!]! + """Datasets associated with this step.""" + datasets( + """ + Starts the result set at (or after if not existent) the given dataset id. + """ + OFFSET: DatasetId - """Datasets associated with this step""" - datasets: [Dataset!]! + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): DatasetSelectResult! + + """Execution events associated with this step.""" + events( + """ + Starts the result set at (or after if not existent) the given execution event id. + """ + OFFSET: ExecutionEventId + + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): ExecutionEventSelectResult! + + """GMOS North instrument configuration for this step.""" + instrumentConfig: GmosNorthDynamic! } """A GmosNorth visit as recorded by Observe""" -type GmosNorthVisit { - """Visit id""" +type GmosNorthVisit implements Visit { + """Visit id.""" id: VisitId! - """Created by Observe at time""" + """Implements the Visit.instrument field to return GMOS_NORTH.""" + instrument: Instrument! + + """Observation associated with this visit.""" + observation: Observation! + + """Created by Observe at time.""" created: Timestamp! - """Started at time""" + """Started at time.""" startTime: Timestamp - """Ended at time""" + """Ended at time.""" endTime: Timestamp - """Step duration""" + """Visit duration.""" duration: TimeSpan! - """GmosNorth static instrument configuration""" - static: GmosNorthStatic! + """Executed (or at least partially executed) atom records for this visit.""" + atomRecords( + """Starts the result set at the given atom creation time.""" + OFFSET: Timestamp + + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): AtomRecordSelectResult! - """GmosNorth recorded atoms""" - atoms: [GmosNorthAtomRecord!]! + """Datasets associated with this visit.""" + datasets( + """ + Starts the result set at (or after if not existent) the given dataset id. + """ + OFFSET: DatasetId - """Sequence events associated with this visit""" - sequenceEvents: [SequenceEvent!]! + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): DatasetSelectResult! + + """Execution events associated with this visit.""" + events( + """ + Starts the result set at (or after if not existent) the given execution event id. + """ + OFFSET: ExecutionEventId + + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): ExecutionEventSelectResult! + + """GmosNorth static instrument configuration""" + static: GmosNorthStatic! } """ @@ -1357,27 +1448,6 @@ type GmosSouthAtom implements Atom { steps: [GmosSouthStep!]! } -"""GMOS South atom as recorded by Observe.""" -type GmosSouthAtomRecord { - """Atom ID""" - id: AtomId! - - """Visit ID""" - visitId: VisitId! - - """Created by Observe at this time.""" - created: Timestamp! - - """Sequence type.""" - sequenceType: SequenceType! - - """Number of valid steps required to complete this atom.""" - stepCount: NonNegShort! - - """Recorded steps""" - steps: [GmosSouthStepRecord!]! -} - """GmosSouth Detector type""" enum GmosSouthDetector { E2_V @@ -1636,72 +1706,128 @@ type GmosSouthStep implements Step { } """A GmosSouth step configuration as recorded by Observe""" -type GmosSouthStepRecord { - """Step id""" +type GmosSouthStepRecord implements StepRecord { + """Step ID.""" id: StepId! - """Atom id""" - atomId: AtomId! + """Instrument associated with the concrete implementation.""" + instrument: Instrument! - """Step index relative to the referenced atom.""" - stepIndex: NonNegShort! + """The atom in which the step was executed.""" + atom: AtomRecord! - """Created by Observe at time""" + """The step was created by Observe at this time.""" created: Timestamp! - """Started at time""" + """Time of the first associated step event, if any.""" startTime: Timestamp - """Ended at time""" + """Time of the last associated step event, if any.""" endTime: Timestamp - """Step duration""" + """Step duration, the difference between 'endTime' and 'startTime'.""" duration: TimeSpan! - """GmosSouth configuration for this step""" - instrumentConfig: GmosSouthDynamic! - - """The executed step itself""" + """ + The step configuration. apart from instrument details in 'instrumentConfig'. + """ stepConfig: StepConfig! - """Step events associated with this step""" - stepEvents: [StepEvent!]! - - """Step QA state based on a combination of dataset QA states""" + """Step QA state based on a combination of dataset QA states.""" stepQaState: StepQaState - """Dataset events associated with this step""" - datasetEvents: [DatasetEvent!]! + """Datasets associated with this step.""" + datasets( + """ + Starts the result set at (or after if not existent) the given dataset id. + """ + OFFSET: DatasetId - """Datasets associated with this step""" - datasets: [Dataset!]! + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): DatasetSelectResult! + + """Execution events associated with this step.""" + events( + """ + Starts the result set at (or after if not existent) the given execution event id. + """ + OFFSET: ExecutionEventId + + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): ExecutionEventSelectResult! + + """GMOS South instrument configuration for this step.""" + instrumentConfig: GmosSouthDynamic! } """A GmosSouth visit as recorded by Observe""" -type GmosSouthVisit { - """Visit id""" +type GmosSouthVisit implements Visit { + """Visit id.""" id: VisitId! - """Created by Observe at time""" + """Implements the Visit.instrument field to return GMOS_SOUTH.""" + instrument: Instrument! + + """Observation associated with this visit.""" + observation: Observation! + + """Created by Observe at time.""" created: Timestamp! - """Started at time""" + """Started at time.""" startTime: Timestamp - """Ended at time""" + """Ended at time.""" endTime: Timestamp - """Step duration""" - duration: TimeSpan + """Visit duration.""" + duration: TimeSpan! - """GmosSouth static instrument configuration""" - static: GmosSouthStatic! + """Executed (or at least partially executed) atom records for this visit.""" + atomRecords( + """Starts the result set at the given atom creation time.""" + OFFSET: Timestamp - """GmosSouth recorded steps""" - steps: [GmosSouthStepRecord!]! + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): AtomRecordSelectResult! + + """Datasets associated with this visit.""" + datasets( + """ + Starts the result set at (or after if not existent) the given dataset id. + """ + OFFSET: DatasetId + + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): DatasetSelectResult! + + """Execution events associated with this visit.""" + events( + """ + Starts the result set at (or after if not existent) the given execution event id. + """ + OFFSET: ExecutionEventId + + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): ExecutionEventSelectResult! - """Sequence events associated with this visit""" - sequenceEvents: [SequenceEvent!]! + """GmosSouth static instrument configuration""" + static: GmosSouthStatic! } """Whether guiding is enabled for a particular science step.""" @@ -1896,6 +2022,12 @@ type Mutation { """ linkUser(input: LinkUserInput!): LinkUserResult! + """Record a new atom""" + recordAtom( + """Atom configuration parameters""" + input: RecordAtomInput! + ): RecordAtomResult! + """ Records a new dataset. This dataset may be subsequently referenced by dataset events. """ @@ -1904,12 +2036,6 @@ type Mutation { input: RecordDatasetInput! ): RecordDatasetResult! - """Record a new GMOS North atom""" - recordGmosNorthAtom( - """Atom configuration parameters""" - input: RecordAtomInput! - ): RecordGmosNorthAtomResult! - """Record a new GMOS North step""" recordGmosNorthStep( """GMOS North step configuration parameters""" @@ -1922,12 +2048,6 @@ type Mutation { input: RecordGmosNorthVisitInput! ): RecordGmosNorthVisitResult! - """Record a new GMOS South atom""" - recordGmosSouthAtom( - """GMOS South atom configuration parameters""" - input: RecordAtomInput! - ): RecordGmosSouthAtomResult! - """Record a new GMOS South step""" recordGmosSouthStep( """GmosSouth step configuration parameters""" @@ -2325,10 +2445,17 @@ input RadialVelocityInput { """Input parameters for creating a new atom record""" input RecordAtomInput { visitId: VisitId! + instrument: Instrument! sequenceType: SequenceType! stepCount: NonNegShort! } +"""The result of recording an atom.""" +type RecordAtomResult { + """The newly added atom record itself.""" + atomRecord: AtomRecord! +} + """Dataset creation parameters.""" input RecordDatasetInput { """Corresponding Step id.""" @@ -2347,12 +2474,6 @@ type RecordDatasetResult { dataset: Dataset! } -"""The result of recording a GMOS North atom.""" -type RecordGmosNorthAtomResult { - """The newly added atom record itself.""" - atomRecord: GmosNorthAtomRecord! -} - """Input parameters for creating a new GmosNorth StepRecord""" input RecordGmosNorthStepInput { atomId: AtomId! @@ -2378,12 +2499,6 @@ type RecordGmosNorthVisitResult { visit: GmosNorthVisit! } -"""The result of recording a GMOS South atom.""" -type RecordGmosSouthAtomResult { - """The newly added atom record itself.""" - atomRecord: GmosSouthAtomRecord! -} - """Input parameters for creating a new GmosSouth StepRecord""" input RecordGmosSouthStepInput { atomId: AtomId! @@ -3613,16 +3728,32 @@ type Dataset { """Dataset id.""" id: DatasetId! - """The corresponding step's id.""" - stepId: StepId! + """The corresponding step.""" + step: StepRecord! """Dataset index within the step.""" index: PosShort! - """Observation associated with this dataset""" + """Observation associated with this dataset.""" observation: Observation! - """Dataset filename""" + """Visit associated with this dataset.""" + visit: Visit! + + """Events associated with the dataset.""" + events( + """ + Starts the result set at (or after if not existent) the given execution event id. + """ + OFFSET: ExecutionEventId + + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): ExecutionEventSelectResult! + + """Dataset filename.""" filename: DatasetFilename! """Dataset QA state, if any has been set.""" @@ -3639,21 +3770,27 @@ type Dataset { Dataset-level events. A single dataset will be associated with multiple events as it makes its way through observe, readout and write stages. """ -type DatasetEvent { +type DatasetEvent implements ExecutionEvent { """Event id.""" id: ExecutionEventId! - """Dataset execution stage.""" - datasetStage: DatasetStage! - - """Identifies the associated dataset.""" - dataset: Dataset! + """Visit associated with this event.""" + visit: Visit! """Observation whose execution produced this event.""" observation: Observation! """Time at which this event was received.""" received: Timestamp! + + """Event type.""" + eventType: ExecutionEventType! + + """Dataset execution stage.""" + datasetStage: DatasetStage! + + """The associated dataset.""" + dataset: Dataset! } """Dataset filename in standard format.""" @@ -3843,7 +3980,20 @@ type Execution { futureLimit: NonNegInt = 25 ): ExecutionConfig! - """Datasets associated with the observation.""" + """ + Executed (or at least partially executed) atom records, across all visits. + """ + atomRecords( + """Starts the result set at the given atom creation time.""" + OFFSET: Timestamp + + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): AtomRecordSelectResult! + + """Datasets associated with the observation, across all visits.""" datasets( """ Starts the result set at (or after if not existent) the given dataset id. @@ -3856,7 +4006,7 @@ type Execution { LIMIT: NonNegInt ): DatasetSelectResult! - """Events associated with the observation""" + """Events associated with the observation, across all visits.""" events( """ Starts the result set at (or after if not existent) the given execution event id. @@ -3868,6 +4018,19 @@ type Execution { """ LIMIT: NonNegInt ): ExecutionEventSelectResult! + + """Visits associated with the observation.""" + visits( + """ + Starts the result set at (or after if not existent) the given visit id. + """ + OFFSET: VisitId + + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): VisitSelectResult! } """Execution configuration""" @@ -3878,17 +4041,20 @@ interface ExecutionConfig { """Execution event (sequence, step, or dataset events)""" interface ExecutionEvent { - """Event id""" + """Event id.""" id: ExecutionEventId! - """Associated visit""" - visitId: VisitId! + """Visit associated with the event.""" + visit: Visit! - """Observation whose execution produced this event""" + """Observation whose execution produced this event.""" observation: Observation! - """Time at which this event was received""" + """Time at which this event was received.""" received: Timestamp! + + """Event type.""" + eventType: ExecutionEventType! } """ExecutionEventId id formatted as `e-[1-9a-f][0-9a-f]*`""" @@ -5970,21 +6136,24 @@ type SequenceDigest { """ Sequence-level events. As commands are issued to execute a sequence, corresponding events are generated. """ -type SequenceEvent { - """Event id""" +type SequenceEvent implements ExecutionEvent { + """Event id.""" id: ExecutionEventId! - """Associated visit""" - visitId: VisitId! - - """Sequence event data""" - command: SequenceCommand! + """Visit associated with this event.""" + visit: Visit! - """Observation whose execution produced this event""" + """Observation whose execution produced this event.""" observation: Observation! - """Time at which this event was received""" + """Time at which this event was received.""" received: Timestamp! + + """Event type.""" + eventType: ExecutionEventType! + + """Sequence event data.""" + command: SequenceCommand! } """Type of sequence, acquisition or science""" @@ -6272,24 +6441,27 @@ enum StellarLibrarySpectrum { """ Step-level events. The execution of a single step will generate multiple events. """ -type StepEvent { +type StepEvent implements ExecutionEvent { """Event id""" id: ExecutionEventId! - """Identifies the step to which the event refers.""" - stepId: StepId! - - """Whether this is an acquisition or science sequence step.""" - sequenceType: SequenceType! - - """Step execution stage.""" - stepStage: StepStage! + """Visit associated with this event.""" + visit: Visit! """Observation whose execution produced this event.""" observation: Observation! """Time at which this event was received""" received: Timestamp! + + """Event type.""" + eventType: ExecutionEventType! + + """Step associated with this event.""" + step: StepRecord! + + """Step execution stage.""" + stepStage: StepStage! } """ @@ -6439,6 +6611,77 @@ type StepEstimate { total: TimeSpan! } +""" +A step as recorded by Observe. There will be one `StepRecord` implementation +per instrument. +""" +interface StepRecord { + """Step ID.""" + id: StepId! + + """Instrument associated with the concrete implementation.""" + instrument: Instrument! + + """The atom in which the step was executed.""" + atom: AtomRecord! + + """The step was created by Observe at this time.""" + created: Timestamp! + + """Time of the first associated step event, if any.""" + startTime: Timestamp + + """Time of the last associated step event, if any.""" + endTime: Timestamp + + """Step duration, the difference between 'endTime' and 'startTime'.""" + duration: TimeSpan! + + """ + The step configuration, apart from instrument details found in the + instrument-specific 'StepRecord' implementation. + """ + stepConfig: StepConfig! + + """Step QA state based on a combination of dataset QA states.""" + stepQaState: StepQaState + + """Datasets associated with this step.""" + datasets( + """ + Starts the result set at (or after if not existent) the given dataset id. + """ + OFFSET: DatasetId + + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): DatasetSelectResult! + + """Execution events associated with this step.""" + events( + """ + Starts the result set at (or after if not existent) the given execution event id. + """ + OFFSET: ExecutionEventId + + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): ExecutionEventSelectResult! +} + +"""StepRecord query results, limited to a maximum of 1000 entries.""" +type StepRecordSelectResult { + """Matching step records up to the return size limit of 1000.""" + matches: [StepRecord!]! + + """`true` when there were additional matches that were not returned.""" + hasMore: Boolean! +} + """System Verification""" type SystemVerification implements ProposalClass { """Minimum percent of requested observation time that is required""" @@ -6706,6 +6949,82 @@ type User { orcidEmail: String } +""" +A visit is recorded whenever any part of an observation is attempted. There +are specific impementations for each instrument. +""" +interface Visit { + """Visit id.""" + id: VisitId! + + """ + Instrument in use for this visit. This serves as a discriminator between the + various specific instrument visit types. + """ + instrument: Instrument! + + """Observation associated with this visit.""" + observation: Observation! + + """Created by Observe at time.""" + created: Timestamp! + + """Started at time.""" + startTime: Timestamp + + """Ended at time.""" + endTime: Timestamp + + """Visit duration.""" + duration: TimeSpan! + + """Executed (or at least partially executed) atom records for this visit.""" + atomRecords( + """Starts the result set at the given atom creation time.""" + OFFSET: Timestamp + + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): AtomRecordSelectResult! + + """Datasets associated with this visit.""" + datasets( + """ + Starts the result set at (or after if not existent) the given dataset id. + """ + OFFSET: DatasetId + + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): DatasetSelectResult! + + """Execution events associated with this visit.""" + events( + """ + Starts the result set at (or after if not existent) the given execution event id. + """ + OFFSET: ExecutionEventId + + """ + Limits the result to at most this number of matches (but never more than 1000). + """ + LIMIT: NonNegInt + ): ExecutionEventSelectResult! +} + +"""Matching visit results, limited to a maximum of 1000 entries.""" +type VisitSelectResult { + """Matching visits up to the return size limit of 1000.""" + matches: [Visit!]! + + """`true` when there were additional matches that were not returned.""" + hasMore: Boolean! +} + """VisitId id formatted as `v-[1-9a-f][0-9a-f]*`""" scalar VisitId diff --git a/lucuma-schemas/src/main/scala/lucuma/schemas/odb/input/package.scala b/lucuma-schemas/src/main/scala/lucuma/schemas/odb/input/package.scala index 6620195d..da7608f6 100644 --- a/lucuma-schemas/src/main/scala/lucuma/schemas/odb/input/package.scala +++ b/lucuma-schemas/src/main/scala/lucuma/schemas/odb/input/package.scala @@ -10,12 +10,21 @@ import eu.timepit.refined.types.numeric.PosBigDecimal import eu.timepit.refined.types.string.NonEmptyString import io.circe.syntax.* import lucuma.core.enums.Band +import lucuma.core.enums.GmosNorthFpu +import lucuma.core.enums.GmosSouthFpu import lucuma.core.math.BrightnessUnits.* import lucuma.core.math.* import lucuma.core.math.dimensional.* import lucuma.core.model.ExposureTimeMode.* import lucuma.core.model.ProposalClass.* import lucuma.core.model.* +import lucuma.core.model.sequence.StepConfig +import lucuma.core.model.sequence.gmos.DynamicConfig +import lucuma.core.model.sequence.gmos.GmosCcdMode +import lucuma.core.model.sequence.gmos.GmosFpuMask +import lucuma.core.model.sequence.gmos.GmosGratingConfig +import lucuma.core.model.sequence.gmos.GmosNodAndShuffle +import lucuma.core.model.sequence.gmos.StaticConfig import lucuma.core.util.* import lucuma.schemas.ObservationDB.Enums.PosAngleConstraintMode import lucuma.schemas.ObservationDB.Types.* @@ -375,6 +384,8 @@ extension [A](o: Offset.Component[A]) def toInput: OffsetComponentInput = OffsetComponentInput(microarcseconds = o.toAngle.toMicroarcseconds.assign) +extension (o: Offset) def toInput: OffsetInput = OffsetInput(o.p.toInput, o.q.toInput) + extension (o: ObservingMode.GmosNorthLongSlit) def toInput: GmosNorthLongSlitInput = GmosNorthLongSlitInput( grating = o.grating.assign, @@ -489,3 +500,120 @@ extension (tw: TimingWindow) startUtc = tw.start, end = tw.end.map(_.toInput).orIgnore ) + +extension (ccd: GmosCcdMode) + def toInput: GmosCcdModeInput = + GmosCcdModeInput(xBin = ccd.xBin.assign, + yBin = ccd.yBin.assign, + ampCount = ccd.ampCount.assign, + ampGain = ccd.ampGain.assign, + ampReadMode = ccd.ampReadMode.assign + ) + +extension (g: GmosGratingConfig.South) + def toInput: GmosSouthGratingConfigInput = + GmosSouthGratingConfigInput(grating = g.grating, + order = g.order, + wavelength = g.wavelength.toInput + ) + +extension (g: GmosGratingConfig.North) + def toInput: GmosNorthGratingConfigInput = + GmosNorthGratingConfigInput(grating = g.grating, + order = g.order, + wavelength = g.wavelength.toInput + ) + +extension (g: GmosFpuMask.Custom) + def toInput: GmosCustomMaskInput = + GmosCustomMaskInput(filename = g.filename.value, slitWidth = g.slitWidth) + +extension (g: GmosFpuMask[GmosSouthFpu]) + def toInput: GmosSouthFpuInput = + GmosSouthFpuInput(customMask = g.custom.map(_.toInput).orUnassign, + builtin = g.builtinFpu.orUnassign + ) + +extension (g: GmosFpuMask[GmosNorthFpu]) + def toInput: GmosNorthFpuInput = + GmosNorthFpuInput(customMask = g.custom.map(_.toInput).orUnassign, + builtin = g.builtinFpu.orUnassign + ) +extension (ns: GmosNodAndShuffle) + def toInput: GmosNodAndShuffleInput = GmosNodAndShuffleInput( + ns.posA.toInput, + ns.posB.toInput, + ns.eOffset, + ns.shuffleOffset, + ns.shuffleCycles + ) + +extension (gmosNStatic: StaticConfig.GmosNorth) + def toInput: GmosNorthStaticInput = GmosNorthStaticInput( + gmosNStatic.stageMode.assign, + gmosNStatic.detector.assign, + gmosNStatic.mosPreImaging.assign, + gmosNStatic.nodAndShuffle.map(_.toInput).orUnassign + ) + +extension (gmosSStatic: StaticConfig.GmosSouth) + def toInput: GmosSouthStaticInput = GmosSouthStaticInput( + gmosSStatic.stageMode.assign, + gmosSStatic.detector.assign, + gmosSStatic.mosPreImaging.assign, + gmosSStatic.nodAndShuffle.map(_.toInput).orUnassign + ) + +extension (gmosSDynamic: DynamicConfig.GmosSouth) + def toInput: GmosSouthDynamicInput = GmosSouthDynamicInput( + gmosSDynamic.exposure.toInput, + gmosSDynamic.readout.toInput, + gmosSDynamic.dtax, + gmosSDynamic.roi, + gmosSDynamic.gratingConfig.map(_.toInput).orUnassign, + gmosSDynamic.filter.orUnassign, + gmosSDynamic.fpu.map(_.toInput).orUnassign + ) + +extension (gmosNDynamic: DynamicConfig.GmosNorth) + def toInput: GmosNorthDynamicInput = GmosNorthDynamicInput( + gmosNDynamic.exposure.toInput, + gmosNDynamic.readout.toInput, + gmosNDynamic.dtax, + gmosNDynamic.roi, + gmosNDynamic.gratingConfig.map(_.toInput).orUnassign, + gmosNDynamic.filter.orUnassign, + gmosNDynamic.fpu.map(_.toInput).orUnassign + ) + +extension (sc: StepConfig) + def toInput: StepConfigInput = sc match { + case StepConfig.Bias => + StepConfigInput(bias = true.assign) + + case StepConfig.Dark => + StepConfigInput(dark = true.assign) + + case StepConfig.Gcal(lamp, filter, diffuser, shutter) => + val gcal = StepConfigGcalInput( + arcs = lamp.arcs.map(_.toNonEmptyList.toList).orIgnore, + continuum = lamp.continuum.orIgnore, + filter = filter, + diffuser = diffuser, + shutter = shutter + ) + StepConfigInput(gcal = gcal.assign) + + case StepConfig.Science(offset, guiding) => + val science = StepConfigScienceInput( + offset = offset.toInput, + guiding = guiding.assign + ) + StepConfigInput(science = science.assign) + + case StepConfig.SmartGcal(smartGcalType) => + val smartGcal = StepConfigSmartGcalInput( + smartGcalType = smartGcalType + ) + StepConfigInput(smartGcal = smartGcal.assign) + }