#[macro_use] extern crate arrayref; use std::error::Error; use std::thread; use std::time::Duration; use std::f32::consts::PI; use chrono::{DateTime, Utc}; use chrono_tz::Tz; use rusttype::Font; #[macro_use] extern crate log; use log4rs; use image::{ Rgb, RgbImage }; use imageproc::rect::Rect; use imageproc::drawing::draw_filled_rect_mut; mod ilidisplay; mod forms; mod signalk; mod vesseldata; use vesseldata::VesselDataEventSource; pub struct HelmsDisplay { font: Box>, boat: forms::Form, compassrose: forms::Form, cog: forms::Form, wind: forms::Form, gps_screen: forms::Screen, time_screen: forms::Screen, course_screen: forms::Screen, } impl HelmsDisplay { pub fn new() -> HelmsDisplay { // in the future, this method should probably take some sort of // configuration object. let mut loader = forms::Loader::new("/root/helms-display".to_string()); let boat = loader.load_form("boat.svg").unwrap(); let compassrose = loader.load_form("compass-rose.svg").unwrap(); let cog = loader.load_form("cog.svg").unwrap(); let wind = loader.load_form("wind.svg").unwrap(); let f = loader.load_font("font.ttf").unwrap(); let font = Box::new(f); HelmsDisplay { font: font, boat: boat, compassrose: compassrose, cog: cog, wind: wind, gps_screen: forms::Screen::new(400, 40), time_screen: forms::Screen::new(200, 60), course_screen: forms::Screen::new(200, 200), } } pub fn render_gps (&mut self, loc: Option) { self.gps_screen.clear(); match loc { None => self.gps_screen.text_c(&self.font, " --\u{00B0}--\u{2032}--\u{2033} --\u{00B0}--\u{2032}--\u{2033}", 32.0, 200, 5), Some(vesseldata::VesselDataEvent::Location(lat,lon)) => { // format with unicodes for degrees, minutes and seconds. let (latitude, lat_ew) = { if lat < 0.0 { (-lat, 'W') } else if lat > 0.0 { (lat, 'E') } else { (0.0, '-') } }; let (longitude, long_ns) = { if lon < 0.0 { (-lon, 'S') } else if lon > 0.0 { (lon, 'N') } else { (0.0, '-') } }; let long_d: u8 = longitude.trunc() as u8; let longitude = longitude.fract() * 60.0; let long_m: u8 = longitude.trunc() as u8; let longitude = longitude.fract() * 60.0; let long_s: u8 = longitude.round() as u8; let lat_d: u8 = latitude.trunc() as u8; let latitude = latitude.fract() * 60.0; let lat_m: u8 = latitude.trunc() as u8; let latitude = latitude.fract() * 60.0; let lat_s: u8 = latitude.round() as u8; self.gps_screen.text_c(&self.font, format!("{}{:02}\u{00B0}{:02}\u{2032}{:02}\u{2033} {}{:02}\u{00B0}{:02}\u{2032}{:02}\u{2033}", long_ns, long_d, long_m, long_s, lat_ew, lat_d, lat_m, lat_s).as_str(), 32.0, 200, 5); }, _ => (), } } pub fn render_time (&mut self) { let tz: Tz = "Europe/Copenhagen".parse().unwrap(); let now = Utc::now().with_timezone(&tz); self.time_screen.text_c(&self.font, now.format("%H:%M:%S %Z").to_string().as_str(), 24.0, 100, 1); self.time_screen.text_c(&self.font, "Europe/Copenhagen", 16.0, 100, 25); self.time_screen.text_c(&self.font, now.format("%Y-%m-%d").to_string().as_str(), 16.0, 100, 43); } pub fn render_course (&mut self, cog: Option, magnetic: Option, wind: Option) { let rad = (6 as f32) * 2.0*PI / 100.0; self.course_screen.clear(); // render course over ground if present match cog { Some(vesseldata::VesselDataEvent::CourseOverGround(c)) => { self.course_screen.render(&self.cog, c, 500, 500); }, _ => (), }; // render wind if present match wind { Some(vesseldata::VesselDataEvent::WindOrigin(o)) => { self.course_screen.render(&self.wind, o, 500, 500); }, _ => (), }; // render compass rose self.course_screen.render(&self.compassrose, rad, 500, 500); self.course_screen.render(&self.boat, 0.0, 500, 500); } } fn main() -> Result<(), Box> { // init log library log4rs::init_file("log4rs.yml", Default::default()).unwrap(); let mut course_screen = forms::Screen::new(200, 200); let mut sog_screen = forms::Screen::new(160,60); let mut time_screen = forms::Screen::new(200, 60); let mut loader = forms::Loader::new("/root/helms-display".to_string()); let c = loader.load_form("compass-rose.svg").unwrap(); let b = loader.load_form("boat.svg").unwrap(); let cog = loader.load_form("cog.svg").unwrap(); let wind = loader.load_form("wind.svg").unwrap(); //let font = loader.load_font("font.ttf").unwrap(); let f2 = loader.load_font("font.ttf").unwrap(); let mut helms = HelmsDisplay::new(); /* let mut img = RgbImage::new(480, 320); let thickness = 10; draw_filled_rect_mut(&mut img, Rect::at(0,0).of_size(240, 160), Rgb([255,0,0])); draw_filled_rect_mut(&mut img, Rect::at(0,160).of_size(240, 160), Rgb([0, 255, 0])); draw_filled_rect_mut(&mut img, Rect::at(240,0).of_size(240, 160), Rgb([0, 0, 255])); */ let mut e = ilidisplay::IliDisplay::init()?; e.init_chip(); e.turn_on(); course_screen.clear(); let rad = (6 as f32) * 2.0*PI / 100.0; course_screen.clear(); course_screen.render(&cog, -rad*2.0, 500, 500); course_screen.render(&wind, rad*2.0, 500, 500); course_screen.render(&c, rad, 500, 500); course_screen.render(&b, 0.0, 500, 500); e.put_image(&(course_screen.image), (140, (160-110))); /* sog_screen.clear(); sog_screen.text(&font, "SOG", 32.0, 5, 5); sog_screen.text(&font, "speed over ground", 12.0, 5, 38); let speed_over_ground = 5.0; // in m/s let speed_over_ground = speed_over_ground * 1.9438612860586; // now in nautic miles per hour sog_screen.text_rj(&font, format!("{:.1}", speed_over_ground).as_str(), 32.0, 138, 5); sog_screen.fraction(&font, "nm", "h", 14.0, 140, 6); e.put_image(&(sog_screen.image), (0, 50)); */ helms.render_gps(None); e.put_image(&(helms.gps_screen.image), (40,0)); helms.render_time(); e.put_image(&(helms.time_screen.image), (140,260)); println!("Display has been rendered now, sleeping for 5s"); let vd = signalk::SignalK::connect(); thread::sleep(Duration::from_millis(15000)); e.turn_off(); Ok(()) }