Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Re: Code review: Start and End markers to ensure Serial data is valid

by roboticus (Chancellor)
on Aug 04, 2019 at 17:30 UTC ( [id://11103871]=note: print w/replies, xml ) Need Help??


in reply to Code review: Start and End markers to ensure Serial data is valid

stevieb:

At first, I was going to give a reply somewhat like haukex's, but use a table-driven state machine, but as haukex already mentioned state machines with implementations, I thought I'd just talk about how I try to ensure my state machine code has as few error(s) as possible. Heh, I intended to make it short, but it still rambled on a bit longer than expected. 8^P

First, even for a simple state machine, I'll sketch it out on a piece of paper with plenty of whitespace, as I tend to draw/erase a good bit while refining it. I'll also make the nodes kinda big with some partitions in them, like this:

+-----------------------+ | Node_Name (notes) | +-------+--------+------+ | Enter | Remain | Exit | +-------+--------+------+

Generally, I try to keep the Enter/Remain/Exit boxes empty, but those are the boxes I use to document special actions to take when entering the node from a different node, when remaining in the same node after consuming an event, or to perform immediately before exiting the node. Your code gets complicated if you have too many actions occurring at various times. So I start off by putting in the actions I expect to need where I think I'll need them. As I refine the machine, I'll try to remove things or move them to places that are more consistent with the rest of the machine to simplify implementation. I also allow edges to have actions, which I'll put in parenthesis, if I need them.

So I tried to draw a machine for your problem and I got this far:

+--------------------+ +--------------------+ | Idle |--[BEG]->| Open | +------+------+------+ +------+------+------+ | | | |<-[RST]--|cnt=1 | | | +------+------+------+ +------+------+------+ ^ ^ ^ | ^ | | | +-[RST]-+ [eps:cnt==3] | [BEG(++cnt)] [RST] [eps:cnt==3] | | | [END(--cnt)] | (trim,publish) | | | | | | | | +-----+ +--------------------+ | v | Close | | +--------------------+ +------+------+------+ +--| Data | |cnt=1 | | |<-[END]--+-----+-------+------+ +------+------+------+ | |addChar| | | ^ +-----+-------+------+ [END(--cnt)] | [BEG(++cnt)] | [else] | | | +---------+

(Whew! ASCII art is difficult to draw as well as ugly. I should really try to start learning the Perlmonks codebase and try to implement my dream extension: a *limited* SVG handler to allow simple text/line/box/circle drawings.)

During and after drawing a machine, I like to imagine what the problems are at each possible state, and look for edges that aren't handled to see what implications arise. Thinking about the variables we want to use to capture the state and looking at what cases we want to handle when examining each edge, you can come up with various questions and/or test cases to think about. A few I came up with are:

  • Are the BEG/END delimiters allowed to be inside the data?
    "[[[data[more[data]]]" ==?== "datamoredata"
    "[[[data[[[]data]]]" ==?== ( "data[[[]data" or "data[[[data" )
  • Do the BEG/END delimiters allow you to escape-out noisy junk?
    "[[[data]junk[data]]]" ==?== "datadata"

After coming up and deciding on what to do about all the special cases, I then start to review the machine to see how to simplify it. By this, I don't necessarily mean to reduce the number of states and/or edges (though if possibilities arise, I'll certainly try to do so). Rather, I want to simplify the implementation of the machine.

Having actions occurring in various places in the machine makes implementation confusing. For example, I have actions occurring on edges, and in the onEntry, and onRemain slots on some nodes. Sometimes a bit of thinking can let you move some actions around to reduce the number of places you have to think about actions.

Another thing in a state machine that can cause complexity are "special" edge types, such as the "else" edge type (meaning anything but END/BEG on the Close state) and the epsilon nodes that activate if we're in a state (such as Open/Close) and a condition happens to be true.

Sometimes you can simplify the code by adding edges/nodes to the state machine: it may make the machine a bit larger, but can let you put actions in more consistent locations and/or let you remove some custom edge types you may otherwise need.

Ah, well, enough rambling on this. I've got a couple Raspberry Pi computers to bring on-line. BTW: Since you're the Perlberry Pi guy, is there a hardcopy option available or coming soon for "Programming the Raspberry Pi with Perl"? I'm not really an eBook sorta guy, I'd rather buy something I can put on the bookshelf. (Yeah, I frequently print PDFs so I can tuck 'em in my clipboard and read 'em at lunch or whatever.)

Edit: Added code tags to test cases to prevent linkification.

...roboticus

When your only tool is a hammer, all problems look like your thumb.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11103871]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (5)
As of 2024-04-16 22:35 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found