I am having a difficult time determining where my IO logic should reside within my application. In this application there are multiple IO devices both USB and serial. I currently have the idea to separate the communication with the devices from their control forms and control threads; ie click a button on a form and the device does something or some series of actions leads to a non-UI thread commanding the device. I have started to work with the serial devices. So I have first wrote a class RS232Comm that wraps the System.IO.Ports.SerialPort class into something nice and thread safe (lock on the Read and Write routines) so that multiple threads can access the device simultaneously. From here I plan to subclass from the RS232Comm for each serial device, all the subclasses will be singleton objects forcing all communication to go through the singleton. My current issue is where should the logic about how the device acts and its state lie? Basically, if I want to query a device for its current state, should the state be held in some object and be passed around to each controlling class (possibly an event NewStateObject)? When a controlling class wants to send a command to the device, should it check the input before calling on the singleton, or should the singleton check the input before a write to the serial port?I feel like it makes more sense for the singleton to check the input and output for errors along with holding the devices state object. This way code is not duplicated in the control classes. But I feel like this will really bloat the singleton classes to hundreds of lines. Outside of the scope of my project, if I were to only have a 1 to 1 correspondence or IO class to control form/logic, would it make more sense to merge them into one class that does it all for that device?
How close should a IO class/logic be to a form or thread that controls the IO device?
design;io
You probably want more intermediate layers.  One to lock or queue at a transactional level, so a device can be dedicated to one caller for multiple sequential requests and responses that must not be interrupted.  And one for the device from an application level.  For example, if you're talking to an LED matrix, this layer's API would be in terms of setting or querying the state of individual pixels.  Depending on your application, you may want additional layers above that to aggregate the common behavior of different devices.Resist the urge to make your classes too large, especially on hardware-specific code.  You want hardware-specific code to be as small and isolated as possible.