mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-15 08:30:02 +00:00
foramt services/dns
This commit is contained in:
parent
8ef2d2d8f2
commit
e096384f66
6 changed files with 332 additions and 320 deletions
|
|
@ -19,7 +19,8 @@ pub(crate) enum Opcode {
|
|||
/// used internally to update the TTL field, and eventually expire the cache (unless cache is frozen)
|
||||
UpdateTtl = 2,
|
||||
|
||||
/// issuing this opcode causes all future attempts to change the DNS server configs to be ignored. This also freezes the cache.
|
||||
/// issuing this opcode causes all future attempts to change the DNS server configs to be ignored. This
|
||||
/// also freezes the cache.
|
||||
FreezeConfig = 3,
|
||||
|
||||
/// this allows automatic updates to the DNS server configs based on DHCP. This is the default state.
|
||||
|
|
@ -52,14 +53,7 @@ pub(crate) enum Opcode {
|
|||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
num_derive::FromPrimitive,
|
||||
num_derive::ToPrimitive,
|
||||
Archive,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Copy,
|
||||
Clone,
|
||||
Debug, num_derive::FromPrimitive, num_derive::ToPrimitive, Archive, Serialize, Deserialize, Copy, Clone,
|
||||
)]
|
||||
#[repr(u16)]
|
||||
pub enum DnsResponseCode {
|
||||
|
|
|
|||
|
|
@ -1,39 +1,37 @@
|
|||
use net::NetIpAddr;
|
||||
use std::net::ToSocketAddrs;
|
||||
|
||||
use net::NetIpAddr;
|
||||
|
||||
use crate::DnsResponseCode;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Dns {
|
||||
}
|
||||
pub struct Dns {}
|
||||
impl Dns {
|
||||
pub fn new(_xns: &xous_names::XousNames) -> Result<Self, xous::Error> {
|
||||
Ok(Dns {
|
||||
})
|
||||
}
|
||||
pub fn new(_xns: &xous_names::XousNames) -> Result<Self, xous::Error> { Ok(Dns {}) }
|
||||
|
||||
/// Checks first to see if the name could be just an IPv4 or IPv6 in string form,
|
||||
/// then tries to pass it to the DNS resolver.
|
||||
pub fn lookup(&self, name: &str) -> Result<NetIpAddr, DnsResponseCode> {
|
||||
log::debug!("looking up {}", name);
|
||||
match (name, 80).to_socket_addrs() { // we throw away the port because we just want the IP address...
|
||||
Ok(mut iter) => {
|
||||
match iter.next() {
|
||||
Some(addr) => {
|
||||
log::debug!("{:?}", addr);
|
||||
Ok(NetIpAddr::from(addr))
|
||||
},
|
||||
None => {
|
||||
log::debug!("name error");
|
||||
Err(DnsResponseCode::NameError)
|
||||
}
|
||||
match (name, 80).to_socket_addrs() {
|
||||
// we throw away the port because we just want the IP address...
|
||||
Ok(mut iter) => match iter.next() {
|
||||
Some(addr) => {
|
||||
log::debug!("{:?}", addr);
|
||||
Ok(NetIpAddr::from(addr))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
log::debug!("name error");
|
||||
Err(DnsResponseCode::NameError)
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::debug!("format error: {:?}", e);
|
||||
Err(DnsResponseCode::FormatError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flush_cache(&self) -> Result<(), xous::Error> {
|
||||
log::warn!("DNS cache flush not implemented in hosted mode!");
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
#![cfg_attr(target_os = "none", no_std)]
|
||||
use xous::CID;
|
||||
use xous_ipc::{Buffer, String};
|
||||
use num_traits::ToPrimitive;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use net::NetIpAddr;
|
||||
use std::net::IpAddr;
|
||||
use num_traits::ToPrimitive;
|
||||
use xous::CID;
|
||||
use xous_ipc::{Buffer, String};
|
||||
|
||||
use crate::api::*;
|
||||
|
||||
|
|
@ -15,10 +15,10 @@ pub struct Dns {
|
|||
impl Dns {
|
||||
pub fn new(xns: &xous_names::XousNames) -> Result<Self, xous::Error> {
|
||||
REFCOUNT.fetch_add(1, Ordering::Relaxed);
|
||||
let conn = xns.request_connection_blocking(crate::api::SERVER_NAME_DNS).expect("Can't connect to Dns server");
|
||||
Ok(Dns {
|
||||
conn
|
||||
})
|
||||
let conn = xns
|
||||
.request_connection_blocking(crate::api::SERVER_NAME_DNS)
|
||||
.expect("Can't connect to Dns server");
|
||||
Ok(Dns { conn })
|
||||
}
|
||||
|
||||
/// Checks first to see if the name could be just an IPv4 or IPv6 in string form,
|
||||
|
|
@ -31,19 +31,17 @@ impl Dns {
|
|||
let mut buf = Buffer::into_buf(alloc_name).or(Err(DnsResponseCode::UnknownError))?;
|
||||
buf.lend_mut(self.conn, Opcode::Lookup.to_u32().unwrap())
|
||||
.or(Err(DnsResponseCode::UnknownError))?;
|
||||
let response = buf.to_original::<DnsResponse,_>().or(Err(DnsResponseCode::UnknownError))?;
|
||||
if let Some(addr) = response.addr {
|
||||
Ok(addr)
|
||||
} else {
|
||||
Err(response.code)
|
||||
}
|
||||
let response = buf.to_original::<DnsResponse, _>().or(Err(DnsResponseCode::UnknownError))?;
|
||||
if let Some(addr) = response.addr { Ok(addr) } else { Err(response.code) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flush_cache(&self) -> Result<(), xous::Error> {
|
||||
xous::send_message(
|
||||
self.conn,
|
||||
xous::Message::new_scalar(Opcode::Flush.to_usize().unwrap(), 0, 0, 0, 0)
|
||||
).map(|_| ())
|
||||
xous::Message::new_scalar(Opcode::Flush.to_usize().unwrap(), 0, 0, 0, 0),
|
||||
)
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -51,13 +49,17 @@ use core::sync::atomic::{AtomicU32, Ordering};
|
|||
static REFCOUNT: AtomicU32 = AtomicU32::new(0);
|
||||
impl Drop for Dns {
|
||||
fn drop(&mut self) {
|
||||
// the connection to the server side must be reference counted, so that multiple instances of this object within
|
||||
// a single process do not end up de-allocating the CID on other threads before they go out of scope.
|
||||
// Note to future me: you want this. Don't get rid of it because you think, "nah, nobody will ever make more than one copy of this object".
|
||||
// the connection to the server side must be reference counted, so that multiple instances of this
|
||||
// object within a single process do not end up de-allocating the CID on other threads before
|
||||
// they go out of scope. Note to future me: you want this. Don't get rid of it because you
|
||||
// think, "nah, nobody will ever make more than one copy of this object".
|
||||
if REFCOUNT.fetch_sub(1, Ordering::Relaxed) == 1 {
|
||||
unsafe{xous::disconnect(self.conn).unwrap();}
|
||||
unsafe {
|
||||
xous::disconnect(self.conn).unwrap();
|
||||
}
|
||||
}
|
||||
// if there was object-specific state (such as a one-time use server for async callbacks, specific to the object instance),
|
||||
// de-allocate those items here. They don't need a reference count because they are object-specific
|
||||
// if there was object-specific state (such as a one-time use server for async callbacks, specific to
|
||||
// the object instance), de-allocate those items here. They don't need a reference count
|
||||
// because they are object-specific
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ pub mod api;
|
|||
#[allow(unused_imports)]
|
||||
pub use api::*;
|
||||
|
||||
#[cfg(any(feature="precursor", feature="renode"))]
|
||||
#[cfg(any(feature = "precursor", feature = "renode"))]
|
||||
mod hw;
|
||||
#[cfg(any(feature="precursor", feature="renode"))]
|
||||
#[cfg(any(feature = "precursor", feature = "renode"))]
|
||||
pub use hw::*;
|
||||
|
||||
#[cfg(not(target_os = "xous"))]
|
||||
|
|
|
|||
|
|
@ -3,18 +3,17 @@
|
|||
|
||||
mod api;
|
||||
mod time; // why is this here? because it's the only place it'll fit. :-/
|
||||
use api::*;
|
||||
|
||||
use net::NetIpAddr;
|
||||
use num_traits::*;
|
||||
use xous::msg_scalar_unpack;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::io::ErrorKind;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, UdpSocket};
|
||||
use std::time::Duration;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use api::*;
|
||||
use net::NetIpAddr;
|
||||
use num_traits::*;
|
||||
use xous::msg_scalar_unpack;
|
||||
use xous_ipc::{Buffer, String};
|
||||
|
||||
// KISS DNS
|
||||
|
|
@ -47,11 +46,7 @@ struct Message {
|
|||
const FLAG_RD: u16 = 0x0100; // Recursion desired
|
||||
|
||||
impl Message {
|
||||
pub fn from(datagram: &[u8]) -> Self {
|
||||
Self {
|
||||
datagram: Vec::from(datagram),
|
||||
}
|
||||
}
|
||||
pub fn from(datagram: &[u8]) -> Self { Self { datagram: Vec::from(datagram) } }
|
||||
|
||||
pub fn query(qname: &str, qtype: QueryType, qclass: QueryClass, id: u16) -> Self {
|
||||
let mut datagram = Vec::new();
|
||||
|
|
@ -85,21 +80,11 @@ impl Message {
|
|||
Self { datagram }
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u16 {
|
||||
u16::from_be_bytes(self.datagram[0..2].try_into().unwrap())
|
||||
}
|
||||
pub fn id(&self) -> u16 { u16::from_be_bytes(self.datagram[0..2].try_into().unwrap()) }
|
||||
|
||||
pub fn header(&self) -> u16 {
|
||||
u16::from_be_bytes(self.datagram[2..4].try_into().unwrap())
|
||||
}
|
||||
pub fn header(&self) -> u16 { u16::from_be_bytes(self.datagram[2..4].try_into().unwrap()) }
|
||||
|
||||
pub fn is_response(&self) -> bool {
|
||||
if (self.header() & (1 << 15)) == 0 {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
pub fn is_response(&self) -> bool { if (self.header() & (1 << 15)) == 0 { false } else { true } }
|
||||
|
||||
fn fast_foward_name(&self, start: usize) -> Result<usize, DnsResponseCode> {
|
||||
use DnsResponseCode::FormatError;
|
||||
|
|
@ -156,9 +141,7 @@ impl Message {
|
|||
// pointer
|
||||
index += 1;
|
||||
if self.datagram[index] != 0xc {
|
||||
log::error!(
|
||||
"Found aname pointer, but value does not conform to length of aname header"
|
||||
);
|
||||
log::error!("Found aname pointer, but value does not conform to length of aname header");
|
||||
return Err(FormatError);
|
||||
}
|
||||
index += 1;
|
||||
|
|
@ -195,10 +178,10 @@ impl Message {
|
|||
log::error!("Got a 4-byte address, but ATYPE != A (1)");
|
||||
return Err(FormatError);
|
||||
}
|
||||
// this copy happens because I can't figure out how to get Ipv4Addr::from() to realize it's casting from a [u8;4]
|
||||
// this copy happens because I can't figure out how to get Ipv4Addr::from() to realize
|
||||
// it's casting from a [u8;4]
|
||||
let mut rdata: [u8; 4] = [0; 4];
|
||||
for (&src, dst) in self.datagram[index..index + 4].iter().zip(rdata.iter_mut())
|
||||
{
|
||||
for (&src, dst) in self.datagram[index..index + 4].iter().zip(rdata.iter_mut()) {
|
||||
*dst = src;
|
||||
}
|
||||
let addr = IpAddr::V4(Ipv4Addr::from(rdata));
|
||||
|
|
@ -211,12 +194,10 @@ impl Message {
|
|||
log::error!("Got a 16-byte address, but ATYPE != AAAA (28)");
|
||||
return Err(FormatError);
|
||||
}
|
||||
// this copy happens because I can't figure out how to get Ipv6Addr::from() to realize it's casting from a [u8;4]
|
||||
// this copy happens because I can't figure out how to get Ipv6Addr::from() to realize
|
||||
// it's casting from a [u8;4]
|
||||
let mut rdata: [u8; 16] = [0; 16];
|
||||
for (&src, dst) in self.datagram[index..index + 16]
|
||||
.iter()
|
||||
.zip(rdata.iter_mut())
|
||||
{
|
||||
for (&src, dst) in self.datagram[index..index + 16].iter().zip(rdata.iter_mut()) {
|
||||
*dst = src;
|
||||
}
|
||||
let addr = IpAddr::V6(Ipv6Addr::from(rdata));
|
||||
|
|
@ -317,15 +298,13 @@ impl Resolver {
|
|||
pub fn new(xns: &xous_names::XousNames) -> Resolver {
|
||||
let trng = trng::Trng::new(&xns).unwrap();
|
||||
let local_port = (49152 + trng.get_u32().unwrap() % 16384) as u16;
|
||||
let socket = UdpSocket::bind(
|
||||
format!("0.0.0.0:{}", local_port),
|
||||
)
|
||||
.expect("couldn't create socket for DNS resolver");
|
||||
let socket = UdpSocket::bind(format!("0.0.0.0:{}", local_port))
|
||||
.expect("couldn't create socket for DNS resolver");
|
||||
let timeout = Duration::from_millis(10_000); // 10 seconds for DNS to resolve by default
|
||||
socket.set_read_timeout(Some(timeout)).unwrap();
|
||||
socket.set_nonblocking(false).unwrap(); // we want this to block.
|
||||
// we /could/ do a non-blocking DNS resolver, but...what would you do in the meantime??
|
||||
// blocking is probably what we actually want this time.
|
||||
// we /could/ do a non-blocking DNS resolver, but...what would you do in the meantime??
|
||||
// blocking is probably what we actually want this time.
|
||||
|
||||
Resolver {
|
||||
mgr: net::protocols::DnsServerManager::register(&xns)
|
||||
|
|
@ -336,26 +315,23 @@ impl Resolver {
|
|||
freeze: false,
|
||||
}
|
||||
}
|
||||
pub fn add_server(&mut self, addr: IpAddr) {
|
||||
self.mgr.add_server(addr);
|
||||
}
|
||||
pub fn remove_server(&mut self, addr: IpAddr) {
|
||||
self.mgr.remove_server(addr);
|
||||
}
|
||||
pub fn clear_all_servers(&mut self) {
|
||||
self.mgr.clear();
|
||||
}
|
||||
|
||||
pub fn add_server(&mut self, addr: IpAddr) { self.mgr.add_server(addr); }
|
||||
|
||||
pub fn remove_server(&mut self, addr: IpAddr) { self.mgr.remove_server(addr); }
|
||||
|
||||
pub fn clear_all_servers(&mut self) { self.mgr.clear(); }
|
||||
|
||||
pub fn set_freeze_config(&mut self, freeze: bool) {
|
||||
self.freeze = freeze;
|
||||
self.mgr.set_freeze(freeze);
|
||||
}
|
||||
pub fn get_freeze(&self) -> bool {
|
||||
self.freeze
|
||||
}
|
||||
|
||||
pub fn get_freeze(&self) -> bool { self.freeze }
|
||||
|
||||
/// this allows us to re-use the TRNG object
|
||||
pub fn trng_u32(&self) -> u32 {
|
||||
self.trng.get_u32().unwrap()
|
||||
}
|
||||
pub fn trng_u32(&self) -> u32 { self.trng.get_u32().unwrap() }
|
||||
|
||||
pub fn resolve(&mut self, name: &str) -> Result<HashMap<IpAddr, u32>, DnsResponseCode> {
|
||||
if let Some(dns_address) = self.mgr.get_random() {
|
||||
let dns_port = 53;
|
||||
|
|
@ -366,9 +342,7 @@ impl Resolver {
|
|||
let qclass = QueryClass::IN;
|
||||
let query = Message::query(qname, qtype, qclass, self.trng.get_u32().unwrap() as u16);
|
||||
|
||||
self.socket
|
||||
.send_to(&query.datagram, &server)
|
||||
.map_err(|_| DnsResponseCode::NetworkError)?;
|
||||
self.socket.send_to(&query.datagram, &server).map_err(|_| DnsResponseCode::NetworkError)?;
|
||||
|
||||
match self.socket.recv(&mut self.buf) {
|
||||
Ok(len) => {
|
||||
|
|
@ -407,10 +381,7 @@ enum NameConversionError {
|
|||
}
|
||||
|
||||
fn name_from_msg(env: &xous::MessageEnvelope) -> Result<&str, NameConversionError> {
|
||||
let msg = env
|
||||
.body
|
||||
.memory_message()
|
||||
.ok_or(NameConversionError::InvalidMessageType)?;
|
||||
let msg = env.body.memory_message().ok_or(NameConversionError::InvalidMessageType)?;
|
||||
let valid_bytes = msg.valid.map(|v| v.get()).unwrap_or_else(|| msg.buf.len());
|
||||
if valid_bytes > DNS_NAME_LENGTH_LIMIT || valid_bytes < 1 {
|
||||
log::error!("valid bytes exceeded DNS name limit");
|
||||
|
|
@ -418,8 +389,7 @@ fn name_from_msg(env: &xous::MessageEnvelope) -> Result<&str, NameConversionErro
|
|||
}
|
||||
// Safe because we've already validated that it's a valid range
|
||||
let str_slice = unsafe { core::slice::from_raw_parts(msg.buf.as_ptr(), valid_bytes) };
|
||||
let name_string =
|
||||
core::str::from_utf8(str_slice).map_err(|_| NameConversionError::InvalidString)?;
|
||||
let name_string = core::str::from_utf8(str_slice).map_err(|_| NameConversionError::InvalidString)?;
|
||||
|
||||
Ok(name_string)
|
||||
}
|
||||
|
|
@ -494,9 +464,7 @@ fn main() -> ! {
|
|||
time::start_time_ux();
|
||||
|
||||
let xns = xous_names::XousNames::new().unwrap();
|
||||
let dns_sid = xns
|
||||
.register_name(api::SERVER_NAME_DNS, None)
|
||||
.expect("can't register server");
|
||||
let dns_sid = xns.register_name(api::SERVER_NAME_DNS, None).expect("can't register server");
|
||||
log::trace!("registered with NS -- {:?}", dns_sid);
|
||||
|
||||
// this will magically populate a list of DNS servers when they become available
|
||||
|
|
@ -572,12 +540,9 @@ fn main() -> ! {
|
|||
};
|
||||
}
|
||||
Some(Opcode::Lookup) => {
|
||||
let mut buf = unsafe {
|
||||
Buffer::from_memory_message_mut(msg.body.memory_message_mut().unwrap())
|
||||
};
|
||||
let name = buf
|
||||
.to_original::<String<DNS_NAME_LENGTH_LIMIT>, _>()
|
||||
.unwrap();
|
||||
let mut buf =
|
||||
unsafe { Buffer::from_memory_message_mut(msg.body.memory_message_mut().unwrap()) };
|
||||
let name = buf.to_original::<String<DNS_NAME_LENGTH_LIMIT>, _>().unwrap();
|
||||
let name_std = std::string::String::from(name.as_str().unwrap());
|
||||
if let Some(cache_entry) = dns_cache.get(&name_std) {
|
||||
// pick a random entry
|
||||
|
|
@ -617,30 +582,20 @@ fn main() -> ! {
|
|||
}
|
||||
} else {
|
||||
// no names found
|
||||
let response = DnsResponse {
|
||||
addr: None,
|
||||
code: DnsResponseCode::NameError,
|
||||
};
|
||||
let response = DnsResponse { addr: None, code: DnsResponseCode::NameError };
|
||||
buf.replace(response).unwrap();
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::debug!("DNS query failed: {}->{:?}", name, e);
|
||||
let response = DnsResponse {
|
||||
addr: None,
|
||||
code: e,
|
||||
};
|
||||
let response = DnsResponse { addr: None, code: e };
|
||||
buf.replace(response).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Opcode::UpdateTtl) => msg_scalar_unpack!(msg, incr_secs, _, _, _, {
|
||||
let increment = if incr_secs < u32::MAX as usize {
|
||||
incr_secs as u32
|
||||
} else {
|
||||
u32::MAX
|
||||
};
|
||||
let increment = if incr_secs < u32::MAX as usize { incr_secs as u32 } else { u32::MAX };
|
||||
if !resolver.get_freeze() {
|
||||
let mut expired_names = Vec::<std::string::String>::new();
|
||||
for (name, cache_map) in dns_cache.iter_mut() {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
|
||||
use std::num::ParseIntError;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
/// The `time_server` is unique is that it is written for exclusive use by `libstd` to extract time.
|
||||
///
|
||||
/// It also has a single hook that is callable from the PDDB to initialize a time value once the
|
||||
|
|
@ -22,21 +26,18 @@
|
|||
/// read RTC, plus you get milliseconds resolution. Time "T" can be updated at any time by just
|
||||
/// reading the RTC and noting the ticktimer offset at the point of reading.
|
||||
use std::thread;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
#[cfg(any(feature="precursor", feature="renode"))]
|
||||
|
||||
use chrono::prelude::*;
|
||||
use gam::modal::*;
|
||||
#[cfg(any(feature = "precursor", feature = "renode"))]
|
||||
use llio::*;
|
||||
use pddb::PddbMountPoller;
|
||||
use num_traits::*;
|
||||
// imports for time ux
|
||||
use locales::t;
|
||||
use chrono::prelude::*;
|
||||
use xous::{Message, send_message};
|
||||
use gam::modal::*;
|
||||
use num_traits::*;
|
||||
use pddb::PddbMountPoller;
|
||||
// ntp imports
|
||||
use sntpc::{Error, NtpContext, NtpTimestampGenerator, NtpUdpSocket, Result};
|
||||
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
|
||||
use std::num::ParseIntError;
|
||||
use xous::{send_message, Message};
|
||||
|
||||
/// This is a "well known name" used by `libstd` to connect to the time server
|
||||
/// Anyone who wants to check if time has been initialized would use this name.
|
||||
|
|
@ -97,30 +98,20 @@ struct StdTimestampGen {
|
|||
}
|
||||
impl NtpTimestampGenerator for StdTimestampGen {
|
||||
fn init(&mut self) {
|
||||
self.duration = std::time::SystemTime::now()
|
||||
.duration_since(std::time::SystemTime::UNIX_EPOCH)
|
||||
.unwrap();
|
||||
self.duration =
|
||||
std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH).unwrap();
|
||||
}
|
||||
|
||||
fn timestamp_sec(&self) -> u64 {
|
||||
self.duration.as_secs()
|
||||
}
|
||||
fn timestamp_sec(&self) -> u64 { self.duration.as_secs() }
|
||||
|
||||
fn timestamp_subsec_micros(&self) -> u32 {
|
||||
self.duration.subsec_micros()
|
||||
}
|
||||
fn timestamp_subsec_micros(&self) -> u32 { self.duration.subsec_micros() }
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
struct UdpSocketWrapper(UdpSocket);
|
||||
|
||||
impl NtpUdpSocket for UdpSocketWrapper {
|
||||
fn send_to<T: ToSocketAddrs>(
|
||||
&self,
|
||||
buf: &[u8],
|
||||
addr: T,
|
||||
) -> Result<usize> {
|
||||
fn send_to<T: ToSocketAddrs>(&self, buf: &[u8], addr: T) -> Result<usize> {
|
||||
match self.0.send_to(buf, addr) {
|
||||
Ok(usize) => Ok(usize),
|
||||
Err(_) => Err(Error::Network),
|
||||
|
|
@ -134,7 +125,7 @@ impl NtpUdpSocket for UdpSocketWrapper {
|
|||
}
|
||||
}
|
||||
}
|
||||
#[cfg(any(feature="precursor", feature="renode"))]
|
||||
#[cfg(any(feature = "precursor", feature = "renode"))]
|
||||
pub fn reset_rtc(i2c: &mut llio::I2c, start_time: u64, tt: &ticktimer_server::Ticktimer) {
|
||||
log::info!("performing rtc reset");
|
||||
i2c.i2c_mutex_acquire();
|
||||
|
|
@ -152,16 +143,16 @@ pub fn reset_rtc(i2c: &mut llio::I2c, start_time: u64, tt: &ticktimer_server::Ti
|
|||
}
|
||||
|
||||
let reset_vals = [
|
||||
0, // clear interrupts, normal run
|
||||
0, // clear interrupts
|
||||
RTC_PWR_MODE, // power mode
|
||||
to_bcd((start_time & 0xFF) as u8 % 60), // seconds
|
||||
to_bcd(((start_time >> 8) & 0xFF) as u8 % 60), // minutes
|
||||
to_bcd(((start_time >> 16) & 0xFF) as u8 % 24), // hours
|
||||
0, // clear interrupts, normal run
|
||||
0, // clear interrupts
|
||||
RTC_PWR_MODE, // power mode
|
||||
to_bcd((start_time & 0xFF) as u8 % 60), // seconds
|
||||
to_bcd(((start_time >> 8) & 0xFF) as u8 % 60), // minutes
|
||||
to_bcd(((start_time >> 16) & 0xFF) as u8 % 24), // hours
|
||||
to_bcd(((start_time >> 24) & 0xFF) as u8 % 28 + 1), // days
|
||||
0, // sunday
|
||||
0, // sunday
|
||||
to_bcd(((start_time >> 32) & 0xFF) as u8 % 12 + 1), // months
|
||||
to_bcd(((start_time >> 40) & 0xFF) as u8 % 5 + 1), // years
|
||||
to_bcd(((start_time >> 40) & 0xFF) as u8 % 5 + 1), // years
|
||||
];
|
||||
i2c.i2c_mutex_acquire();
|
||||
log::info!("writing: {:x?}", reset_vals);
|
||||
|
|
@ -180,12 +171,12 @@ pub fn start_time_server() {
|
|||
let rtc_checked = Arc::new(AtomicBool::new(false));
|
||||
|
||||
// the public SID is well known and accessible by anyone who uses `libstd`
|
||||
let pub_sid = xous::create_server_with_address(&TIME_SERVER_PUBLIC)
|
||||
.expect("Couldn't create Ticktimer server");
|
||||
let pub_sid =
|
||||
xous::create_server_with_address(&TIME_SERVER_PUBLIC).expect("Couldn't create Ticktimer server");
|
||||
let self_cid = xous::connect(pub_sid).unwrap();
|
||||
|
||||
// this thread handles more sensitive operations on the RTC.
|
||||
#[cfg(any(feature="precursor", feature="renode"))]
|
||||
#[cfg(any(feature = "precursor", feature = "renode"))]
|
||||
thread::spawn({
|
||||
let rtc_checked = rtc_checked.clone();
|
||||
let self_cid = self_cid.clone();
|
||||
|
|
@ -205,8 +196,9 @@ pub fn start_time_server() {
|
|||
Some(susres::SuspendOrder::Early),
|
||||
&xns,
|
||||
PrivTimeOp::SusRes as u32,
|
||||
priv_conn
|
||||
).expect("couldn't create suspend/resume object");
|
||||
priv_conn,
|
||||
)
|
||||
.expect("couldn't create suspend/resume object");
|
||||
|
||||
// on boot, do the validation checks of the RTC. If it is not initialized or corrupted, fix it.
|
||||
let mut settings = [0u8; 8];
|
||||
|
|
@ -215,13 +207,13 @@ pub fn start_time_server() {
|
|||
match i2c.i2c_read_no_repeated_start(ABRTCMC_I2C_ADR, ABRTCMC_CONTROL3, &mut settings) {
|
||||
Ok(I2cStatus::ResponseReadOk) => {
|
||||
i2c.i2c_mutex_release();
|
||||
break
|
||||
},
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
i2c.i2c_mutex_release();
|
||||
log::error!("Couldn't check RTC, retrying!");
|
||||
xous::yield_slice(); // recheck in a fast loop, we really should be able to grab this resource on boot.
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
if is_rtc_invalid(&settings) {
|
||||
|
|
@ -239,11 +231,14 @@ pub fn start_time_server() {
|
|||
}),
|
||||
Some(PrivTimeOp::SusRes) => xous::msg_scalar_unpack!(msg, token, _, _, _, {
|
||||
susres.suspend_until_resume(token).expect("couldn't execute suspend/resume");
|
||||
// resync time on resume, but give a little time for other processes to clear as this is not urgent
|
||||
// resync time on resume, but give a little time for other processes to clear as this
|
||||
// is not urgent
|
||||
tt.sleep_ms(180).unwrap();
|
||||
send_message(self_cid,
|
||||
Message::new_scalar(TimeOp::HwSync.to_usize().unwrap(), 0, 0, 0, 0)
|
||||
).expect("couldn't queue sync request");
|
||||
send_message(
|
||||
self_cid,
|
||||
Message::new_scalar(TimeOp::HwSync.to_usize().unwrap(), 0, 0, 0, 0),
|
||||
)
|
||||
.expect("couldn't queue sync request");
|
||||
}),
|
||||
_ => log::error!("Time server private thread received unknown opcode: {:?}", msg),
|
||||
}
|
||||
|
|
@ -257,7 +252,7 @@ pub fn start_time_server() {
|
|||
move || {
|
||||
let xns = xous_names::XousNames::new().unwrap();
|
||||
let llio = llio::Llio::new(&xns);
|
||||
#[cfg(not(feature="minimal-testing"))]
|
||||
#[cfg(not(feature = "minimal-testing"))]
|
||||
let prefs = userprefs::Manager::new();
|
||||
|
||||
let tt = ticktimer_server::Ticktimer::new().unwrap();
|
||||
|
|
@ -266,7 +261,8 @@ pub fn start_time_server() {
|
|||
tt.sleep_ms(42).unwrap();
|
||||
}
|
||||
let mut start_rtc_secs: u64;
|
||||
// this loop allows us to fail slightly more gracefully in the case that there is an RTC hardware failure
|
||||
// this loop allows us to fail slightly more gracefully in the case that there is an RTC hardware
|
||||
// failure
|
||||
loop {
|
||||
match llio.get_rtc_secs() {
|
||||
Ok(s) => {
|
||||
|
|
@ -283,17 +279,19 @@ pub fn start_time_server() {
|
|||
log::trace!("start_rtc_secs: {}", start_rtc_secs);
|
||||
log::trace!("start_tt_ms: {}", start_tt_ms);
|
||||
|
||||
#[cfg(not(feature="minimal-testing"))]
|
||||
#[cfg(not(feature = "minimal-testing"))]
|
||||
let pddb_poller = PddbMountPoller::new();
|
||||
// enqueue a the first mount poll message
|
||||
#[cfg(not(feature="minimal-testing"))]
|
||||
xous::send_message(self_cid,
|
||||
xous::Message::new_scalar(TimeOp::PddbMountPoll.to_usize().unwrap(), 0, 0, 0, 0)
|
||||
).expect("couldn't check mount poll");
|
||||
#[cfg(not(feature = "minimal-testing"))]
|
||||
xous::send_message(
|
||||
self_cid,
|
||||
xous::Message::new_scalar(TimeOp::PddbMountPoll.to_usize().unwrap(), 0, 0, 0, 0),
|
||||
)
|
||||
.expect("couldn't check mount poll");
|
||||
// an initial behavior which just retuns the raw RTC time, until the PDDB is mounted.
|
||||
let mut temp = 0;
|
||||
loop {
|
||||
#[cfg(not(feature="minimal-testing"))]
|
||||
#[cfg(not(feature = "minimal-testing"))]
|
||||
if pddb_poller.is_mounted_nonblocking() {
|
||||
log::debug!("PDDB mount detected, transitioning to real-time adjusted server");
|
||||
break;
|
||||
|
|
@ -308,35 +306,37 @@ pub fn start_time_server() {
|
|||
log::trace!("mount poll");
|
||||
}
|
||||
temp += 1;
|
||||
xous::send_message(self_cid,
|
||||
xous::Message::new_scalar(TimeOp::PddbMountPoll.to_usize().unwrap(), 0, 0, 0, 0)
|
||||
).expect("couldn't check mount poll");
|
||||
xous::send_message(
|
||||
self_cid,
|
||||
xous::Message::new_scalar(TimeOp::PddbMountPoll.to_usize().unwrap(), 0, 0, 0, 0),
|
||||
)
|
||||
.expect("couldn't check mount poll");
|
||||
}
|
||||
Some(TimeOp::HwSync) => {
|
||||
start_rtc_secs = llio.get_rtc_secs().expect("couldn't read RTC offset value");
|
||||
start_tt_ms = tt.elapsed_ms();
|
||||
},
|
||||
}
|
||||
Some(TimeOp::GetUtcTimeMs) => xous::msg_blocking_scalar_unpack!(msg, _, _, _, _, {
|
||||
let t =
|
||||
start_rtc_secs as i64 * 1000i64
|
||||
+ (tt.elapsed_ms() - start_tt_ms) as i64;
|
||||
let t = start_rtc_secs as i64 * 1000i64 + (tt.elapsed_ms() - start_tt_ms) as i64;
|
||||
log::debug!("hw only UTC ms {}", t);
|
||||
// NOTE: endian swap for compatibility with `std`
|
||||
xous::return_scalar2(msg.sender,
|
||||
xous::return_scalar2(
|
||||
msg.sender,
|
||||
(t as u64 & 0xFFFF_FFFF) as usize,
|
||||
(((t as u64) >> 32) & 0xFFFF_FFFF) as usize,
|
||||
).expect("couldn't respond to GetUtcTimeMs");
|
||||
)
|
||||
.expect("couldn't respond to GetUtcTimeMs");
|
||||
}),
|
||||
Some(TimeOp::GetLocalTimeMs) => xous::msg_blocking_scalar_unpack!(msg, _, _, _, _, {
|
||||
let t =
|
||||
start_rtc_secs as i64 * 1000i64
|
||||
+ (tt.elapsed_ms() - start_tt_ms) as i64;
|
||||
let t = start_rtc_secs as i64 * 1000i64 + (tt.elapsed_ms() - start_tt_ms) as i64;
|
||||
assert!(t > 0, "time result is negative, this is an error");
|
||||
log::debug!("hw only local ms {}", t);
|
||||
xous::return_scalar2(msg.sender,
|
||||
xous::return_scalar2(
|
||||
msg.sender,
|
||||
(((t as u64) >> 32) & 0xFFFF_FFFF) as usize,
|
||||
(t as u64 & 0xFFFF_FFFF) as usize,
|
||||
).expect("couldn't respond to GetLocalTimeMs");
|
||||
)
|
||||
.expect("couldn't respond to GetLocalTimeMs");
|
||||
}),
|
||||
Some(TimeOp::WallClockTimeInit) => xous::msg_blocking_scalar_unpack!(msg, _, _, _, _, {
|
||||
// definitely not initialized
|
||||
|
|
@ -345,19 +345,22 @@ pub fn start_time_server() {
|
|||
_ => log::warn!("Time server can't handle this message yet: {:?}", msg),
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature="minimal-testing"))]
|
||||
#[cfg(not(feature = "minimal-testing"))]
|
||||
let mut utc_offset_ms = prefs.utc_offset().unwrap_or_else(|error| {
|
||||
log::error!("cannot read utc offset: {:?}", error);
|
||||
0
|
||||
});
|
||||
#[cfg(not(feature="minimal-testing"))]
|
||||
let mut tz_offset_ms = prefs.timezone_offset().unwrap_or_else(|error| {
|
||||
log::error!("cannot read timezone offset: {:?}", error);
|
||||
None
|
||||
}).unwrap_or_default();
|
||||
#[cfg(feature="minimal-testing")]
|
||||
#[cfg(not(feature = "minimal-testing"))]
|
||||
let mut tz_offset_ms = prefs
|
||||
.timezone_offset()
|
||||
.unwrap_or_else(|error| {
|
||||
log::error!("cannot read timezone offset: {:?}", error);
|
||||
None
|
||||
})
|
||||
.unwrap_or_default();
|
||||
#[cfg(feature = "minimal-testing")]
|
||||
let mut utc_offset_ms = 0;
|
||||
#[cfg(feature="minimal-testing")]
|
||||
#[cfg(feature = "minimal-testing")]
|
||||
let mut tz_offset_ms = 0;
|
||||
|
||||
log::debug!("offset_key: {}", utc_offset_ms / 1000);
|
||||
|
|
@ -372,86 +375,96 @@ pub fn start_time_server() {
|
|||
Some(TimeOp::PddbMountPoll) => {
|
||||
// do nothing, we're mounted now.
|
||||
continue;
|
||||
},
|
||||
Some(TimeOp::HwSync) => {
|
||||
match llio.get_rtc_secs() {
|
||||
Ok(val) => {
|
||||
start_rtc_secs = val;
|
||||
start_tt_ms = tt.elapsed_ms();
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Error syncing time: {:?}; retrying!", e);
|
||||
tt.sleep_ms(82).unwrap();
|
||||
send_message(self_cid,
|
||||
Message::new_scalar(TimeOp::HwSync.to_usize().unwrap(), 0, 0, 0, 0)
|
||||
).expect("couldn't queue sync request");
|
||||
}
|
||||
}
|
||||
Some(TimeOp::HwSync) => match llio.get_rtc_secs() {
|
||||
Ok(val) => {
|
||||
start_rtc_secs = val;
|
||||
start_tt_ms = tt.elapsed_ms();
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Error syncing time: {:?}; retrying!", e);
|
||||
tt.sleep_ms(82).unwrap();
|
||||
send_message(
|
||||
self_cid,
|
||||
Message::new_scalar(TimeOp::HwSync.to_usize().unwrap(), 0, 0, 0, 0),
|
||||
)
|
||||
.expect("couldn't queue sync request");
|
||||
}
|
||||
},
|
||||
Some(TimeOp::GetUtcTimeMs) => xous::msg_blocking_scalar_unpack!(msg, _, _, _, _, {
|
||||
let t =
|
||||
start_rtc_secs as i64 * 1000i64
|
||||
let t = start_rtc_secs as i64 * 1000i64
|
||||
+ (tt.elapsed_ms() - start_tt_ms) as i64
|
||||
+ utc_offset_ms;
|
||||
if t < 0 { // the offset has some error in it, perhaps due to an RTC reset. reset the offset!
|
||||
log::warn!("Time was negative, recovering from time setting error by clearing utc offset to 0");
|
||||
if t < 0 {
|
||||
// the offset has some error in it, perhaps due to an RTC reset. reset the offset!
|
||||
log::warn!(
|
||||
"Time was negative, recovering from time setting error by clearing utc offset to 0"
|
||||
);
|
||||
prefs.set_utc_offset(0).ok();
|
||||
utc_offset_ms = 0;
|
||||
}
|
||||
log::trace!("utc ms {}", t);
|
||||
// NOTE: endian swap for compatibility with `std`
|
||||
xous::return_scalar2(msg.sender,
|
||||
xous::return_scalar2(
|
||||
msg.sender,
|
||||
(t as u64 & 0xFFFF_FFFF) as usize,
|
||||
(((t as u64) >> 32) & 0xFFFF_FFFF) as usize,
|
||||
).expect("couldn't respond to GetUtcTimeMs");
|
||||
)
|
||||
.expect("couldn't respond to GetUtcTimeMs");
|
||||
}),
|
||||
Some(TimeOp::GetLocalTimeMs) => xous::msg_blocking_scalar_unpack!(msg, _, _, _, _, {
|
||||
log::trace!("current offset {}", (start_rtc_secs as i64 * 1000i64 + (tt.elapsed_ms() - start_tt_ms) as i64) / 1000);
|
||||
let t =
|
||||
start_rtc_secs as i64 * 1000i64
|
||||
log::trace!(
|
||||
"current offset {}",
|
||||
(start_rtc_secs as i64 * 1000i64 + (tt.elapsed_ms() - start_tt_ms) as i64) / 1000
|
||||
);
|
||||
let t = start_rtc_secs as i64 * 1000i64
|
||||
+ (tt.elapsed_ms() - start_tt_ms) as i64
|
||||
+ utc_offset_ms
|
||||
+ tz_offset_ms;
|
||||
if t < 0 {
|
||||
log::warn!("Time was negative, recovering from time setting error by clearing utc and timezone offsets to 0.");
|
||||
log::warn!(
|
||||
"Time was negative, recovering from time setting error by clearing utc and timezone offsets to 0."
|
||||
);
|
||||
prefs.set_utc_offset(0).ok();
|
||||
prefs.set_timezone_offset(0).ok();
|
||||
utc_offset_ms = 0;
|
||||
tz_offset_ms = 0;
|
||||
}
|
||||
log::trace!("local since epoch {}", t / 1000);
|
||||
xous::return_scalar2(msg.sender,
|
||||
xous::return_scalar2(
|
||||
msg.sender,
|
||||
(((t as u64) >> 32) & 0xFFFF_FFFF) as usize,
|
||||
(t as u64 & 0xFFFF_FFFF) as usize,
|
||||
).expect("couldn't respond to GetLocalTimeMs");
|
||||
}),
|
||||
Some(TimeOp::SetUtcTimeMs) => xous::msg_scalar_unpack!(msg, utc_hi_ms, utc_lo_ms, _, _, {
|
||||
let utc_time_ms = (utc_hi_ms as i64) << 32 | (utc_lo_ms as i64);
|
||||
start_rtc_secs = llio.get_rtc_secs().expect("couldn't read RTC offset value");
|
||||
start_tt_ms = tt.elapsed_ms();
|
||||
log::info!("utc_time: {}", utc_time_ms / 1000);
|
||||
log::info!("rtc_secs: {}", start_rtc_secs);
|
||||
log::info!("start_tt_ms: {}", start_tt_ms);
|
||||
let offset =
|
||||
utc_time_ms -
|
||||
(start_rtc_secs as i64) * 1000;
|
||||
utc_offset_ms = offset;
|
||||
#[cfg(not(feature="minimal-testing"))]
|
||||
prefs.set_utc_offset(offset).unwrap_or_else(|err| {
|
||||
log::error!("cannot set utc offset: {:?}", err);
|
||||
});
|
||||
)
|
||||
.expect("couldn't respond to GetLocalTimeMs");
|
||||
}),
|
||||
Some(TimeOp::SetUtcTimeMs) => {
|
||||
xous::msg_scalar_unpack!(msg, utc_hi_ms, utc_lo_ms, _, _, {
|
||||
let utc_time_ms = (utc_hi_ms as i64) << 32 | (utc_lo_ms as i64);
|
||||
start_rtc_secs = llio.get_rtc_secs().expect("couldn't read RTC offset value");
|
||||
start_tt_ms = tt.elapsed_ms();
|
||||
log::info!("utc_time: {}", utc_time_ms / 1000);
|
||||
log::info!("rtc_secs: {}", start_rtc_secs);
|
||||
log::info!("start_tt_ms: {}", start_tt_ms);
|
||||
let offset = utc_time_ms - (start_rtc_secs as i64) * 1000;
|
||||
utc_offset_ms = offset;
|
||||
#[cfg(not(feature = "minimal-testing"))]
|
||||
prefs.set_utc_offset(offset).unwrap_or_else(|err| {
|
||||
log::error!("cannot set utc offset: {:?}", err);
|
||||
});
|
||||
})
|
||||
}
|
||||
Some(TimeOp::SetTzOffsetMs) => xous::msg_scalar_unpack!(msg, tz_hi_ms, tz_lo_ms, _, _, {
|
||||
let tz_ms = ((tz_hi_ms as i64) << 32) | (tz_lo_ms as i64);
|
||||
// sanity check with very broad bounds: I don't know of any time zones that are more than +/2 days from UTC
|
||||
// 86400 seconds in a day, times 1000 milliseconds, times 2 days
|
||||
if tz_ms < -(86400 * 1000 * 2) ||
|
||||
tz_ms > 86400 * 1000 * 2 {
|
||||
// sanity check with very broad bounds: I don't know of any time zones that are more
|
||||
// than +/2 days from UTC 86400 seconds in a day, times 1000
|
||||
// milliseconds, times 2 days
|
||||
if tz_ms < -(86400 * 1000 * 2) || tz_ms > 86400 * 1000 * 2 {
|
||||
log::warn!("Requested timezone offset {} is out of bounds, ignoring!", tz_ms);
|
||||
continue;
|
||||
} else {
|
||||
tz_offset_ms = tz_ms;
|
||||
#[cfg(not(feature="minimal-testing"))]
|
||||
#[cfg(not(feature = "minimal-testing"))]
|
||||
prefs.set_timezone_offset(tz_ms).unwrap_or_else(|err| {
|
||||
log::error!("cannot set timezone offset: {:?}", err);
|
||||
});
|
||||
|
|
@ -505,9 +518,7 @@ fn is_rtc_invalid(settings: &[u8]) -> bool {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn to_binary(bcd: u8) -> u8 {
|
||||
(bcd & 0xf) + ((bcd >> 4) * 10)
|
||||
}
|
||||
fn to_binary(bcd: u8) -> u8 { (bcd & 0xf) + ((bcd >> 4) * 10) }
|
||||
#[allow(dead_code)]
|
||||
fn to_bcd(binary: u8) -> u8 {
|
||||
let mut lsd: u8 = binary % 10;
|
||||
|
|
@ -542,7 +553,7 @@ fn simple_kilofloat_parse(input: &str) -> core::result::Result<i32, ParseIntErro
|
|||
}
|
||||
significance /= 10;
|
||||
} else {
|
||||
return "z".parse::<i32>() // you can't create a ParseIntError any other way
|
||||
return "z".parse::<i32>(); // you can't create a ParseIntError any other way
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
|
|
@ -552,14 +563,15 @@ fn simple_kilofloat_parse(input: &str) -> core::result::Result<i32, ParseIntErro
|
|||
}
|
||||
}
|
||||
|
||||
pub (crate) fn start_time_ux() {
|
||||
pub(crate) fn start_time_ux() {
|
||||
thread::spawn({
|
||||
move || {
|
||||
// some RTC UX structures
|
||||
let xns = xous_names::XousNames::new().unwrap();
|
||||
let sid = xns.register_name(crate::TIME_UX_NAME, Some(1)).unwrap();
|
||||
let modals = modals::Modals::new(&xns).unwrap();
|
||||
let timeserver_cid = xous::connect(xous::SID::from_bytes(crate::time::TIME_SERVER_PUBLIC).unwrap()).unwrap();
|
||||
let timeserver_cid =
|
||||
xous::connect(xous::SID::from_bytes(crate::time::TIME_SERVER_PUBLIC).unwrap()).unwrap();
|
||||
let pddb_poller = pddb::PddbMountPoller::new();
|
||||
let trng = trng::Trng::new(&xns).unwrap();
|
||||
|
||||
|
|
@ -570,7 +582,9 @@ pub (crate) fn start_time_ux() {
|
|||
match FromPrimitive::from_usize(msg.body.id()) {
|
||||
Some(crate::TimeUxOp::SetTime) => xous::msg_scalar_unpack!(msg, _, _, _, _, {
|
||||
if !pddb_poller.is_mounted_nonblocking() {
|
||||
modals.show_notification(t!("stats.please_mount", locales::LANG), None).expect("couldn't show notification");
|
||||
modals
|
||||
.show_notification(t!("stats.please_mount", locales::LANG), None)
|
||||
.expect("couldn't show notification");
|
||||
continue;
|
||||
}
|
||||
let mut tz_set = false;
|
||||
|
|
@ -579,7 +593,7 @@ pub (crate) fn start_time_ux() {
|
|||
Some(data) => {
|
||||
tz_set = true;
|
||||
data
|
||||
},
|
||||
}
|
||||
None => {
|
||||
tz_set = false;
|
||||
0i64
|
||||
|
|
@ -595,40 +609,52 @@ pub (crate) fn start_time_ux() {
|
|||
// a key exists, but nothing was written to it (length of key was 0 or inappropriate)
|
||||
if !tz_set {
|
||||
log::info!("{}RTC.TZ,{}", xous::BOOKEND_START, xous::BOOKEND_END);
|
||||
let tz_str = modals.alert_builder(t!("rtc.timezone", locales::LANG))
|
||||
let tz_str = modals
|
||||
.alert_builder(t!("rtc.timezone", locales::LANG))
|
||||
.field(None, Some(tz_ux_validator))
|
||||
.build()
|
||||
.expect("couldn't get timezone")
|
||||
.first();
|
||||
let tz = simple_kilofloat_parse(tz_str.as_str()).expect("pre-validated input failed to re-parse!");
|
||||
let tz = simple_kilofloat_parse(tz_str.as_str())
|
||||
.expect("pre-validated input failed to re-parse!");
|
||||
log::info!("got tz offset {}", tz);
|
||||
tz_offset_ms = (tz * 3600) as i64;
|
||||
xous::send_message(timeserver_cid,
|
||||
xous::send_message(
|
||||
timeserver_cid,
|
||||
Message::new_scalar(
|
||||
crate::time::TimeOp::SetTzOffsetMs.to_usize().unwrap(),
|
||||
(tz_offset_ms >> 32) as usize,
|
||||
(tz_offset_ms & 0xFFFF_FFFF) as usize,
|
||||
0, 0,
|
||||
)
|
||||
).expect("couldn't set timezone");
|
||||
0,
|
||||
0,
|
||||
),
|
||||
)
|
||||
.expect("couldn't set timezone");
|
||||
}
|
||||
|
||||
// see if we want to try to use NTP or not
|
||||
log::info!("{}RTC.NTP,{}", xous::BOOKEND_START, xous::BOOKEND_END);
|
||||
modals.add_list_item(t!("pddb.yes", locales::LANG)).expect("couldn't build radio item list");
|
||||
modals.add_list_item(t!("pddb.no", locales::LANG)).expect("couldn't build radio item list");
|
||||
modals
|
||||
.add_list_item(t!("pddb.yes", locales::LANG))
|
||||
.expect("couldn't build radio item list");
|
||||
modals
|
||||
.add_list_item(t!("pddb.no", locales::LANG))
|
||||
.expect("couldn't build radio item list");
|
||||
let mut try_ntp = true;
|
||||
match modals.get_radiobutton(t!("rtc.try_ntp", locales::LANG)) {
|
||||
Ok(selection) => {
|
||||
if selection == t!("pddb.no", locales::LANG) {
|
||||
try_ntp = false;
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => log::error!("get_radiobutton failed"),
|
||||
}
|
||||
if try_ntp {
|
||||
let local_port = (trng.get_u32().unwrap() % 16384 + 49152) as u16;
|
||||
let socket_addr = SocketAddr::new(std::net::IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0)), local_port);
|
||||
let socket_addr = SocketAddr::new(
|
||||
std::net::IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0)),
|
||||
local_port,
|
||||
);
|
||||
let socket = UdpSocket::bind(socket_addr).expect("Unable to create UDP socket");
|
||||
log::debug!("NTP rx socket created {:?}", socket);
|
||||
socket
|
||||
|
|
@ -640,24 +666,30 @@ pub (crate) fn start_time_ux() {
|
|||
match result {
|
||||
Ok(time) => {
|
||||
log::info!("Got NTP time: {}.{}", time.sec(), time.sec_fraction());
|
||||
let current_time = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) + chrono::Duration::seconds(time.sec() as i64);
|
||||
let current_time = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0)
|
||||
+ chrono::Duration::seconds(time.sec() as i64);
|
||||
log::info!("Setting UTC time: {:?}", current_time.to_string());
|
||||
xous::send_message(timeserver_cid,
|
||||
xous::send_message(
|
||||
timeserver_cid,
|
||||
Message::new_scalar(
|
||||
crate::time::TimeOp::SetUtcTimeMs.to_usize().unwrap(),
|
||||
((current_time.timestamp_millis() as u64) >> 32) as usize,
|
||||
(current_time.timestamp_millis() as u64 & 0xFFFF_FFFF) as usize,
|
||||
0, 0,
|
||||
)
|
||||
).expect("couldn't set time");
|
||||
0,
|
||||
0,
|
||||
),
|
||||
)
|
||||
.expect("couldn't set time");
|
||||
log::info!("{}RTC.NTPOK,{}", xous::BOOKEND_START, xous::BOOKEND_END);
|
||||
continue;
|
||||
}
|
||||
Err(err) => {
|
||||
log::info!("Err: {:?}", err);
|
||||
log::info!("{}RTC.NTPFAIL,{}", xous::BOOKEND_START, xous::BOOKEND_END);
|
||||
modals.show_notification(t!("rtc.ntp_fail", locales::LANG), None).expect("couldn't show NTP error");
|
||||
},
|
||||
modals
|
||||
.show_notification(t!("rtc.ntp_fail", locales::LANG), None)
|
||||
.expect("couldn't show NTP error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -668,18 +700,38 @@ pub (crate) fn start_time_ux() {
|
|||
let mut days: u8 = 0;
|
||||
let mut years: u8 = 0;
|
||||
|
||||
let date = modals.alert_builder(t!("rtc.set_time_modal", locales::LANG))
|
||||
.field(Some(String::from(t!("rtc.month", locales::LANG))), Some(rtc_ux_validate_month))
|
||||
.field(Some(String::from(t!("rtc.day", locales::LANG))), Some(rtc_ux_validate_day))
|
||||
.field(Some(String::from(t!("rtc.year", locales::LANG))), Some(rtc_ux_validate_year))
|
||||
.field(Some(String::from(t!("rtc.hour", locales::LANG))), Some(rtc_ux_validate_hour))
|
||||
.field(Some(String::from(t!("rtc.minute", locales::LANG))), Some(rtc_ux_validate_minute))
|
||||
.field(Some(String::from(t!("rtc.seconds", locales::LANG))), Some(rtc_ux_validate_seconds))
|
||||
let date = modals
|
||||
.alert_builder(t!("rtc.set_time_modal", locales::LANG))
|
||||
.field(
|
||||
Some(String::from(t!("rtc.month", locales::LANG))),
|
||||
Some(rtc_ux_validate_month),
|
||||
)
|
||||
.field(
|
||||
Some(String::from(t!("rtc.day", locales::LANG))),
|
||||
Some(rtc_ux_validate_day),
|
||||
)
|
||||
.field(
|
||||
Some(String::from(t!("rtc.year", locales::LANG))),
|
||||
Some(rtc_ux_validate_year),
|
||||
)
|
||||
.field(
|
||||
Some(String::from(t!("rtc.hour", locales::LANG))),
|
||||
Some(rtc_ux_validate_hour),
|
||||
)
|
||||
.field(
|
||||
Some(String::from(t!("rtc.minute", locales::LANG))),
|
||||
Some(rtc_ux_validate_minute),
|
||||
)
|
||||
.field(
|
||||
Some(String::from(t!("rtc.seconds", locales::LANG))),
|
||||
Some(rtc_ux_validate_seconds),
|
||||
)
|
||||
.build()
|
||||
.expect("cannot get date from user");
|
||||
|
||||
for (index, elem) in date.content().iter().enumerate() {
|
||||
let elem = elem.as_str().parse::<u8>().expect("pre-validated input failed to re-parse!");
|
||||
let elem =
|
||||
elem.as_str().parse::<u8>().expect("pre-validated input failed to re-parse!");
|
||||
match index {
|
||||
0 => months = elem,
|
||||
1 => days = elem,
|
||||
|
|
@ -687,29 +739,36 @@ pub (crate) fn start_time_ux() {
|
|||
3 => hours = elem,
|
||||
4 => mins = elem,
|
||||
5 => secs = elem,
|
||||
_ => {},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("Setting time: {}/{}/{} {}:{}:{}", months, days, years, hours, mins, secs);
|
||||
let new_dt = chrono::FixedOffset::east((tz_offset_ms / 1000) as i32).ymd(years as i32 + 2000, months as u32, days as u32)
|
||||
.and_hms(hours as u32, mins as u32, secs as u32);
|
||||
xous::send_message(timeserver_cid,
|
||||
let new_dt = chrono::FixedOffset::east((tz_offset_ms / 1000) as i32)
|
||||
.ymd(years as i32 + 2000, months as u32, days as u32)
|
||||
.and_hms(hours as u32, mins as u32, secs as u32);
|
||||
xous::send_message(
|
||||
timeserver_cid,
|
||||
Message::new_scalar(
|
||||
crate::time::TimeOp::SetUtcTimeMs.to_usize().unwrap(),
|
||||
((new_dt.timestamp_millis() as u64) >> 32) as usize,
|
||||
(new_dt.timestamp_millis() as u64 & 0xFFFF_FFFF) as usize,
|
||||
0, 0,
|
||||
)
|
||||
).expect("couldn't set time");
|
||||
0,
|
||||
0,
|
||||
),
|
||||
)
|
||||
.expect("couldn't set time");
|
||||
}),
|
||||
Some(crate::TimeUxOp::SetTimeZone) => xous::msg_scalar_unpack!(msg, _, _, _, _, {
|
||||
if !pddb_poller.is_mounted_nonblocking() {
|
||||
modals.show_notification(t!("stats.please_mount", locales::LANG), None).expect("couldn't show notification");
|
||||
modals
|
||||
.show_notification(t!("stats.please_mount", locales::LANG), None)
|
||||
.expect("couldn't show notification");
|
||||
continue;
|
||||
}
|
||||
|
||||
let tz_str = modals.alert_builder(t!("rtc.timezone", locales::LANG))
|
||||
let tz_str = modals
|
||||
.alert_builder(t!("rtc.timezone", locales::LANG))
|
||||
.field(None, Some(tz_ux_validator))
|
||||
.build()
|
||||
.expect("couldn't get timezone")
|
||||
|
|
@ -718,14 +777,17 @@ pub (crate) fn start_time_ux() {
|
|||
.expect("pre-validated input failed to re-parse!");
|
||||
log::info!("got tz offset {}", tz);
|
||||
let tzoff_ms = (tz * 3600) as i64;
|
||||
xous::send_message(timeserver_cid,
|
||||
xous::send_message(
|
||||
timeserver_cid,
|
||||
Message::new_scalar(
|
||||
crate::time::TimeOp::SetTzOffsetMs.to_usize().unwrap(),
|
||||
(tzoff_ms >> 32) as usize,
|
||||
(tzoff_ms & 0xFFFF_FFFF) as usize,
|
||||
0, 0,
|
||||
)
|
||||
).expect("couldn't set timezone");
|
||||
0,
|
||||
0,
|
||||
),
|
||||
)
|
||||
.expect("couldn't set timezone");
|
||||
}),
|
||||
Some(crate::TimeUxOp::Quit) => {
|
||||
xous::return_scalar(msg.sender, 0).unwrap();
|
||||
|
|
@ -756,9 +818,11 @@ fn tz_ux_validator(input: TextEntryPayload) -> Option<ValidatorErr> {
|
|||
let text_str = input.as_str();
|
||||
|
||||
match simple_kilofloat_parse(text_str) {
|
||||
Ok(input) => if input < -12_000 || input > 14_000 {
|
||||
return Some(ValidatorErr::from_str(t!("rtc.range_err", locales::LANG)));
|
||||
},
|
||||
Ok(input) => {
|
||||
if input < -12_000 || input > 14_000 {
|
||||
return Some(ValidatorErr::from_str(t!("rtc.range_err", locales::LANG)));
|
||||
}
|
||||
}
|
||||
_ => return Some(ValidatorErr::from_str(t!("rtc.integer_err", locales::LANG))),
|
||||
}
|
||||
None
|
||||
|
|
@ -773,7 +837,7 @@ fn rtc_ux_validate_month(input: TextEntryPayload) -> Option<ValidatorErr> {
|
|||
};
|
||||
|
||||
if input < 1 || input > 12 {
|
||||
return Some(ValidatorErr::from_str(t!("rtc.range_err", locales::LANG)))
|
||||
return Some(ValidatorErr::from_str(t!("rtc.range_err", locales::LANG)));
|
||||
}
|
||||
|
||||
None
|
||||
|
|
@ -788,7 +852,7 @@ fn rtc_ux_validate_day(input: TextEntryPayload) -> Option<ValidatorErr> {
|
|||
};
|
||||
|
||||
if input < 1 || input > 31 {
|
||||
return Some(ValidatorErr::from_str(t!("rtc.range_err", locales::LANG)))
|
||||
return Some(ValidatorErr::from_str(t!("rtc.range_err", locales::LANG)));
|
||||
}
|
||||
|
||||
None
|
||||
|
|
@ -803,7 +867,7 @@ fn rtc_ux_validate_year(input: TextEntryPayload) -> Option<ValidatorErr> {
|
|||
};
|
||||
|
||||
if input > 99 {
|
||||
return Some(ValidatorErr::from_str(t!("rtc.range_err", locales::LANG)))
|
||||
return Some(ValidatorErr::from_str(t!("rtc.range_err", locales::LANG)));
|
||||
}
|
||||
|
||||
None
|
||||
|
|
@ -818,7 +882,7 @@ fn rtc_ux_validate_hour(input: TextEntryPayload) -> Option<ValidatorErr> {
|
|||
};
|
||||
|
||||
if input > 23 {
|
||||
return Some(ValidatorErr::from_str(t!("rtc.range_err", locales::LANG)))
|
||||
return Some(ValidatorErr::from_str(t!("rtc.range_err", locales::LANG)));
|
||||
}
|
||||
|
||||
None
|
||||
|
|
@ -833,7 +897,7 @@ fn rtc_ux_validate_minute(input: TextEntryPayload) -> Option<ValidatorErr> {
|
|||
};
|
||||
|
||||
if input > 59 {
|
||||
return Some(ValidatorErr::from_str(t!("rtc.range_err", locales::LANG)))
|
||||
return Some(ValidatorErr::from_str(t!("rtc.range_err", locales::LANG)));
|
||||
}
|
||||
|
||||
None
|
||||
|
|
@ -848,9 +912,8 @@ fn rtc_ux_validate_seconds(input: TextEntryPayload) -> Option<ValidatorErr> {
|
|||
};
|
||||
|
||||
if input > 59 {
|
||||
return Some(ValidatorErr::from_str(t!("rtc.range_err", locales::LANG)))
|
||||
return Some(ValidatorErr::from_str(t!("rtc.range_err", locales::LANG)));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue