Conversation

Jarkko Sakkinen

next step in my zmodem2 crate is to make send and receive asynchronous so that progress can be tracked. then i’m ready to integrate this to my other small project, tior. #zmodem #rustlang

1
1
3

Jarkko Sakkinen

Edited 1 year ago
I was going to use `fsmentry` crate for the state machine but then i ended up to something way more simpler: https://github.com/jarkkojs/zmodem2/commit/86a4af491448cf30a718a43dd1e5998b5b1863c6
1
0
0
Moving file through the serial post is tricky, not because of protocols but because mostly it is based on launching rz and sz. This indirection makes impossible to implement an user experience that people used to transferring file such as showing progress, canceling the transfer and such and so forth. I'm fed up with this difficulty so that's where the motivation comes from.
1
0
0

After looking that for some time my state diagram for “sz” is simply this:

fsmentry::dsl! {
    #[derive(Debug)]
    pub Mode {
        WaitingForReceiver -> Starting;
        Starting -> Sending;
        Starting -> WaitingForPosition;
        Sending -> WaitingForPosition;
        WaitingForPosition -> Sending;
        Sending -> Ending;
    }
}

This is scales exactly of the normal serial transfer. ZCHALLENGE/ZCOMPL, “corporate zmodem” (yes it was a real concept back in the say) and running commands by the request of the sender are definitely out of scope. So pretty clean and understandable in the end when you carve deep enough…

1
0
0

Jarkko Sakkinen

Edited 1 year ago

This should clarify how it is organized:

/// Map the previous frame type of the sender and incoming frame type of the
/// receiver to the next packet to be sent.
///
/// NOTE: ZRINIT is used here as a wait state, as the sender does not use it for
/// other purposes. Other than tat the states map to the packets that the sender
/// sends next.
const fn next_state(sender: Option<Type>, receiver: Type) -> Option<Type> {
    match (sender, receiver) {
        (None, Type::ZRINIT) => Some(Type::ZFILE),
        (None, _) => Some(Type::ZRQINIT),
        (Some(Type::ZRQINIT), Type::ZRINIT) => Some(Type::ZFILE),
        (Some(Type::ZFILE), Type::ZRPOS) => Some(Type::ZDATA),
        (Some(Type::ZFILE), Type::ZRINIT) => Some(Type::ZRINIT),
        (Some(Type::ZRINIT), Type::ZRPOS) => Some(Type::ZDATA),
        (Some(Type::ZDATA), Type::ZACK) => Some(Type::ZDATA),
        (Some(Type::ZDATA), Type::ZRPOS) => Some(Type::ZDATA),
        (Some(Type::ZDATA), Type::ZRINIT) => Some(Type::ZFIN),
        (Some(Type::ZFIN), Type::ZFIN) => None,
        (_, _) => None,
    }
}

No reason to add complexity with fsmentry. This is a nice clean const function.

1
0
0

My crate has the implementation now in 933 lines and not a lot of dependencies. It starts to be in a shape that is not a huge stretch to make it fully no_std even. Not the first priority tho but entirely possible.

After I have a similar state function for the receiver I’ll look at futures crate, which claims to be no_std compatible. I.e. idea would be to get yield once per iteration.

I’m not interested in async stuff for multi-threading. I’m interested single-threaded sequenced type of stuff, co-operative multitasking…

0
0
0