1+ use embassy_time:: { Duration , Instant } ;
12use embedded_graphics:: {
23 mono_font:: { ascii, MonoFont , MonoTextStyle } ,
34 pixelcolor:: BinaryColor ,
45 prelude:: { DrawTarget , Point , Primitive , Size } ,
5- primitives:: { Line , Polyline , PrimitiveStyle , Rectangle , RoundedRectangle , StyledDrawable } ,
6+ primitives:: {
7+ Line , Polyline , PrimitiveStyle , PrimitiveStyleBuilder , Rectangle , RoundedRectangle ,
8+ StrokeAlignment , StyledDrawable ,
9+ } ,
610 text:: { Alignment , Baseline , Text , TextStyle , TextStyleBuilder } ,
711 Drawable ,
812} ;
@@ -11,10 +15,13 @@ use esp_hal::{
1115 i2c:: master:: I2c ,
1216 Blocking ,
1317} ;
18+ use log:: { error, info} ;
1419use ssd1306:: {
1520 mode:: BufferedGraphicsMode , prelude:: I2CInterface , size:: DisplaySize128x64 , Ssd1306 ,
1621} ;
1722
23+ use crate :: meeting_instruction:: ProgressRatio ;
24+
1825type DisplayType = Ssd1306 <
1926 I2CInterface < I2c < ' static , Blocking > > ,
2027 DisplaySize128x64 ,
@@ -25,6 +32,7 @@ pub struct ControlPanelState {
2532 pub usb_power_1 : Output < ' static > ,
2633 pub usb_power_2 : Output < ' static > ,
2734 pub meeting_sign_power : Output < ' static > ,
35+ pub meeting_sign_completion : Option < Instant > ,
2836 pub ui_selection_mode : UISelectionMode ,
2937 pub ui_section : UISection ,
3038 pub display : DisplayType ,
@@ -35,6 +43,9 @@ pub enum MovementDirection {
3543 CounterClockwise ,
3644}
3745impl ControlPanelState {
46+ const MEETING_SIGN_INTERVAL : Duration = Duration :: from_secs ( 60 * 5 ) ;
47+ const MEETING_SIGN_MAX_DURATION : Duration = Duration :: from_secs ( 60 * 120 ) ;
48+
3849 pub fn rotary_encoder_rotate ( & mut self , direction : MovementDirection ) {
3950 match self . ui_selection_mode {
4051 UISelectionMode :: Menu => {
@@ -58,8 +69,91 @@ impl ControlPanelState {
5869 . unwrap ( ) ;
5970 }
6071 UISection :: MeetingSign => match direction {
61- MovementDirection :: Clockwise => todo ! ( ) ,
62- MovementDirection :: CounterClockwise => todo ! ( ) ,
72+ MovementDirection :: Clockwise => {
73+ match self . meeting_sign_power . output_level ( ) {
74+ Level :: High => {
75+ info ! ( "Meeting Sign is already running" ) ;
76+ match self . meeting_sign_completion {
77+ None => {
78+ self . meeting_sign_power . set_high ( ) ;
79+ self . meeting_sign_completion =
80+ Some ( Instant :: now ( ) + Self :: MEETING_SIGN_INTERVAL ) ;
81+ error ! (
82+ "Meeting sign completion is None, but power is high."
83+ ) ;
84+ }
85+ Some ( instant) => {
86+ if instant < Instant :: now ( ) {
87+ self . meeting_sign_power . set_high ( ) ;
88+ self . meeting_sign_completion =
89+ Some ( Instant :: now ( ) + Self :: MEETING_SIGN_INTERVAL ) ;
90+ error ! ( "Meeting sign completion was before NOW." ) ;
91+ } else if instant + Self :: MEETING_SIGN_INTERVAL
92+ < Instant :: now ( ) + Self :: MEETING_SIGN_MAX_DURATION
93+ {
94+ self . meeting_sign_power . set_high ( ) ;
95+ self . meeting_sign_completion =
96+ Some ( instant + Self :: MEETING_SIGN_INTERVAL ) ;
97+ info ! (
98+ "Meeting sign timer increased by {}s." ,
99+ Self :: MEETING_SIGN_INTERVAL . as_secs( )
100+ ) ;
101+ } else {
102+ info ! ( "Meeting sign timer would exceed max duration, not increasing." ) ;
103+ }
104+ }
105+ }
106+ }
107+ Level :: Low => {
108+ self . meeting_sign_power . set_high ( ) ;
109+ self . meeting_sign_completion =
110+ Some ( Instant :: now ( ) + Self :: MEETING_SIGN_INTERVAL ) ;
111+ info ! ( "Meeting Sign turned on" ) ;
112+ info ! ( "Meeting Sign timer started" ) ;
113+ }
114+ } ;
115+ self . check_meeting_sign_timer ( ) . unwrap ( ) ;
116+ }
117+ MovementDirection :: CounterClockwise => {
118+ match self . meeting_sign_power . output_level ( ) {
119+ Level :: High => {
120+ info ! ( "Meeting Sign is already running" ) ;
121+ match self . meeting_sign_completion {
122+ None => {
123+ self . meeting_sign_power . set_low ( ) ;
124+ error ! (
125+ "Meeting sign completion is None, but power is high."
126+ ) ;
127+ }
128+ Some ( instant) => {
129+ if instant < Instant :: now ( ) {
130+ self . meeting_sign_power . set_low ( ) ;
131+ error ! ( "Meeting sign completion was before NOW, but power is high." ) ;
132+ } else if instant - Self :: MEETING_SIGN_INTERVAL
133+ < Instant :: now ( )
134+ {
135+ self . meeting_sign_power . set_low ( ) ;
136+ self . meeting_sign_completion = None ;
137+ info ! (
138+ "Meeting sign turned off and completion set to None."
139+ ) ;
140+ } else {
141+ self . meeting_sign_completion =
142+ Some ( instant - Self :: MEETING_SIGN_INTERVAL ) ;
143+ info ! (
144+ "Decreasing Meeting Sign timer by {}s." ,
145+ Self :: MEETING_SIGN_INTERVAL . as_secs( )
146+ ) ;
147+ }
148+ }
149+ }
150+ self . check_meeting_sign_timer ( ) . unwrap ( ) ;
151+ }
152+ Level :: Low => {
153+ info ! ( "Meeting sign is already off" ) ;
154+ }
155+ } ;
156+ }
63157 } ,
64158 } ,
65159 } ;
@@ -138,6 +232,42 @@ impl ControlPanelState {
138232 Ok ( ( ) )
139233 }
140234
235+ pub fn check_meeting_sign_timer ( & mut self ) -> Result < ( ) , <DisplayType as DrawTarget >:: Error > {
236+ match (
237+ self . meeting_sign_power . output_level ( ) ,
238+ self . meeting_sign_completion ,
239+ ) {
240+ ( Level :: Low , None ) => { }
241+ ( Level :: Low , Some ( _) ) => {
242+ error ! ( "Meeting Sign is not on, but completion is set to something" ) ;
243+ }
244+ ( Level :: High , None ) => {
245+ error ! ( "Meeting Sign is on, but completion is None" ) ;
246+ // This should not happen, but if it does, we can reset the state
247+ self . meeting_sign_power . set_low ( ) ;
248+ self . meeting_sign_completion = None ;
249+ }
250+ ( Level :: High , Some ( instant) ) => {
251+ if instant < Instant :: now ( ) {
252+ self . meeting_sign_power . set_low ( ) ;
253+ self . meeting_sign_completion = None ;
254+ info ! ( "Meeting Sign timer has completed" ) ;
255+ MeetingSignUI . draw_progress ( & mut self . display , ProgressRatio ( 0 ) ) ?;
256+ } else {
257+ let ratio = ProgressRatio :: from_durations (
258+ & ( instant - Instant :: now ( ) ) ,
259+ & Self :: MEETING_SIGN_MAX_DURATION ,
260+ )
261+ . unwrap ( ) ;
262+ info ! ( "Meeting Sign timer is running with ratio={:?}" , ratio) ;
263+ MeetingSignUI . draw_progress ( & mut self . display , ratio) ?;
264+ }
265+ }
266+ } ;
267+
268+ Ok ( ( ) )
269+ }
270+
141271 pub fn draw_entire_ui ( & mut self ) -> Result < ( ) , <DisplayType as DrawTarget >:: Error > {
142272 self . draw_border_ui ( ) ?;
143273 self . usb_switch_state . draw_entire_ui ( & mut self . display ) ?;
@@ -481,18 +611,28 @@ impl MeetingSignUI {
481611 Self :: CENTER_ALIGNED ,
482612 ) ;
483613
484- pub const BORDER_ON_STYLE : PrimitiveStyle < BinaryColor > =
485- PrimitiveStyle :: with_stroke ( BinaryColor :: On , 1 ) ;
486- pub const BORDER_OFF_STYLE : PrimitiveStyle < BinaryColor > =
487- PrimitiveStyle :: with_stroke ( BinaryColor :: Off , 1 ) ;
614+ pub const BORDER_ON_STYLE : PrimitiveStyle < BinaryColor > = PrimitiveStyleBuilder :: new ( )
615+ . stroke_color ( BinaryColor :: On )
616+ . stroke_width ( 1 )
617+ . stroke_alignment ( StrokeAlignment :: Outside )
618+ . build ( ) ;
619+ pub const BORDER_OFF_STYLE : PrimitiveStyle < BinaryColor > = PrimitiveStyleBuilder :: new ( )
620+ . stroke_color ( BinaryColor :: Off )
621+ . stroke_width ( 1 )
622+ . stroke_alignment ( StrokeAlignment :: Outside )
623+ . build ( ) ;
624+
488625 const PROGRESS_SIZE : Size = Size :: new ( UISection :: MEETING_SIGN_SIZE . width - 20 , 12 ) ;
626+ const PROGRESS_X : i32 = Self :: MIDDLE_X - ( Self :: PROGRESS_SIZE . width as i32 / 2 ) ;
627+ const PROGRESS_Y : i32 = 25 ;
628+ const PROGRESS_PT : Point = Point :: new ( Self :: PROGRESS_X , Self :: PROGRESS_Y ) ;
629+ const PROGRESS_CORNER_RADIUS : Size = Size :: new ( 3 , 3 ) ;
489630 pub const PROGRESS_OUTLINE : RoundedRectangle = RoundedRectangle :: with_equal_corners (
490- Rectangle :: new (
491- Point :: new ( Self :: MIDDLE_X - ( Self :: PROGRESS_SIZE . width as i32 / 2 ) , 25 ) ,
492- Self :: PROGRESS_SIZE ,
493- ) ,
494- Size :: new ( 3 , 3 ) ,
631+ Rectangle :: new ( Self :: PROGRESS_PT , Self :: PROGRESS_SIZE ) ,
632+ Self :: PROGRESS_CORNER_RADIUS ,
495633 ) ;
634+ pub const BAR_ON_STYLE : PrimitiveStyle < BinaryColor > =
635+ PrimitiveStyle :: with_fill ( BinaryColor :: On ) ;
496636
497637 pub fn draw < D : DrawTarget < Color = BinaryColor > > (
498638 & self ,
@@ -506,4 +646,25 @@ impl MeetingSignUI {
506646 Self :: PROGRESS_OUTLINE . draw_styled ( & style, target) ?;
507647 Ok ( ( ) )
508648 }
649+
650+ pub fn draw_progress < D : DrawTarget < Color = BinaryColor > > (
651+ & self ,
652+ target : & mut D ,
653+ ratio : ProgressRatio ,
654+ ) -> Result < ( ) , D :: Error > {
655+ let width = ratio. apply_to ( Self :: PROGRESS_SIZE . width as usize ) as u32 ;
656+
657+ Self :: PROGRESS_OUTLINE . draw_styled ( & PrimitiveStyle :: with_fill ( BinaryColor :: Off ) , target) ?;
658+
659+ RoundedRectangle :: with_equal_corners (
660+ Rectangle :: new (
661+ Self :: PROGRESS_PT ,
662+ Size :: new ( width, Self :: PROGRESS_SIZE . height ) ,
663+ ) ,
664+ Self :: PROGRESS_CORNER_RADIUS ,
665+ )
666+ . draw_styled ( & Self :: BAR_ON_STYLE , target) ?;
667+
668+ Ok ( ( ) )
669+ }
509670}
0 commit comments