I am writing a program-emulator of instrument operation (several at once). Data exchange "occurs" conditionally over the RS-485 network, i.e. The program connects to the network and adds "virtual devices" to the network. It is implemented like this: 1. There is an object "data bus". Essentially a container of objects of the same type. Receiving from one object Send makes distribution to all others. 2. Bus objects. They may have different functionalities, but they implement one basic interface with the Receive function (data reception from the bus). There are two types of bus objects: "virtual device" - (based on the received data, it does the calculation and "sends" the result to the "bus"), "data source" (for example, the object opens a COM port, or TCP, or some other, and sends the data to the "bus", and from the bus back to the port). There is no limit on the number of any type of bus objects (i.e. there may be 10 COM ports).

So here. Everything works fine. But I would like to put all this in a separate thread. What it is for - all objects "initiate" events (TNotifyEvent), for example OnDataReceive, OnChanged, etc. Processing (visual reflection of the accompanying information in the program window) of these events may take a long time (critical for emulation). Because of what the master device (the real device on the RS-485 bus) "loses" virtual devices.

Fully implementation can not imagine (and therefore implement, not knowing exactly what to do). It looks like this: - Create an object "bus" - Create-Add the necessary objects to the "bus" - Start the stream (some method of the bus "RunEmulation"), start the "data sources" objects. - All data from the "sources" are sent to the stream of emulation, is sent to the recipients, processing. - In the case of "initiation" of an event, the bus sends PostMessage with the necessary information and the bus already calls the main thread OnDataReceive, OnChanged, etc. (then the messages will be queued, and user processing will not affect the work of the emulation flow)

Something like this. Can someone solve a similar problem or just have thoughts on the right approach to implementation

  • In fact, you described everything correctly: the TBus (bus) stream initializes the TDevice stepchildren, and then “slumbers” (actually makes Sleep on a timer or WaitForSIngle / MultipleObjects - here you need to look specifically at how often and in what mass events occur). Depending on the ideology, you then either directly synchronize the event, or send it through the chain to the queue - Viktor Tomilov
  • If I emulate a Master device, then, accordingly, the requests come with the interval that I set. But the Master "in hardware" sends requests in a continuous stream with waiting for a response in milliseconds (in reality, even less), which suggests the distinction - processing requests in one thread, visualization - in another (here the time frames are not critical) . - dr. FIN
  • So often? Those. Can I arrive with a frequency of once every 0.1 ms? This is already critical: in fact, you only have 200 processor ticks to handle the event. Somehow a little. - Alexey Kozlov
  • Yes, so often. The master device polls 255 devices in turn (cyclically) addressable, they in turn report changes (that is, recalculation occurs only by user actions) and inform the master of the recalculation result. This all happens fairly quickly. The hitch is that the master, having received new data from the device, informs this device about the incorrectness of the “input”. The device then calls the user method. And if the user simply corrects the error - everything is ok. But if Showmessage is displayed to the user, then there is a problem that requires putting it into a separate thread. - dr. FIN
  • You reason correctly. Your every device and bus should work each in its own stream. And the notification of the user about events should be through PostMessage in a window of the main flow. But the interaction of devices and buses must be done through a queue of commands and events, and not through PostMessage. PostMessage assumes the presence of a window into which it can be sent, but it does not have threads. Why do you need to fasten virtual windows to threads? - kot-da-vinci

1 answer 1

Thank you all. The following turned out: 1) A stream for processing messages:

TEmulatorThread = class(TThread) private FOwnerHandle: THandle; protected procedure Execute; override; property OwnerHandle: THandle read FOwnerHandle; public constructor Create(OwnerHandle: THandle); end; constructor TEmulatorThread.Create(OwnerHandle: THandle); begin FOwnerHandle := OwnerHandle; inherited Create(False); end; procedure TEmulatorThread.Execute; var msg: TMsg; begin with TDataBus.Create(OwnerHandle) do try while not Terminated do begin try while PeekMessage(msg, 0, 0, 0, PM_REMOVE) do if msg.message = WM_TERMINATE then Terminate else DispatchMessage(msg); if not Terminated then WaitMessage; except PostMessage(OwnerHandle, WM_EXCEPTION, Integer(AcquireExceptionObject), 0); end; end; finally Free; end; end; 

2) The emulator itself (receiver messages from the data bus):

 constructor TEmulator.Create; begin inherited; FDataBus := nil; FWindowHandle := AllocateHWnd(WindowProc); FThread := TEmulatorThread.Create(FWindowHandle); end; destructor TEmulator.Destroy; begin PostThreadMessage(FThread.ThreadID, WM_TERMINATE, 0, 0); FThread.WaitFor; FThread.Free; DeallocateHWnd(FWindowHandle); inherited; end; procedure TEmulator.WindowProc(var Message: TMessage); begin with Message do case msg of WM_DATABUS_NOTIFY: case WParam of DB_CREATED: FDataBus := TDataBus(LParam); DB_DESTROYING: FDataBus := nil; DB_OBJECT_ADDED: ; DB_OBJECT_DELETED: ; end; WM_OBJECT_NOTIFY: ; WM_EXCEPTION: begin raise Exception(WParam); end; else DefWindowProc(FWindowHandle, msg, WParam, LParam); end; end; 

3) Data bus (emulator alert):

 procedure TBONDataBus.AfterConstruction; begin PostMessage(FOwnerHandle, WM_DATABUS_NOTIFY, DB_CREATED, Integer(Self)); end; procedure TBONDataBus.BeforeDestruction; begin PostMessage(FOwnerHandle, WM_DATABUS_NOTIFY, DB_DESTROYING, 0); inherited; end; 

All hetera / setters of objects in the flow are covered with critical sections.