Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarifying the docs on Write::write #168

Closed
steveklabnik opened this issue Sep 8, 2020 · 4 comments 路 Fixed by #355
Closed

Clarifying the docs on Write::write #168

steveklabnik opened this issue Sep 8, 2020 · 4 comments 路 Fixed by #355
Labels
difficulty: easy Pretty easy to solve priority: medium Medium priority for the Knurling team status: needs PR Issue just needs a Pull Request implementing the changes type: question A question that isn't answered by the docs

Comments

@steveklabnik
Copy link

steveklabnik commented Sep 8, 2020

Hey folks! 馃憢

So, I ran into a fairly big issue today, and that's that I assumed that Write::write would be getting an entire log frame at a time, whereas instead, it gets bits of a log frame each time.

Would you like a docs PR that clarifies this? I figured I'd ask before I started looking at it, and I'm not 100% sure what exactly I should say...

(This came up when trying to polyfill #108)

@jonas-schievink
Copy link
Contributor

Yeah, we'd love a PR that clarifies the docs here!

I think we might also want to add a method to Write that is called before or after each log frame, because that allows adding framing in the transport (and sounds like it would help you as well). Not sure about the best design here though.

@japaric
Copy link
Member

japaric commented Sep 9, 2020

I think in terms of documenting the write method I would say "whether the bytes argument is an entire log frame or a fragment of it is unspecified". It seems unlikely that we'll ever be able to pass a whole log frame as bytes: that would require a buffer large enough to hold the frame and frames can be arbitrarily large but as future proofing goes I would like to not set in stone that it will always be a fragment.

In terms of attaching a header or footer to the frame I think something like adding start_of_frame and end_of_frame methods to Write as well as some sort of associated State type to it seems like it should be flexible enough. At least that seems like it would cover adding a CRC as a footer to detect transmission errors; I'm not sure if that would be enough to add COBS framing (is COBS "streaming" possible? I don't know). Knowing in what kind of ways people want to extend frames would help pick a design here.

@MathiasKoch
Copy link
Sponsor Contributor

MathiasKoch commented Sep 9, 2020

In terms of attaching a header or footer to the frame I think something like adding start_of_frame and end_of_frame methods to Write as well as some sort of associated State type to it seems like it should be flexible enough. At least that seems like it would cover adding a CRC as a footer to detect transmission errors; I'm not sure if that would be enough to add COBS framing (is COBS "streaming" possible? I don't know). Knowing in what kind of ways people want to extend frames would help pick a design here.

I think that would be very helpful!

At least it would allow for something like (assuming start_of_frame would contain the total length of the frame):

static mut LOGPRODUCER: Option<LogProducer> = None;

struct LogProducer {
    producer: bbqueue::Producer<'static, LogBufferSize>,
    encoder: Option<(bbqueue::GrantW<'static, LogBufferSize>, cobs::CobsEncoder<'static>)>
}

impl LogProducer {
    pub fn new(producer: bbqueue::Producer<'static, LogBufferSize>) -> Self {
        Self {
            producer,
            encoder: None
        }
    }

    pub fn start_encoder(&mut self, len: usize) -> Result<(), ()> {
        if self.encoder.is_some() {
            return Err(())
        }
        match self.producer.grant_exact(cobs::max_encoding_length(len)) {
            Ok(mut grant) => {
                let buf = unsafe { grant.as_static_mut_buf() };
                self.encoder = Some((grant, cobs::CobsEncoder::new(buf)));
                Ok(())
            }
            Err(_) => Err(())
        }
    }

    pub fn encode(&mut self, bytes: &[u8]) -> Result<(), ()> {
        if let Some((_, ref mut encoder)) = self.encoder {
            encoder.push(bytes)
        } else {
            Err(())
        }
    }

    pub fn finalize_encoder(&mut self) -> Result<(), ()> {
        if let Some((grant, encoder)) = self.encoder.take() {
            let size = encoder.finalize()?;
            grant.commit(size);
            Ok(())
        } else {
            Err(())
        }
    }
}

#[defmt::global_logger]
struct Logger;

impl defmt::Write for Logger {
    fn start_of_frame(&mut self, len: usize) {
        handle().start_encoder(len).ok();
    }

    fn end_of_frame(&mut self) {
        handle().finalize_encoder().ok();
    }

    fn write(&mut self, bytes: &[u8]) {
        handle().encode(bytes).ok();
    }
}

#[inline]
fn handle() -> &'static mut LogProducer {
    unsafe {
        match LOGPRODUCER {
            Some(ref mut x) => x,
            // FIXME: Should not panic here!
            None => panic!(),
        }
    }
}

@steveklabnik
Copy link
Author

I think we might also want to add a method to Write that is called before or after each log frame, because that allows adding framing in the transport (and sounds like it would help you as well). Not sure about the best design here though.

Yep! That would help, as it is effectively what I am doing.

I think in terms of documenting the write method I would say "whether the bytes argument is an entire log frame or a fragment of it is unspecified". It seems unlikely that we'll ever be able to pass a whole log frame as bytes: that would require a buffer large enough to hold the frame and frames can be arbitrarily large but as future proofing goes I would like to not set in stone that it will always be a fragment.

馃憤

In terms of attaching a header or footer to the frame I think something like adding start_of_frame and end_of_frame methods to Write as well as some sort of associated State type to it seems like it should be flexible enough. At least that seems like it would cover adding a CRC as a footer to detect transmission errors; I'm not sure if that would be enough to add COBS framing (is COBS "streaming" possible? I don't know). Knowing in what kind of ways people want to extend frames would help pick a design here.

So for me, the framing is purely a work around for the lack of structured logging. And also using structured logging is kind of a workaround for the fact that defmt seems to be designed around the idea of there being one main program, and hence logger, running on the entire system. Whereas I'm in a position where I'm implementing an OS, and need each program/process/task to have its own logger. This is pretty far afield of the original issue but if yinz ever want to talk about it, I'm happy to fill you in.

@jonas-schievink jonas-schievink added this to Incoming in Issue Triage Nov 10, 2020
@jonas-schievink jonas-schievink added difficulty: easy Pretty easy to solve priority: medium Medium priority for the Knurling team status: needs PR Issue just needs a Pull Request implementing the changes type: question A question that isn't answered by the docs labels Nov 10, 2020
@jonas-schievink jonas-schievink moved this from Incoming to Triaged in Issue Triage Nov 10, 2020
bors bot added a commit that referenced this issue Jan 21, 2021
354: f64 support r=japaric a=jonas-schievink

Closes #346

355: Clarify docs on `Write::write` r=japaric a=jonas-schievink

Fixes #168

Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
@bors bors bot closed this as completed in e67e6f6 Jan 21, 2021
@bors bors bot closed this as completed in #355 Jan 21, 2021
Issue Triage automation moved this from Triaged to Closed Jan 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
difficulty: easy Pretty easy to solve priority: medium Medium priority for the Knurling team status: needs PR Issue just needs a Pull Request implementing the changes type: question A question that isn't answered by the docs
Projects
No open projects
Development

Successfully merging a pull request may close this issue.

4 participants