Skip to content

Publish/Receive Loop

End-to-end flow when one user publishes a module and another receives it. The receiving side required no new codeCPulseConnection.ProcessPublishQueue already handled SettingsType.CORE_MODULE. This page documents how the round-trip works so future maintainers know where each piece lives.

Round-trip diagram

sequenceDiagram
    participant A as User A (publisher)
    participant DB as Database (PublishQueue column)
    participant B as User B (receiver)
    participant VFS as Pulse.UserSettings (B's VFS)
    participant Module as CCoreModule (B's instance)

    A->>A: Right-click module header
    A->>A: Click "Publish Module..."
    Note over A: btnPublish_ItemClick:<br/>builds CPublishInfo_CSD_1<br/>SettingsType = CORE_MODULE<br/>PublishMap("Module") = ModuleID<br/>SerialData = CCoreModuleData
    A->>DB: ShowPublishForm writes to<br/>PublishQueue rows of target users

    Note over B: Later — User B clicks Refresh
    B->>DB: Read own PublishQueue
    B->>B: ProcessPublishQueue() drains the queue
    Note over B: For SettingsType.CORE_MODULE:<br/>writes SerialData into<br/>VirtualFile("\<ModuleID>.core")
    B->>VFS: Update virtual file

    B->>B: Main.LoadFromFiles()
    loop For each core module
        B->>Module: Settings.LoadFromVirtualFile()
        Module->>Module: Settings_DataLoadEnd fires
        Module->>Module: Disposes inner MyTabPage instances
        Module->>Module: Recreates from CoreModuleData.m_BoxModuleTabs
    end
    Module->>B: Tab control rebuilt with A's tabs

Publisher side (User A)

The new btnPublish_ItemClick handler in FCoreModule_Popup.vb does three things:

curCoreModule.Settings.RaiseOnDataSave()  ' (1)

Dim newPF As New SaveAndShare.CPublishInfo_CSD_1(
    curCoreModule.Settings.m_SerialData,  ' (2)
    SaveAndShare.SettingsType.CORE_MODULE)
newPF.PublishPolicy.PublishPolicyCode = SaveAndShare.PolicyCode.Everyone
newPF.PublishMap.Add("Module", curCoreModule.ModuleInfo.ModuleID)  ' (3)
newPF.ShowPublishForm(Instance.MainForm)
  1. RaiseOnDataSave() flushes the in-memory tab state into CoreModuleData.m_BoxModuleTabs. Without this, recently-edited tabs would not be in the published payload. CCoreModule.Settings_DataSaveBegin (around line 374 of CCoreModule.vb) is the handler that does the flush.
  2. m_SerialData is the CCoreModuleData itself. CPublishInfo_CSD_1 accepts any serializable payload.
  3. PublishMap("Module") = ModuleID is the key the receiver uses to locate the right virtual file. This shape is what ProcessPublishQueue expects (see below).

ShowPublishForm is the existing UI — picks recipients, writes entries into each recipient's PublishQueue column.

Receiver side (User B)

Triggered when User B clicks the global Refresh button (Main.btnRefreshCurrent_MouseDown around line 1457):

Main.vb — global Refresh handler (excerpt)
SaveSettingsToVirtualFileAndDatabase()
If Pulse.Connection.ProcessPublishQueue Then
    LoadFromFiles()
End If

ProcessPublishQueue reads User B's row from the USER_CONTROL table, deserializes the PublishQueue column into a Queue(Of CPublishInfo_CSD_1), and drains it:

CPulseConnection.vb — ProcessPublishQueue (CORE_MODULE branch)
While PublishQueue.Count > 0
    Dim curPublishInfo = PublishQueue.Dequeue()
    Select Case curPublishInfo.SettingsType
        Case SettingsType.CORE_MODULE
            Dim szCoreModuleVFBName As String = "\" & curPublishInfo.PublishMap("Module") & ".core"
            Pulse.UserSettings.VirtualFile(szCoreModuleVFBName).SerializableObject = curPublishInfo.SerialData
        ' ... (TAB branch is similar)
    End Select
End While

Two things are happening:

  • Lookup key. The path \<ModuleID>.core is the canonical virtual file name for a core module's settings. Same key is used everywhere else for LoadFromVirtualFile / SaveToVirtualFile.
  • In-memory write. The new CCoreModuleData is now in User B's Pulse.UserSettings virtual file system, but the live CCoreModule instance is still showing the old data.

The "live update" happens inside LoadFromFiles (around line 2106 of Main.vb):

For Each curCoreModule As CCoreModule In m_CoreModules
    curCoreModule.Settings.LoadFromVirtualFile()
Next

LoadFromVirtualFile reads from Pulse.UserSettings.VirtualFile(...) (which now contains A's payload), fires DataLoad, and CCoreModule.Settings_DataLoadEnd rebuilds the tab control:

CCoreModule.vb — Settings_DataLoadEnd (rebuild loop)
m_TabControl.TabPages.Clear()
For Each curTab As CBoxModuleTabData In CoreModuleData.m_BoxModuleTabs
    Dim newTabPage As New MyTabPage(True)
    m_TabControl.TabPages.Add(newTabPage)
    newTabPage.SerialData = curTab
Next
m_TabControl.PerformLayout()

When User B clicks the refreshed module tab, the new MyTabPage instances run their LoadSettings() / LoadData() flow on focus, populating each Pulse box with live data.

Why no new code on the receive side

The shape btnPublish_ItemClick produces — SettingsType.CORE_MODULE, PublishMap("Module") = ModuleID, SerialData = CCoreModuleData — exactly matches what ProcessPublishQueue already expects. The downstream chain (LoadFromFilesLoadFromVirtualFileSettings_DataLoadEnd) was also already in place for handling settings reloads from any source (user switch, manual import, etc.), so it picks up the published payload without needing to know it came from a publish.

This means the publish-receive loop for Load Module from File… is also implicit: a file load uses the same Settings.RaiseOnDataLoad() trigger, so the rebuild path is shared.