1use std::cell::RefCell;
59use std::collections::BTreeMap;
60use std::fmt::{self, Debug, Display, Formatter};
61use std::ops::Shl;
62use std::rc::Rc;
63use std::time::Duration;
64
65use serde::Serialize;
66use tracing::{Level, event, span};
67
68use crate::diagnostics::CurrentInstructionDiagnostics;
69
70use super::PETR;
71use super::alarm::{Alarm, AlarmDetails, Alarmer};
72use super::alarmunit::AlarmUnit;
73use super::changelog::ChangeIndex;
74use super::context::Context;
75use super::control::ControlUnit;
76use super::event::*;
77use super::types::*;
78use base::charset::LincolnState;
79use base::prelude::*;
80
81mod dev_lincoln_writer;
82mod dev_petr;
83mod pollq;
84
85use dev_lincoln_writer::{LincolnWriterInput, LincolnWriterOutput};
86pub(crate) use dev_petr::Petr;
87use pollq::PollQueue;
88
89const IO_MASK_MISIND: Unsigned36Bit = Unsigned36Bit::MAX.and(0o_000_000_010_000);
91
92const IO_MASK_CONNECTED: Unsigned36Bit = Unsigned36Bit::MAX.and(0o_000_000_040_000);
94
95const IO_MASK_MAINT: Unsigned36Bit = Unsigned36Bit::MAX.and(0o_000_000_100_000);
97
98const IO_MASK_AVAIL: Unsigned36Bit = Unsigned36Bit::MAX.and(0o_000_000_200_000);
101
102const IO_MASK_FLAG: Unsigned36Bit = Unsigned36Bit::MAX.and(0o_000_000_400_000);
105
106#[derive(Debug)]
108pub enum TransferFailed {
109 BufferNotFree,
111 Alarm(Alarm), }
114
115impl Display for TransferFailed {
116 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
117 match self {
118 TransferFailed::BufferNotFree => {
119 f.write_str("Unit buffer not available for use by the CPU")
120 }
121 TransferFailed::Alarm(alarm) => write!(f, "{alarm}"),
122 }
123 }
124}
125
126impl std::error::Error for TransferFailed {}
127
128pub enum UnitType {
129 StartOver,
131 IndexRegister(SequenceNumber),
133 Hardware(SequenceNumber),
135
136 SoftwareOnly(SequenceNumber),
141}
142
143impl UnitType {
144 pub fn sequence(&self) -> SequenceNumber {
145 match self {
146 UnitType::StartOver => SequenceNumber::ZERO,
147 UnitType::IndexRegister(s) | UnitType::SoftwareOnly(s) | UnitType::Hardware(s) => *s,
148 }
149 }
150}
151
152impl From<Unsigned6Bit> for UnitType {
153 fn from(n: Unsigned6Bit) -> UnitType {
154 match u8::from(n) {
155 0 => UnitType::StartOver,
156 1..=0o40 => UnitType::IndexRegister(n),
157 0o41..=0o75 => UnitType::Hardware(n),
158 0o76..=0o77 => UnitType::SoftwareOnly(n),
159 _ => unreachable!("unit number outside the range of an unsigned 6-bit quantity"),
160 }
161 }
162}
163
164#[derive(Debug, Serialize)]
167pub struct UnitStatus {
168 pub special: Unsigned12Bit,
169 pub change_flag: Option<FlagChange>,
170 pub buffer_available_to_cpu: bool,
171 pub inability: bool,
172 pub missed_data: bool,
173 pub mode: Unsigned12Bit,
174
175 pub poll_after: Duration,
178
179 pub is_input_unit: bool,
183}
184
185#[derive(Debug, Serialize)]
187pub struct ExtendedConnectedUnitStatus {
188 pub buffer_available_to_cpu: bool,
189 pub inability: bool,
190 pub missed_data: bool,
191 pub special: u16,
192 pub mode: u16,
193}
194
195#[derive(Debug)]
197pub struct ExtendedUnitState {
198 pub flag: bool,
200
201 pub index_value: Signed18Bit,
203
204 pub connected: bool, pub in_maintenance: bool,
209
210 pub name: String,
212
213 pub text_info: String,
215
216 pub status: Option<ExtendedConnectedUnitStatus>,
219}
220
221fn make_unit_report_word(
222 unit: Unsigned6Bit,
223 is_connected: bool,
224 is_maint: bool,
225 current_flag: bool,
226 status: &UnitStatus,
227) -> Unsigned36Bit {
228 let mut report: Unsigned36Bit = Unsigned36Bit::from(status.mode);
229 if status.missed_data {
230 report = report | IO_MASK_MISIND;
231 }
232 if is_connected {
233 report = report | IO_MASK_CONNECTED;
234 }
235 if is_maint {
236 report = report | IO_MASK_MAINT;
237 }
238 if status.buffer_available_to_cpu {
239 report = report | IO_MASK_AVAIL;
240 }
241 if current_flag || matches!(&status.change_flag, Some(FlagChange::Raise(_))) {
243 report = report | IO_MASK_FLAG;
244 }
245 report | Unsigned36Bit::from(unit).shl(18) | Unsigned36Bit::from(status.special).shl(24)
246}
247
248fn make_report_word_for_invalid_unit(unit: Unsigned6Bit, current_flag: bool) -> Unsigned36Bit {
249 make_unit_report_word(
250 unit,
251 false, true, current_flag,
254 &UnitStatus {
255 special: Unsigned12Bit::ZERO,
256 change_flag: None,
257 buffer_available_to_cpu: false,
258 inability: true,
259 missed_data: false,
260 mode: Unsigned12Bit::ZERO,
261 poll_after: Duration::from_secs(10),
262 is_input_unit: false,
263 },
264 )
265}
266
267pub trait Unit {
268 fn poll(&mut self, ctx: &Context) -> UnitStatus;
270
271 fn text_info(&self, ctx: &Context) -> String;
273
274 fn connect(&mut self, ctx: &Context, mode: Unsigned12Bit);
276
277 fn disconnect(&mut self, ctx: &Context);
279
280 fn transfer_mode(&self) -> TransferMode;
282
283 fn read(
285 &mut self,
286 ctx: &Context,
287 diagnostics: &CurrentInstructionDiagnostics,
288 ) -> Result<MaskedWord, TransferFailed>;
289
290 fn write(
292 &mut self,
293 ctx: &Context,
294 source: Unsigned36Bit,
295 diagnostics: &CurrentInstructionDiagnostics,
296 ) -> Result<Option<OutputEvent>, TransferFailed>;
297
298 fn name(&self) -> String;
300
301 fn on_input_event(
303 &mut self,
304 ctx: &Context,
305 event: InputEvent,
306 ) -> Result<InputFlagRaised, InputEventError>;
307}
308
309pub struct AttachedUnit {
312 inner: RefCell<Box<dyn Unit>>,
313
314 unit: Unsigned6Bit,
315
316 pub is_input_unit: bool,
320
321 pub connected: bool,
322 pub in_maintenance: bool,
323}
324
325impl AttachedUnit {
326 fn is_disconnected_output_unit(&self) -> bool {
327 (!self.is_input_unit) && (!self.connected)
328 }
329
330 fn call_inner<A, F, T>(
334 &self,
335 what: &str,
336 alarmer: &mut A,
337 diagnostics: &CurrentInstructionDiagnostics,
338 f: F,
339 ) -> Result<T, Alarm>
340 where
341 A: Alarmer,
342 F: Fn(&dyn Unit) -> T,
343 {
344 if self.connected {
345 let output = f(self.inner.borrow().as_ref());
346 Ok(output)
347 } else {
348 Err(alarmer.always_fire(
349 Alarm {
350 sequence: Some(self.unit),
351 details: AlarmDetails::BUGAL {
352 activity: crate::alarm::BugActivity::Io,
353 diagnostics: diagnostics.clone(),
354 message: format!(
355 "attempt read-only use (for {}) of disconnected unit {:o}",
356 what, self.unit
357 ),
358 },
359 },
360 diagnostics,
361 ))
362 }
363 }
364
365 fn call_mut_inner<A, F, T>(
369 &self,
370 what: &str,
371 alarmer: &mut A,
372 diagnostics: &CurrentInstructionDiagnostics,
373 mut f: F,
374 ) -> Result<T, Alarm>
375 where
376 A: Alarmer,
377 F: FnMut(&mut dyn Unit) -> T,
378 {
379 if self.connected {
380 let output = f(self.inner.borrow_mut().as_mut());
381 Ok(output)
382 } else {
383 Err(alarmer.always_fire(
384 Alarm {
385 sequence: Some(self.unit),
386 details: AlarmDetails::BUGAL {
387 activity: crate::alarm::BugActivity::Io,
388 diagnostics: diagnostics.clone(),
389 message: format!(
390 "attempt read-write use (for {}) of disconnected unit {:o}",
391 what, self.unit
392 ),
393 },
394 },
395 diagnostics,
396 ))
397 }
398 }
399
400 pub fn poll<A: Alarmer>(&self, ctx: &Context, _alarmer: &mut A) -> Result<UnitStatus, Alarm> {
402 Ok(self.inner.borrow_mut().poll(ctx))
403 }
404
405 fn text_info(&self, ctx: &Context) -> String {
408 self.inner.borrow().text_info(ctx)
409 }
410
411 pub fn connect(&self, ctx: &Context, mode: Unsigned12Bit) {
413 self.inner.borrow_mut().connect(ctx, mode);
414 }
415
416 pub fn disconnect<A: Alarmer>(&self, ctx: &Context, _alarmer: &mut A) -> Result<(), Alarm> {
418 if !self.connected {
419 event!(
426 Level::WARN,
427 "disconnecting the not-connected unit {:o}",
428 self.unit
429 );
430 }
431 self.inner.borrow_mut().disconnect(ctx);
432 Ok(())
433 }
434
435 pub fn transfer_mode<A: Alarmer>(
437 &self,
438 alarmer: &mut A,
439 diagnostics: &CurrentInstructionDiagnostics,
440 ) -> Result<TransferMode, Alarm> {
441 self.call_inner("transfer_mode", alarmer, diagnostics, |unit: &dyn Unit| {
442 unit.transfer_mode()
443 })
444 }
445
446 pub fn read<A: Alarmer>(
448 &self,
449 ctx: &Context,
450 alarmer: &mut A,
451 diagnostics: &CurrentInstructionDiagnostics,
452 ) -> Result<MaskedWord, TransferFailed> {
453 match self.call_mut_inner("read", alarmer, diagnostics, |unit: &mut dyn Unit| {
454 unit.read(ctx, diagnostics)
455 }) {
456 Ok(Ok(mw)) => Ok(mw),
457 Ok(Err(e)) => Err(e),
458 Err(alarm) => Err(TransferFailed::Alarm(alarm)),
459 }
460 }
461
462 pub fn write(
464 &mut self,
465 ctx: &Context,
466 source: Unsigned36Bit,
467 diagnostics: &CurrentInstructionDiagnostics,
468 ) -> Result<Option<OutputEvent>, TransferFailed> {
469 self.inner.borrow_mut().write(ctx, source, diagnostics)
470 }
471
472 pub fn on_input_event(
474 &self,
475 ctx: &Context,
476 event: InputEvent,
477 ) -> Result<InputFlagRaised, InputEventError> {
478 self.inner.borrow_mut().on_input_event(ctx, event)
479 }
480
481 pub fn name(&self) -> String {
483 self.inner.borrow().name()
484 }
485}
486
487impl Debug for AttachedUnit {
488 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
489 f.debug_struct("AttachedUnit")
490 .field("inner", &format_args!("<unit: {}>", self.name()))
491 .field("is_input_unit", &self.is_input_unit)
492 .field("connected", &self.connected)
493 .field("in_maintenance", &self.in_maintenance)
494 .finish()
495 }
496}
497
498#[derive(Debug)]
501pub struct DeviceManager {
502 devices: BTreeMap<Unsigned6Bit, AttachedUnit>,
503 poll_queue: PollQueue,
504 changes: ChangeIndex<Unsigned6Bit>,
505}
506
507#[derive(Debug, Clone, Copy, PartialEq, Eq)]
509pub enum InputFlagRaised {
510 No,
511 Yes,
512}
513
514impl From<InputFlagRaised> for bool {
515 fn from(f: InputFlagRaised) -> bool {
516 match f {
517 InputFlagRaised::Yes => true,
518 InputFlagRaised::No => false,
519 }
520 }
521}
522
523impl DeviceManager {
524 #[must_use]
525 pub fn new() -> DeviceManager {
526 DeviceManager {
527 devices: BTreeMap::new(),
528 poll_queue: PollQueue::new(),
529 changes: ChangeIndex::default(),
530 }
531 }
532
533 #[must_use]
534 pub fn get(&self, unit_number: &Unsigned6Bit) -> Option<&AttachedUnit> {
535 self.devices.get(unit_number)
536 }
537
538 pub fn get_mut(&mut self, unit_number: &Unsigned6Bit) -> Option<&mut AttachedUnit> {
539 self.devices.get_mut(unit_number)
540 }
541
542 pub fn update_poll_time(&mut self, ctx: &Context, seq: SequenceNumber) {
543 if let Err(pollq::PollQueueUpdateFailure::UnknownSequence(seq)) =
544 self.poll_queue.update(seq, ctx.simulated_time)
545 {
546 match UnitType::from(seq) {
551 UnitType::SoftwareOnly(_) => (), UnitType::Hardware(_) => (), UnitType::StartOver => {
554 unreachable!("attempted to update poll time for STARTOVER unit");
555 }
556 UnitType::IndexRegister(n) => {
557 unreachable!("attempted to update poll time for index register unit {n:o}");
558 }
559 }
560 }
561 }
562
563 fn get_extended_status<A: Alarmer>(
564 &self,
565 ctx: &Context,
566 unit: &AttachedUnit,
567 alarmer: &mut A,
568 index_value: Signed18Bit,
569 ) -> Result<ExtendedUnitState, Alarm> {
570 let (extended_unit_status, flag): (Option<ExtendedConnectedUnitStatus>, bool) =
571 if unit.connected {
572 let unit_status = unit.poll(ctx, alarmer)?;
573 let flag: bool = matches!(unit_status.change_flag, Some(FlagChange::Raise(_)));
574 let ext_status = ExtendedConnectedUnitStatus {
575 buffer_available_to_cpu: unit_status.buffer_available_to_cpu,
576 inability: unit_status.inability,
577 missed_data: unit_status.missed_data,
578 special: u16::from(unit_status.special),
579 mode: u16::from(unit_status.mode),
580 };
581 (Some(ext_status), flag)
582 } else {
583 (None, false)
584 };
585 Ok(ExtendedUnitState {
586 flag,
587 connected: unit.connected,
588 in_maintenance: unit.in_maintenance,
589 name: unit.name(),
590 text_info: unit.text_info(ctx),
591 status: extended_unit_status,
592 index_value,
593 })
594 }
595
596 pub fn device_statuses(
597 &self,
598 ctx: &Context,
599 control: &mut ControlUnit,
600 ) -> Result<BTreeMap<Unsigned6Bit, ExtendedUnitState>, Alarm> {
601 let mut result: BTreeMap<Unsigned6Bit, ExtendedUnitState> = BTreeMap::new();
602 for (unit, attached) in &self.devices {
603 let xreg_value = match control
604 .inspect_registers()
605 .index_regs
606 .get(usize::from(*unit))
607 {
608 Some(xreg_value) => xreg_value,
609 None => {
610 unreachable!("There is no index register for unit {unit:o}");
614 }
615 };
616 let ext_status = self.get_extended_status(ctx, attached, control, *xreg_value)?;
617 result.insert(*unit, ext_status);
618 }
619 Ok(result)
620 }
621
622 pub fn attach(
623 &mut self,
624 ctx: &Context,
625 unit_type: UnitType,
626 in_maintenance: bool,
627 mut unit: Box<dyn Unit>,
628 ) {
629 let unit_number = match unit_type {
630 UnitType::StartOver => {
631 panic!("Cannot attach hardware to unit 0");
632 }
633 UnitType::IndexRegister(reg) => {
634 panic!(
635 "Cannot attach hardware to unit {reg:o}, it's reserved for use as an index register"
636 );
637 }
638 UnitType::SoftwareOnly(seq) => {
639 panic!("Cannot attach hardware to software-only unit {seq:o}");
640 }
641 UnitType::Hardware(seq) => seq,
642 };
643 self.mark_device_changed(unit_number);
644 let status: UnitStatus = unit.poll(ctx);
645 self.devices.insert(
646 unit_number,
647 AttachedUnit {
648 inner: RefCell::new(unit),
649 unit: unit_number,
650 is_input_unit: status.is_input_unit,
651 connected: false,
652 in_maintenance,
653 },
654 );
655 self.poll_queue.push(unit_number, status.poll_after);
656 }
657
658 pub fn on_input_event(
659 &mut self,
660 ctx: &Context,
661 unit_number: Unsigned6Bit,
662 input_event: InputEvent,
663 ) -> Result<InputFlagRaised, InputEventError> {
664 self.mark_device_changed(unit_number);
665 if let Some(attached) = self.devices.get_mut(&unit_number) {
666 attached.on_input_event(ctx, input_event)
667 } else {
668 Err(InputEventError::InputOnUnattachedUnit)
671 }
672 }
673
674 pub fn report(
676 &mut self,
677 ctx: &Context,
678 current_sequence: Option<SequenceNumber>,
680 unit: Unsigned6Bit,
682 current_flag: bool,
683 alarm_unit: &mut AlarmUnit,
684 diagnostics: &CurrentInstructionDiagnostics,
685 ) -> Result<Unsigned36Bit, Alarm> {
686 match self.devices.get_mut(&unit) {
687 Some(attached) => {
688 let unit_status = attached.poll(ctx, alarm_unit)?;
693 self.poll_queue.push(unit, unit_status.poll_after);
694 Ok(make_unit_report_word(
695 unit,
696 attached.connected,
697 attached.in_maintenance,
698 current_flag,
699 &unit_status,
700 ))
701 }
702 None => {
703 alarm_unit.fire_if_not_masked(
704 Alarm {
705 sequence: current_sequence,
706 details: AlarmDetails::IOSAL {
707 unit,
708 operand: None,
709 message: format!("unit {unit:o} is not known"),
710 },
711 },
712 diagnostics,
713 )?;
714 Ok(make_report_word_for_invalid_unit(unit, current_flag))
716 }
717 }
718 }
719
720 pub(super) fn poll(
721 &mut self,
722 ctx: &Context,
723 alarm_unit: &mut AlarmUnit,
724 ) -> Result<(u64, Option<Alarm>, Option<Duration>), Alarm> {
725 let system_time = &ctx.simulated_time;
726 let mut raised_flags: u64 = 0;
727 let mut alarm: Option<Alarm> = None;
728 let mut next_poll: Option<Duration> = None;
729
730 event!(Level::DEBUG, "poll_queue is: {:?}", &self.poll_queue);
731 loop {
732 match self.poll_queue.peek() {
733 None => {
734 break;
735 }
736 Some((_, poll_time)) => {
737 if poll_time > system_time {
738 event!(
740 Level::TRACE,
741 "poll: next poll is not due yet; due={:?}, now={:?}",
742 poll_time,
743 system_time
744 );
745 next_poll = Some(*poll_time);
746 break;
747 }
748 }
749 }
750 match self.poll_queue.pop() {
751 None => unreachable!(),
752 Some((devno, poll_time)) => {
753 let span = span!(Level::ERROR, "poll", unit=%devno);
754 let _enter = span.enter();
755
756 self.mark_device_changed(devno);
762
763 event!(
764 Level::DEBUG,
765 "poll: next poll is now due for unit {devno:o}; due={:?}, now={:?}",
766 poll_time,
767 system_time
768 );
769 assert!(poll_time <= *system_time);
770
771 let attached = match self.devices.get_mut(&devno) {
772 Some(attached) => attached,
773 None => {
774 event!(
775 Level::ERROR,
776 "Device {:?} is present in the polling queue but not in the device map; ignoring it",
777 devno
778 );
779 continue;
780 }
781 };
782 if !attached.connected {
783 event!(Level::TRACE, "not polling unit. it's not connected");
784 continue;
785 }
786 assert!(!attached.in_maintenance); event!(
788 Level::TRACE,
789 "polling unit at system time {:?}",
790 system_time
791 );
792 let unit_status = attached.poll(ctx, alarm_unit)?;
793 event!(Level::TRACE, "unit {devno:02o} status is {unit_status:?}");
794 self.poll_queue.push(devno, unit_status.poll_after);
795 if let Some(FlagChange::Raise(reason)) = unit_status.change_flag {
796 event!(
797 Level::DEBUG,
798 "unit {devno:02o} has raised its flag: {reason}"
799 );
800 raised_flags |= 1 << u8::from(devno);
801 }
802 if alarm.is_none() {
803 if unit_status.inability {
807 alarm = Some(Alarm {
808 sequence: Some(devno),
809 details: AlarmDetails::IOSAL {
810 unit: devno,
811 operand: None,
812 message: format!("unit {devno:o} reports inability (EIA)"),
813 },
814 });
815 } else if unit_status.missed_data {
816 alarm = Some(Alarm {
817 sequence: None,
818 details: AlarmDetails::MISAL {
819 affected_unit: devno,
820 },
821 });
822 }
823 }
824 }
825 }
826 }
827 Ok((raised_flags, alarm, next_poll))
828 }
829
830 pub fn disconnect_all<A: Alarmer>(
831 &mut self,
832 ctx: &Context,
833 alarmer: &mut A,
834 ) -> Result<(), Alarm> {
835 let mut changes: Vec<Unsigned6Bit> = Vec::with_capacity(self.devices.len());
836 for attached in self.devices.values_mut() {
837 if attached.connected {
838 changes.push(attached.unit);
839 attached.disconnect(ctx, alarmer)?;
840 attached.connected = false;
841 }
842 }
843 for unit in changes.into_iter() {
844 self.mark_device_changed(unit);
845 }
846 Ok(())
847 }
848
849 pub fn disconnect(
850 &mut self,
851 ctx: &Context,
852 device: &Unsigned6Bit,
853 alarm_unit: &mut AlarmUnit,
854 diagnostics: &CurrentInstructionDiagnostics,
855 ) -> Result<(), Alarm> {
856 let mut changed = false;
857 if *device == u6!(0o42) {
858 return Ok(());
859 }
860 let result = match self.devices.get_mut(device) {
861 Some(attached) => {
862 if attached.connected {
863 attached.connected = false;
864 } else {
865 event!(
866 Level::WARN,
867 "disconnecting unit {device:o} but it is not connected"
868 );
869 }
870 changed = true;
871 attached.disconnect(ctx, alarm_unit)
872 }
873 None => {
874 alarm_unit.fire_if_not_masked(
875 Alarm {
876 sequence: Some(*device),
877 details: AlarmDetails::IOSAL {
878 unit: *device,
879 operand: None,
880 message: format!("Attempt to disconnect missing unit {device:o}"),
881 },
882 },
883 diagnostics,
884 )?;
885 Ok(()) }
887 };
888 if changed {
889 self.mark_device_changed(*device);
890 }
891 result
892 }
893
894 pub fn connect(
895 &mut self,
896 ctx: &Context,
897 calling_sequence: Option<SequenceNumber>,
898 device: &Unsigned6Bit,
899 mode: Unsigned12Bit,
900 alarm_unit: &mut AlarmUnit,
901 diagnostics: &CurrentInstructionDiagnostics,
902 ) -> Result<Option<FlagChange>, Alarm> {
903 self.mark_device_changed(*device);
904 match self.devices.get_mut(device) {
905 Some(attached) => {
906 if attached.in_maintenance {
907 event!(
908 Level::INFO,
909 "attempt to connect in-maintenance unit {device:o}, raising IOSAL"
910 );
911 Err(Alarm {
912 sequence: Some(*device),
913 details: AlarmDetails::IOSAL {
914 unit: *device,
915 operand: None,
916 message: format!("Attempt to connect in-maint unit {device:o}"),
917 },
918 })
919 } else {
920 event!(Level::DEBUG, "connecting unit {device:o}");
921 let flag_change = if attached.is_disconnected_output_unit() {
925 event!(
926 Level::DEBUG,
927 "Connecting previously-unconnected output unit {device:o}, so raising its flag"
928 );
929 Some(FlagChange::Raise(
930 "attaching a previously-disconnected output unit",
931 ))
932 } else {
933 None
934 };
935 attached.connect(ctx, mode);
936 attached.connected = true;
937 Ok(flag_change)
938 }
939 }
940 None => {
941 event!(
942 Level::WARN,
943 "attempt to connect nonexistent unit {device:o}"
944 );
945 alarm_unit.fire_if_not_masked(
946 Alarm {
947 sequence: calling_sequence, details: AlarmDetails::IOSAL {
949 unit: *device,
950 operand: None,
951 message: format!("Attempt to connect missing unit {device:o}"),
952 },
953 },
954 diagnostics,
955 )?;
956 Ok(None) }
958 }
959 }
960
961 pub fn mark_device_changed(&mut self, unit: Unsigned6Bit) {
962 self.changes.add(unit);
963 }
964
965 pub fn drain_changes(
966 &mut self,
967 ctx: &Context,
968 control: &mut ControlUnit,
969 ) -> Result<BTreeMap<Unsigned6Bit, ExtendedUnitState>, Alarm> {
970 let mut result: BTreeMap<Unsigned6Bit, ExtendedUnitState> = BTreeMap::new();
971 for unit_with_change in self.changes.drain().into_iter() {
972 if let Some(attached_unit) = self.get(&unit_with_change) {
973 let xreg_value = match control
974 .inspect_registers()
975 .index_regs
976 .get(usize::from(unit_with_change))
977 {
978 Some(xreg_value) => xreg_value,
979 None => {
980 unreachable!("There is no index register for unit {unit_with_change:o}");
984 }
985 };
986 match self.get_extended_status(ctx, attached_unit, control, *xreg_value) {
987 Ok(state) => {
988 result.insert(unit_with_change, state);
989 }
990 Err(alarm) => {
991 return Err(alarm);
992 }
993 }
994 }
995 }
996 Ok(result)
997 }
998}
999
1000impl Default for DeviceManager {
1001 fn default() -> DeviceManager {
1003 Self::new()
1004 }
1005}
1006
1007pub fn set_up_peripherals(ctx: &Context, devices: &mut DeviceManager) {
1008 const NOT_IN_MAINTENANCE: bool = false;
1009 fn attach_lw(
1010 ctx: &Context,
1011 input_unit: UnitType,
1012 output_unit: UnitType,
1013 devices: &mut DeviceManager,
1014 ) {
1015 let state = Rc::new(RefCell::new(LincolnState::default()));
1016 let output = Box::new(LincolnWriterOutput::new(
1017 output_unit.sequence(),
1018 state.clone(),
1019 ));
1020 let input = Box::new(LincolnWriterInput::new(input_unit.sequence(), state));
1021 devices.attach(ctx, output_unit, NOT_IN_MAINTENANCE, output);
1022 devices.attach(ctx, input_unit, NOT_IN_MAINTENANCE, input);
1023 }
1024
1025 devices.attach(
1026 ctx,
1027 UnitType::from(PETR),
1028 NOT_IN_MAINTENANCE,
1029 Box::new(Petr::new()),
1030 );
1031 attach_lw(
1032 ctx,
1033 UnitType::from(u6!(0o65)),
1034 UnitType::from(u6!(0o66)),
1035 devices,
1036 );
1037}