The purpose of this code is important, so here's a quick rundown.
This code runs controls a physical machine which utilizes a camera and a moving stage holding an object to be imaged. Imaging can be broken down into two parts; exposing (collecting photons) and readout (reading the formatted image from the camera over USB).
Stage moves are performed asynchronously. The object being imaged is broken up into many (700+) "tiles" which must be imaged separately, so one move + expose + readout for each tile.
An optimization which saves ~3 minutes per imaging operation is to begin moving to tile N+1 after exposing tile N but before readout has completed. We then wait for readout to complete, expose the current tile, and repeat the process again.
This leads to a loop structure that, while correct, is a bit confusing. It's not a horrible piece of code, but it's been bugging me and I'd like to spend some time today refactoring it. Problem is, I haven't come up with any great ideas.
Here's the loop structure:
void ScanLoopExample()
{
var tiles = GetAllTiles();
var firstTile = tiles.First();
// get into position for the first tile
MoveStageAsync(firstTile.X, firstTile.Y, firstTile.Z);
foreach(var nextTile in tiles.Skip(1))
{
// wait for the move to complete
WaitForMotionEnd();
// expose over the first tile
ExposeTile();
// get into position for the next tile
// we *must* make this move right here in
// order to save time moving while the camera is busy
MoveStageAsync(nextTile.X, nextTile.Y, nextTile.Z);
// wait for the image to arrive from the camera,
// we cannot expose again until this completes
WaitForReadout();
}
// because we only moved to the last tile
// without capturing, capture it here
WaitForMotionEnd();
ExposeTile();
WaitForReadout();
}
Like I said, not the worst code ever, but there are a couple of things I don't like:
I don't like that part of the logic is separated from the loop itself (the move to the first tile).
I don't like that the foreach variable is the tile to move to, not the tile being captured. It has slightly confused two coworkers I showed it to (though they got it quick enough after I explained the process).
I don't like that the capture code (wait + expose + readout) must be repeated after the loop is finished.
I've tried a couple of variants, but nothing I have come up with can get all of the capture logic in the body of the loop. This is a bit of an obsessive need to make the code as pretty as possible, but hey, it's the weekend and I'm not on the clock.
The tricky bit is that we must begin the asynchronous move of the stage after exposure but before readout. That places a strict rule on the ordering of operations in the loop.