For the fun of it, I have described the digit system using all what Ada is good for readability 😎
- a
Display
of 4 ofDigits
- each
Digits
may have 10 different values (0
..9
) - each
Digit
is made of 7Segments
notedA
throughG
, with predefined values. - Display has a specific connection (squared) matrix
-- Ada pseudo code
type Display is array (1 .. 4) of Digit;
subtype Digit_Values is Natural range 0 .. 9;
type Digit is record
Value : Digit_Values;
Segments : Segment_array;
Consumption : Current;
end record;
type Segment_array is array (A .. G) of Status (off, on);
type Segment_IDs is (A, B, C, D, E, F, G);
Digit_0 : constant Digit := (
Value => 0,
Segments => (
A => ON,
B => ON,
C => ON,
D => OFF,
E => ON,
F => ON,
G => ON
),
Consumption => 6
);
(...)
type Connection_Matrix is array (Segment_IDs, Segment_IDs) of Boolean;
A line will be read in 3 operations:
- 10
signal patterns
(length based on actual length of the combined 10 digits (segments) = 60) - delimiter
|
- the 4
digit output
values.
Each kind of object will have his own 'Read
procedure. see § Stream-Oriented Attributes
To read
the different [a-g]+
patterns having different lengths, a specific object Pattern
is defined in package SevenSegment.Digit_Device_IO
:
type Pattern is record
Length : Natural;
Segments : Segment_array := (others => OFF);
end record;
Using Ada standard build-in Stream
features, and Stream attributes
, decoding the data file is elegant and readable.
procedure Read_Pattern
(Stream : not null access Root_Stream_Type'Class;
Item : out Pattern);
for Pattern'Read use Read_Pattern;
procedure Read_Segment_ID_With_Delimiter
(Stream : not null access Root_Stream_Type'Class;
Item : out Segment_ID_with_Delimiter);
for Segment_ID_with_Delimiter'Read use Read_Segment_ID_With_Delimiter;
To cope with the end of line not having a usable pattern, basic Stream_IO
is prefered over Text_Streams
.
Stream_IO
do not eat the CR/LF
characters which can be used as delimiter(s) of the last [a-g]+
pattern.
Using some of the best Ada goodies ❤️ : Unchecked Conversion
Usage:
-- build corresponding segments of some Digit `S`
for ID in Segment_IDs loop
if (To_Binary (Matrix (ID)) and To_Binary (This.Segments)) /= 0 then
S (ID) := ON;
end if;
end loop;
Definition:
function To_Binary
(S : Segment_array)
return Segment_Binary
is
function Binary is new Ada.Unchecked_Conversion
(Source => Segment_array,
Target => Segment_Binary);
begin
return Binary (S);
end To_Binary;
Using some of the best Ada goodies ❤️ : Logical operations on arrays of booleans
PV_1, PV_4, PV_069 : Segment_array;
(...)
Matrix (B) := (PV_4 xor PV_1) and PV_069;
- Store any
[a-g]+
pattern in a (sorted) arraySignal_Pattern
Signal_Pattern (D) := Some_Pattern;
- Define for each occurence a
Digit_Matrix
- Compute the right combination of segment attribution
Match_Connections (Digit_Matrix, Signal_Pattern);
- Store any output pattern
[a-g]+
in an arrayOutput_Value (D) := Some_Pattern;
- Find out which digit value corresponds to a given segment combination
Connect (Some_Pattern, Digit_Matrix);
-- Compute the matrix
Matrix (F) := PV_1 and PV_069;
Matrix (C) := Matrix (F) xor PV_1;
Matrix (A) := PV_7 and PV_235;
Matrix (B) := (PV_4 xor PV_1) and PV_069;
Matrix (D) := (PV_4 xor PV_1) and PV_235;
Matrix (G) := ((PV_235 and PV_069) xor PV_7) xor PV_1;
Matrix (E) := PV_8 xor (PV_4 or PV_069);
For the fun of it (and to illustrate), you will find some usage of Ada Exception mecanism.
Two Check
procedures verifies the (theorical) health of each Segment
.
They verify the correct number of lit segments compared to the predefined current.
SEGMENT_IN_DIGIT_FAILURE
exception could raise, and trigger DISPLAY_FAILURE
exception.