Background: I'm exploring a solution to control automated (theatrical/concert) lighting. If you're unfamiliar with the DMX protocol, basically it sends 512 bytes over the wire, about 40-ish times per second, and the receiving light fixtures that are mapped to certain bytes (channels) set themselves accordingly. So if a light has its dimmer on channel 7, and the value coming is 255, it's fully on, and if it's 0, it's dark. There are a few intermediate network protocols in between computers and a DMX interface (primarily Art-Net and sACN if you're super curious), so the code I'm writing is just spraying the network with packets formatted for one of the network protocols every 25ms (40 times per second).
The sending of the bytes is easy enough, and surprisingly there isn't a lot of overhead in packaging the byte arrays. However, a number of things can be setting values on those arrays, like manual inputs ("faders" on screen, if you will), or algorithms intended to fade between lights or make them move or whatever. I have different ideas about how to prioritize those and which thing "wins" for the periodic refresh. That part is less important than how to make all of those things execute on the refresh. None of them individually take more than a millisecond or two, but they all need to finish before the next refresh.
In terms of code, there's a timer (the System.Timers
variety) that looks like this:
_sendingTimer = new System.Timers.Timer();
_sendingTimer.Interval = _networkInterface.SendingInterval; // 25ms
_sendingTimer.AutoReset = false;
_sendingTimer.Elapsed += async (_, _) =>
{
SendTheData(); // Takes on average a ms or two
_stateManager.OnUpdateCycle();
_sendingTimer.Start();
};
_sendingTimer.Start();
The _stateManager
bit looks like this:
public void OnUpdateCycle()
{
UpdateCycled.Invoke(this, EventArgs.Empty); // Can these be run in parallel?
}
public event EventHandler UpdateCycled;
There could potentially be a bunch of things attached to that event, which, as I said, mostly do math like figuring out in a cross-fade between cues where the dimmer should be set relative to where it was in the last cycle. These actions are pretty light weight, maybe running as long as 2ms if I have debugging attached or some logging. My concern though is that if I have 25 of them running, they won't all complete, since event handlers execute serially.
Questions:
- Is this timer strategy the right set up so that it runs every interval with some reasonable (if imperfect) accuracy, or will long execution of the methods delay the next interval? I think ideally that if for some reason it did not complete by the end of the interval, I would want it to just abandon the previous attempt.
- Is there an efficient way to make the event handlers execute in parallel, and using whatever resources are necessary to complete? Oddly enough, the network protocols are sent as UDP packets, so if they get lost, whatever, the lights will respond to the next one. But if these calculations on the event handler don't complete, the gaps could span multiple cycles, making fading or movement not smooth. Some follow sine curves, so you would definitely see it. (Assume that the values I'm computing are relative to time, and not the previous values.)