twsapi@groups.io | Heartbeats and Healthchecks (2024)

Likes

" ); } else { wrap = '

Attachments:

    '; count = 0; for (i = 0; i < response.length; i++) { if (response[i].Inline == false) { wrap += '
  • ' + response[i].Name + ' (' + response[i].HumanSize + ')
  • "; count++; } } wrap += '

'; if (count > 0) { $('#attachments' + id).replaceWith(wrap); } else { $('#attachments' + id).replaceWith( "

" ); } } }); } var modTimeoutId; function modOnFormChange(id, draftid, groupurl, csrf) { clearTimeout(modTimeoutId); if (modSaving == true) { modTimeoutId = setTimeout(function () { // Runs 1 second (1000 ms) after the last change modOnFormChange(id, draftid, groupurl, csrf); }, 1000); return; } modTimeoutId = setTimeout(function () { // Runs 1 second (1000 ms) after the last change modSaveDraft(id, draftid, groupurl, csrf, false); }, 1000); } var modSaving = false; // modSaveDraft saves the current form state in the draft. function modSaveDraft(id, draftid, groupurl, csrf, onLeave) { if (draftid == 0) { console.log("DraftID 0, not modSaving"); return; } console.log("DELETEDDRAFT IS:", modDeletedDraft); console.log("DESTROYEDEDITOR IS:", modDestroyedEditor); if (modDeletedDraft == true) { console.log('NOT SAVING BECAUSE OF DELETED'); return; } if (modDestroyedEditor == true) { console.log('NOT SAVING BECAUSE OF DESTROYED'); return; } modSaving = true; console.log('modSaving'); var fromval = $('#from' + id).val(); var subject = $('#subject' + id).val(); var body = $('#editor' + id).val(); var bodytype = $('#bodytype' + id).val(); var replyto = $('#replyto' + id).val(); var special = '0'; if ($('#special').prop('checked') == true) { special = '1'; } var bccme = '0'; if ($('#bccmecheck' + id).prop('checked') == true) { bccme = '1'; } var bccall = '0'; if ($('#bccall' + id).prop('checked') == true) { bccall = '1'; } var saveval = '1'; if (onLeave == true) { saveval = '2'; } var hashtags = $('#hashtags').val(); upload = { draftid: draftid, csrf: csrf, from: fromval, subject: subject, body: body, bodytype: bodytype, special: special, replyto: replyto, bccme: bccme, bccall: bccall, hashtags: JSON.stringify(hashtags), mid: id, save: saveval }; let opts = { url: groupurl + '/draftop', cache: false, data: upload, method: 'POST', xhrFields: { withCredentials: true }, dataType: 'json' }; if (modUnloading == false) { // if we are unloading we don't want to retry, because sometimes // that can result in a spurious error, esp on Firefox opts.retryCount = 5; opts.retryVerify = modRetryVerify; } $.ajax(opts).done(function (response) { // Do something with the request console.log('saved'); modSaving = false; }); } // called to see if we need to continue retrying function modRetryVerify() { if (modDeletedDraft == true || modDestroyedEditor == true) { return false; } return true; } // stop modSaving drafts when we do a submit var postVar = null; // Code to find and return a selected piece of HTML. function modGetSelection(id) { var flag = 0; var sel = document.getSelection(); var selText = ''; id = 'msgbody' + id; var forkfork = document.getElementById(id); if (sel.rangeCount > 0) { var range = sel.getRangeAt(0); var test = range.cloneContents(); var clonedSelection = ''; if (typeof test.getElementByID != 'undefined') { clonedSelection = range.cloneContents().getElementById(id); } if (clonedSelection) { selText = clonedSelection.innerHTML; } else { clonedSelection = range.cloneContents(); var startNode = sel.getRangeAt(0).startContainer.parentNode; //console.log(modIsChild(startNode, forkfork)); if (modIsChild(startNode, forkfork)) { var div = document.createElement('div'); div.appendChild(clonedSelection); selText = div.innerHTML; } } } return selText.toString(); } function modIsChild(child, parent) { if (child === parent) return true; var current = child; while (current) { if (current === parent) return true; current = current.parentNode; } return false; } return { InitEditor: function ( id, bodyType, draftid, groupurl, csrf, handleAttachments, noFontChanges, isReply, isWiki, body, sig, onInitFunc ) { if (typeof onInitFunc === 'undefined') { onInitFunc = null; } document.body.addEventListener('htmx:beforeSwap', modDestroyAllEditors, {once: true}); modDeletedDraft = false; modDestroyedEditor = false; modUnloading = false; $('#preview' + id).hide(); $('#addattachments' + id).hide(); $('#return' + id).hide(); $('#markdownlink' + id).hide(); if (bodyType == 'html') { if (sig != '') { $('#editor' + id).val(sig); //tinyMCE.get('editor'+id).setContent(sig); } editor.initHTMLEditor( id, draftid, groupurl, csrf, handleAttachments, noFontChanges, isReply, isWiki, body, sig, onInitFunc ); } else { if (sig != '') { $('#editor' + id).val(sig); } editor.initPlainEditor(id, bodyType, groupurl, handleAttachments, sig); } }, initHTMLEditor: function ( id, draftid, groupurl, csrf, handleAttachments, noFontChanges, isReply, isWiki, body, sig, onInitFunc ) { if (typeof onInitFunc === 'undefined') { onInitFunc = null; } // extras: print, emoticons, image, insert, media, print /* All plugins: 'advlist autolink lists link image print preview hr anchor pagebreak', 'searchreplace wordcount visualblocks visualchars code fullscreen', 'insertdatetime media nonbreaking save table contextmenu directionality', 'emoticons template paste textcolor colorpicker textpattern imagetools codesample toc' */ modDeletedDraft = false; modDestroyedEditor = false; modUnloading = false; let attachments = ''; if (handleAttachments == 0 || handleAttachments == 3) { attachments = ' addPictures addAttachments'; } let fontchanges = ''; if (noFontChanges == false) { fontchanges = ' fontselect fontsizeselect forecolor backcolor'; } let fontawesome = ' charmap'; let forceRootBlock = false; if (isWiki == true) { attachments += ' addWikiImage addWikiLink addWikiTOC'; fontawesome = ' fontawesome'; // BORK fontawesome = ''; forceRootBlock = 'p'; } let toolbar1 = 'styleselect bold italic bullist numlist link blockquote alignleft aligncenter alignright' + attachments + ' advancedToolbar'; let toolbar2 = 'strikethrough underline hr alignjustify' + fontchanges + ' removeformat' + fontawesome + ' outdent indent undo redo preview code'; let small_toolbar1 = 'bold italic link blockquote' + attachments + ' advancedToolbar'; let small_toolbar2 = 'strikethrough underline hr alignjustify removeformat outdent indent'; let tm_fonts = 'Arial=arial,helvetica,sans-serif;' + 'Arial Black=arial black,avant garde;' + 'Comic Sans MS=comic sans ms;' + 'Courier Neue=courier_newregular,courier;' + 'Helvetica Neue=helvetica neue;' + 'Helvetica=helvetica;' + 'Impact=impactregular,chicago;' + 'Lucida Grande=lucida grande;' + 'Tahoma=tahoma,arial,helvetica,sans-serif;' + 'Times New Roman=times new roman,times;' + 'Verdana=verdana,geneva'; let plugins = [ 'SplitBlockquote', 'advlist autolink lists link image preview hr anchor', 'code fullscreen', 'nonbreaking table charmap', 'textcolor colorpicker imagetools noneditable' ]; let css = '/bootstrap/3.3.6/css/bootstrap.min.css,/bootstrap/3.3.6/css/bootstrap-theme.min.css,/css/groupsio.css,/css/tinymce.css,/fontawesome/all.min.css'; let fontsizes = '8pt 10pt 11pt 12pt 14pt 18pt 24pt 36pt'; let codesample_languages = [ { text: 'C', value: 'c' }, { text: 'C#', value: 'csharp' }, { text: 'C++', value: 'cpp' }, { text: 'CSS', value: 'css' }, { text: 'Go', value: 'go' }, { text: 'HTML/XML', value: 'markup' }, { text: 'Java', value: 'java' }, { text: 'JavaScript', value: 'javascript' }, { text: 'PHP', value: 'php' }, { text: 'Python', value: 'python' }, { text: 'Ruby', value: 'ruby' } ]; let style_formats = [ { title: 'Paragraph', block: 'p' }, { title: 'Header 1', block: 'h1' }, { title: 'Header 2', block: 'h2' }, { title: 'Header 3', block: 'h3' }, { title: 'Header 4', block: 'h4' }, { title: 'Header 5', block: 'h5' }, { title: 'Header 6', block: 'h6' } ]; if (isReply == true) { toolbar1 = 'quoteMessage ' + toolbar1; small_toolbar1 = 'quoteMessage ' + small_toolbar1; } if (document.documentElement.clientWidth > 1000) { tinymce.init({ noneditable_noneditable_class: 'fa', extended_valid_elements: 'span[*]', branding: false, link_context_toolbar: true, default_link_target: '_blank', link_assume_external_targets: true, elementpath: false, forced_root_block: forceRootBlock, content_css: css, relative_urls: false, remove_script_host: false, menubar: false, statusbar: true, plugins: plugins, toolbar1: toolbar1, toolbar2: toolbar2, font_formats: tm_fonts, browser_spellcheck: true, contextmenu: false, selector: '#editor' + id, resize: true, fontsize_formats: fontsizes, style_formats: style_formats, setup: function (teditor) { teditor.on('Init', function (e) { // see if any text is selected toquote = modGetSelection(id); if (toquote != '') { console.log('id=' + id); editor.ShowMessageHistory(id, groupurl, 'html', toquote, sig, true); } else { if (body != "") { console.log("body setContent"); teditor.setContent(body); } else if (sig != "") { console.log("sig setContent " + sig); teditor.setContent(sig); } } if (onInitFunc != null) { onInitFunc(e); } }); teditor.on('BeforeRenderUI', function (e) { teditor.theme.panel .find('toolbar') .slice(1) .hide(); }); teditor.addButton('advancedToolbar', { tooltip: 'Show advanced toolbar', icon: 'fa fa-bars', onclick: function () { if (!this.active()) { this.active(true); teditor.theme.panel .find('toolbar') .slice(1) .show(); } else { this.active(false); teditor.theme.panel .find('toolbar') .slice(1) .hide(); } } }); teditor.addButton('addPictures', { tooltip: 'Add pictures', icon: 'fa fa-image', onclick: function () { modUploaderPrompt("pictures", id, draftid, groupurl, csrf); } }); teditor.addButton('addAttachments', { tooltip: 'Add attachments', icon: 'fa fa-paperclip', onclick: function () { modUploaderPrompt("attachments", id, draftid, groupurl, csrf); } }); if (groupurl != '') { teditor.addButton('quoteMessage', { tooltip: 'Quote post', icon: 'fa fa-comment', onclick: function () { editor.ShowMessageHistory(id, groupurl, 'html', '', sig, false); } }); } if (draftid != '' && draftid != '0' && draftid != 0) { teditor.on('NodeChange', function () { //tinymce.triggerSave(); if (tinymce.activeEditor != null) { let markupStr = tinymce.activeEditor.getContent(); $('#editor' + id).val(markupStr); modOnFormChange(id, draftid, groupurl, csrf); } }); teditor.on('keyup', function () { //tinymce.triggerSave(); let markupStr = tinymce.activeEditor.getContent(); $('#editor' + id).val(markupStr); modOnFormChange(id, draftid, groupurl, csrf); }); } if (isWiki == true) { // special wiki buttons teditor.addButton('addWikiImage', { tooltip: 'Insert image', icon: 'fa fa-image', onclick: function () { $('#ImageModal').modal({}); } }); teditor.addButton('addWikiLink', { tooltip: 'Insert link to wiki page', icon: 'fa fa-book', onclick: function () { $('#LinkModal').modal({}); } }); teditor.addButton('addWikiTOC', { tooltip: 'Insert table of contents', icon: 'fa fa-list-alt', onclick: function () { $('#TOCModal').modal({}); } }); } } }); } else { tinymce.init({ branding: false, link_context_toolbar: true, default_link_target: '_blank', link_assume_external_targets: true, elementpath: false, forced_root_block: forceRootBlock, content_css: css, relative_urls: false, remove_script_host: false, menubar: false, statusbar: true, plugins: plugins, toolbar1: small_toolbar1, toolbar2: small_toolbar2, font_formats: tm_fonts, browser_spellcheck: true, contextmenu: false, selector: '#editor' + id, resize: true, fontsize_formats: fontsizes, style_formats: style_formats, setup: function (teditor) { teditor.on('Init', function (e) { // see if any text is selected toquote = modGetSelection(id); if (toquote != '') { console.log('id=' + id); editor.ShowMessageHistory(id, groupurl, 'html', toquote, sig, true); } else { if (body != "") { console.log("body setContent"); teditor.setContent(body); } else if (sig != "") { console.log("sig setContent" + sig); teditor.setContent(sig); } } if (onInitFunc != null) { onInitFunc(e); } }); teditor.on('BeforeRenderUI', function (e) { teditor.theme.panel .find('toolbar') .slice(1) .hide(); }); teditor.addButton('advancedToolbar', { tooltip: 'Show advanced toolbar', icon: 'fa fa-bars', onclick: function () { if (!this.active()) { this.active(true); teditor.theme.panel .find('toolbar') .slice(1) .show(); } else { this.active(false); teditor.theme.panel .find('toolbar') .slice(1) .hide(); } } }); teditor.addButton('addPictures', { tooltip: 'Add pictures', icon: 'fa fa-image', onclick: function () { modUploaderPrompt("pictures", id, draftid, groupurl, csrf); } }); teditor.addButton('addAttachments', { tooltip: 'Add attachments', icon: 'fa fa-paperclip', onclick: function () { modUploaderPrompt("attachments", id, draftid, groupurl, csrf); } }); if (groupurl != '') { teditor.addButton('quoteMessage', { tooltip: 'Quote post', icon: 'fa fa-comment', onclick: function () { editor.ShowMessageHistory(id, groupurl, 'html', '', sig, false); } }); } if (draftid != '' && draftid != '0' && draftid != 0) { teditor.on('NodeChange', function () { if (tinymce.activeEditor != null) { //tinymce.triggerSave(); let markupStr = tinymce.activeEditor.getContent(); $('#editor' + id).val(markupStr); modOnFormChange(id, draftid, groupurl, csrf); } }); teditor.on('keyup', function () { //tinymce.triggerSave(); let markupStr = tinymce.activeEditor.getContent(); $('#editor' + id).val(markupStr); modOnFormChange(id, draftid, groupurl, csrf); }); } // special wiki buttons teditor.addButton('addWikiImage', { tooltip: 'Add Image', icon: 'fa fa-image', onclick: function () { $('#ImageModal').modal({}); } }); teditor.addButton('addWikiLink', { tooltip: 'Add Link', icon: 'fa fa-book', onclick: function () { $('#LinkModal').modal({}); } }); teditor.addButton('addWikiTOC', { tooltip: 'Table of Contents', icon: 'fa fa-list-alt', onclick: function () { $('#TOCModal').modal({}); } }); } }); // disable tooltips because they require double taps on mobile $('.note-editor *').tooltip('disable'); } }, initPlainEditor: function (id, bodyType, groupurl, handleAttachments, sig) { $('#addattachments').show(); if (bodyType == 'plain') { $('#bodytype' + id).val('plain'); $('#preview' + id).hide(); $('#return' + id).hide(); $('#preview' + id).hide(); $('#markdownlink' + id).hide(); } else { $('#bodytype' + id).val('markdown'); $('#markdownbuttons' + id).show(); $('#preview' + id).show(); $('#return' + id).hide(); $('#previewWindow' + id).hide(); $('#markdownlink' + id).show(); } toquote = modGetSelection(id); if (toquote != '') { editor.ShowMessageHistory(id, groupurl, 'plain', toquote, sig, true); //$('#editor' + id).val(toquote); } }, InitPostDraft: function (id, draftid, csrf, groupurl) { // save the draft when leaving the page. $(window).on('beforeunload', function () { modUnloading = true; modSaveDraft(id, draftid, groupurl, csrf, true); }); // save the draft 1 second after a change $('form input, form textarea').on('input propertychange change', function () { modOnFormChange(id, draftid, groupurl, csrf); }); modUpdateAttachments(id, draftid, csrf, groupurl); if (typeof Capacitor !== 'undefined') { modInitDeviceUploader(id, draftid, csrf, groupurl); } else { modInitWebUploader(id, draftid, csrf, groupurl); } }, // InitReplyDraft creates a new draft, assumes a hidden form input called #draftidmid, and then calls initWindow(). InitReplyDraft: function ( id, bodytype, draftid, groupurl, csrf, handleAttachments, noFontChanges, isReply, isWiki, body, sig, onInitFunc ) { console.log('in InitReplyDraft draftid=' + draftid); modDeletedDraft = false; modDestroyedEditor = false; modUnloading = false; if (draftid == 0) { // create a new draft console.log('generating new draft' + groupurl); console.log('id=' + id); upload = { mid: id, csrf: csrf, body: sig }; $.ajax({ url: groupurl + '/reply', cache: false, method: 'POST', data: upload, xhrFields: { withCredentials: true }, dataType: 'json', error: function (xhr, ajaxOptions, thrownError) { if (modDeletedDraft == false && modDestroyedEditor == false) { createAlert("There was an error saving the draft. Please reload the page.", true, false) } } }).done(function (response) { console.log('reply draft created'); console.log('draftid:' + response.DraftID); draftid = response.DraftID; $('#draftid' + id).val(response.DraftID); editor.InitEditor( id, bodytype, draftid, groupurl, csrf, handleAttachments, noFontChanges, true, false, body, sig, onInitFunc ); editor.InitPostDraft(id, draftid, csrf, groupurl); console.log('id=' + id); $('#bodytype' + id).val(bodytype); $('#cancel-' + id).attr( 'onclick', 'editor.discardReplyDraft("' + id + '", "' + draftid + '","' + bodytype + '","' + csrf + '","' + groupurl + '");' ); return; }); return; } editor.InitEditor( id, bodytype, draftid, groupurl, csrf, handleAttachments, noFontChanges, true, false, body, sig, onInitFunc ); editor.InitPostDraft(id, draftid, csrf, groupurl); $('#bodytype' + id).val(bodytype); $('#cancel-' + id).attr( 'onclick', 'editor.discardReplyDraft("' + id + '", "' + draftid + '","' + bodytype + '","' + csrf + '","' + groupurl + '");' ); console.log('DONE'); }, // discardReplyDraft deletes the draft and any attachments and returns the user to the previous page. discardReplyDraft: function (id, draftid, bodytype, csrf, groupurl) { console.log('editor delete reply draft'); upload = { draftid: draftid, csrf: csrf, jsondelete: '1' }; $.ajax({ url: groupurl + '/draftop', cache: false, data: upload, method: 'POST', xhrFields: { withCredentials: true }, dataType: 'json' }).done(function (response) { // Do something with the request console.log("success delete reply draft"); $('#draftid' + id).val(''); if (bodytype == 'html') { tinymce.get('editor' + id).remove(); } $('#subject' + id).val($('#origsubject' + id).val()); $('#editor' + id).val(''); modDeletedDraft = true; modDestroyedEditor = true; }); }, PreviewMarkdown: function (id, groupurl) { let markdown = $('#editor' + id).val(); upload = { md: markdown }; $.ajax({ url: groupurl + '/previewmd', cache: false, data: upload, method: 'POST', xhrFields: { withCredentials: true }, dataType: 'json' }).done(function (response) { // Do something with the request console.log(response.markdown); wrap = '

' + response.markdown + '

'; $('#editwindow' + id).hide(); $('#previewWindow' + id).replaceWith(wrap); $('#previewWindow' + id).show(); }); $('#preview' + id).hide(); $('#return' + id).show(); }, ReturnMarkdown: function (id) { $('#preview' + id).show(); $('#return' + id).hide(); $('#previewWindow' + id).hide(); $('#editwindow' + id).show(); }, ClearTimeout: function() { clearTimeout(modTimeoutId); }, ShowMessageHistory: function( id, groupurl, bodytype, selectedText, sig, firstTime ) { console.log('URL ' + groupurl); console.log('ID ' + id); if (bodytype == 'html' && firstTime == false) { existingmsg = tinyMCE.get('editor' + id).getContent(); } else { existingmsg = $('#editor' + id).val(); } if (selectedText == '') { upload = { preview: bodytype, id: id }; } else { upload = { preview: bodytype, id: id, text: selectedText }; if (firstTime == true) { existingmsg = sig; } } $.ajax({ url: groupurl + '/previewmd', cache: false, data: upload, method: 'POST', xhrFields: { withCredentials: true }, dataType: 'json' }).done(function (response) { $('#editor' + id).val(response.reply + existingmsg); if (bodytype == 'html') { console.log('SETTING ' + response.reply + existingmsg); tinyMCE.get('editor' + id).setContent(response.reply + existingmsg); tinyMCE.get('editor' + id).selection.select(tinyMCE.get('editor' + id).getBody(), true); tinyMCE.get('editor' + id).selection.collapse(false); console.log('DONE'); } }); $('#editor' + id).focus(); } /* $('form').submit(function(e) { clearTimeout(modTimeoutId); if (postVar != null) { postVar.abort(); } console.log("SETTING DELETED TO TRUE"); console.log("EVENT:", e); modDeletedDraft = true; if ($(this).hasClass('form-submitted')) { e.preventDefault(); return; } $(this).addClass('form-submitted'); }); */ };}());async function uploadAttachments(doctype, id, draftid, groupurl, csrf) { const result = await Capacitor.Plugins.FilePicker.pickFiles(); const file = result.files[0]; console.log("in uploadAttachments"); console.log("files:", result.files); console.log("mimeType:", file.mimeType); console.log("name:", file.name); console.log("doctype:", doctype);const b64toBlob = (base64, type = 'image/jpeg') => fetch(`data:${type};base64,${base64}`).then(res => res.blob());const result2 = await Capacitor.Plugins.Filesystem.readFile({path:file.path})console.log("result2: ", result2);const blob = await b64toBlob(result2.data, file.mimeType);console.log("in uploadAttachments 2, " + file.mimeType); uploadImage(doctype, id, draftid, groupurl, csrf, blob, file.name);}async function takePicture2(doctype, id, draftid, groupurl, csrf) { console.log("in takePicture2"); try { const image = await Capacitor.Plugins.Camera.getPhoto({ quality: 90, allowEditing: false, resultType: "uri" }); console.log("got image");const b64toBlob = (base64, type = 'image/jpeg') => fetch(`data:${type};base64,${base64}`).then(res => res.blob());const result = await Capacitor.Plugins.Filesystem.readFile({path:image.path})const blob = await b64toBlob(result.data);console.log("path: ", image.path); uploadImage(doctype, id, draftid, groupurl, csrf, blob, image.path.split("/").pop()); } catch (err) { console.log("catch err 1: ", err); }}async function uploadImage(doctype, id, draftid, groupurl, csrf, raw, name) { console.log("here0"); const controller = new AbortController(); const formData = new FormData(); console.log("here1"); formData.append("csrf", csrf); formData.append("draftid", draftid); formData.append("upload", "1"); if (doctype === "pictures") { formData.append("inline", "1"); } console.log("FORMDATA:", formData); formData.append("fileupload", raw, name); console.log("here3"); const myRequest = new Request(groupurl + '/draftop', { method: 'POST', credentials: 'include', body: formData, signal: controller.signal, mode: 'cors' }); console.log("uploading"); try { const response = await fetch(myRequest); const result = await response.json(); if (doctype === "pictures") { console.log("picture processing"); for (let i = result.length - 1; i >= 0; i--) { console.log("Processing: ", i); const fileurl = result[i]; console.log('FILE: ' + result[i]); console.log('URL: ' + fileurl); const imghtml = 'twsapi@groups.io | Heartbeats and Healthchecks (1)'; console.log('imghtml: ' + imghtml); tinymce.activeEditor.insertContent(imghtml); } } else { console.log("attachment processing"); updateAttachments(id, draftid, csrf, groupurl); console.log("attachment processing done"); } } catch (error) { console.error('Error:', error); }}

  1. Twsapi
  2. Topics

Search

DateDate1 - 4 of 4

  • previous page
  • next page

Heartbeats and Healthchecks

Kit Haywood

  • All Messages By This Member

#52693


Hi, I'm building an app which relies on the continuity of reqRealTimeBars. My architecture is Ubuntu hosted docker environment, using an ib-gateway docker images, and the python IBAPI client, currently local wifi but eventually cloud hosted. My issue is I get disconnected at midnight and my 30 minutely scheduled reconnects don't appear to reconnect me. I've toyed with both reqCurrentTime and reqAccountSummary for heartbeat candidates but they both have their flaws. My question is, is there any good advice, material or examples anyone can point me towards to help with this 'eternal continuity' issue I've been having. Has anyone managed to set up a functioning heartbeat and reconnect process? Below is a small excerpt of a moment where the reconnect didn;t work. Apologies for the log format - the thing I notice is I stopped fetting 'ANSWER' messages from the client. Maybe this is relevant. Thanks in advance for you're help

ib-market-interface_1 | self.isConnected(): True ECLIENT.CONNECTED: 2 £EClient.CONNECTING: 1
ib-market-interface_1 | 03-04-24 21:58:30.082 :| check_connection | Connected
ib-market-interface_1 | 03-04-24 21:58:30.087 :| logRequest | REQUEST reqCurrentTime {}
ib-market-interface_1 | 03-04-24 21:58:30.092 :| sendMsg | SENDING reqCurrentTime b'\x00\x00\x00\x0549\x001\x00'
ib-market-interface_1 | 03-04-24 21:58:30.118 :| logAnswer | ANSWER currentTime {'time': 1712181510}
ib-market-interface_1 | 03-04-24 21:58:31.077 :| main | {'status': 'OK', 'destination': 'quote', 'payload': {'instrument': 'GBPUSD', 'product': 'CFD', 'bid': 1.26508, 'timestamp': 1712181505000}}
ib-market-interface_1 | 03-04-24 21:58:36.086 :| main | {'status': 'OK', 'destination': 'quote', 'payload': {'instrument': 'GBPUSD', 'product': 'CFD', 'bid': 1.26508, 'timestamp': 1712181510000}}
ib-market-interface_1 | 03-04-24 21:58:41.070 :| main | {'status': 'OK', 'destination': 'quote', 'payload': {'instrument': 'GBPUSD', 'product': 'CFD', 'bid': 1.26506, 'timestamp': 1712181515000}}
ib-market-interface_1 | 03-04-24 21:58:46.090 :| main | {'status': 'OK', 'destination': 'quote', 'payload': {'instrument': 'GBPUSD', 'product': 'CFD', 'bid': 1.26506, 'timestamp': 1712181520000}}
ib-market-interface_1 | 03-04-24 21:58:50.412 :| logAnswer | ANSWER accountSummary {'reqId': 9001, 'account': 'DU8391722', 'tag': 'AvailableFunds', 'value': '1008964.62', 'currency': 'GBP'}
ib-market-interface_1 | 03-04-24 21:58:50.728 :| main | {'status': 'OK', 'destination': 'quote', 'payload': {'instrument': 'GBPUSD', 'product': 'CFD', 'bid': 1.265065, 'timestamp': 1712181525000}}
ib-market-interface_1 | 03-04-24 21:58:56.098 :| main | {'status': 'OK', 'destination': 'quote', 'payload': {'instrument': 'GBPUSD', 'product': 'CFD', 'bid': 1.265055, 'timestamp': 1712181530000}}
ib-market-interface_1 | 03-04-24 21:59:00.034 :| blanker_writer | [GBPUSD] - wrote prices to DB
ib-market-interface_1 | 03-04-24 21:59:00.071 :| blanker_writer | [GBPUSD] - wrote prices to DB
ib-market-interface_1 | 03-04-24 21:59:00.997 :| disconnect | disconnecting
ib-market-interface_1 | 03-04-24 21:59:00.997 :| logAnswer | ANSWER connectionClosed {}
ib-market-interface_1 | 03-04-24 21:59:30.336 :| connect | sent startApi
ib-market-interface_1 | 03-04-24 21:59:30.341 :| logRequest | REQUEST startApi {}
ib-market-interface_1 | 03-04-24 21:59:30.342 :| sendMsg | SENDING startApi b'\x00\x00\x00\x0871\x002\x000\x00\x00'
ib-market-interface_1 | 03-04-24 21:59:30.347 :| logAnswer | ANSWER connectAck {}
ib-market-interface_1 | 03-04-24 21:59:35.396 :| req_real_time_data | GBPUSD
ib-market-interface_1 | 03-04-24 21:59:35.401 :| logRequest | REQUEST reqRealTimeBars {'reqId': 5, 'contract': 138184640828880: 0,GBP,CASH,,0,,,IDEALPRO,,USD,,,False,,,,combo:, 'barSize': 5, 'whatToShow': 'MIDPOINT', 'useRTH': True, 'realTimeBarsOptions': []}
ib-market-interface_1 | 03-04-24 21:59:35.401 :| sendMsg | SENDING reqRealTimeBars b'\x00\x00\x00750\x003\x005\x000\x00GBP\x00CASH\x00\x000.0\x00\x00\x00IDEALPRO\x00\x00USD\x00\x00\x005\x00MIDPOINT\x001\x00\x00'
ib-market-interface_1 | 03-04-24 21:59:35.407 :| logRequest | REQUEST reqCurrentTime {}
ib-market-interface_1 | 03-04-24 21:59:35.407 :| sendMsg | SENDING reqCurrentTime b'\x00\x00\x00\x0549\x001\x00'
ib-market-interface_1 | self.isConnected(): True ECLIENT.CONNECTED: 2 £EClient.CONNECTING: 1
ib-market-interface_1 | 03-04-24 22:00:30.061 :| check_connection | Connected
ib-market-interface_1 | 03-04-24 22:00:30.061 :| logRequest | REQUEST reqCurrentTime {}
ib-market-interface_1 | 03-04-24 22:00:30.066 :| sendMsg | SENDING reqCurrentTime b'\x00\x00\x00\x0549\x001\x00'
ib-market-interface_1 | self.isConnected(): True ECLIENT.CONNECTED: 2 £EClient.CONNECTING: 1
ib-market-interface_1 | 03-04-24 22:01:30.046 :| check_connection | Connected
ib-market-interface_1 | 03-04-24 22:01:30.046 :| logRequest | REQUEST reqCurrentTime {}
ib-market-interface_1 | 03-04-24 22:01:30.046 :| sendMsg | SENDING reqCurrentTime b'\x00\x00\x00\x0549\x001\x00'
ib-market-interface_1 | 03-04-24 22:02:30.015 :| check_connection | Connected
ib-market-interface_1 | self.isConnected(): True ECLIENT.CONNECTED: 2 £EClient.CONNECTING: 1
ib-market-interface_1 | 03-04-24 22:02:30.015 :| logRequest | REQUEST reqCurrentTime {}
ib-market-interface_1 | 03-04-24 22:02:30.015 :| sendMsg | SENDING reqCurrentTime b'\x00\x00\x00\x0549\x001\x00'
ib-market-interface_1 | self.isConnected(): True ECLIENT.CONNECTED: 2 £EClient.CONNECTING: 1
ib-market-interface_1 | 03-04-24 22:03:30.068 :| check_connection | Connected

Jürgen Reinold

  • All Messages By This Member

#52694


We just had a post called "Connection / Disconnect / Reconnect Monitoring" you might want to look at.

If you then still want to go the transparent reconnect route, make sure that your client properly disconnects and cleans out the old API connection (such as socket and reader thread) and that it does not reuse any objects from the old connections once the new connections is established (a new socket, with a new reader thread, and a new message queue). Otherwise, you can run into the kind of situation you describe where messages from TWS/IBGW arrive but are not processed.

I am not sure I understand what flaws reqCurrentTime has when used as a connection health/heartbeat detector.

What causes the disconnect at mid-night? Wouldn't a simple client restart on disconnect be easier and possibly more reliable?

Jürgen

toggle quoted messageShow quoted text

On Thu, Apr 4, 2024 at 05:27 PM, <kithaywood53@...> wrote:

Hi, I'm building an app which relies on the continuity of reqRealTimeBars. My architecture is Ubuntu hosted docker environment, using an ib-gateway docker images, and the python IBAPI client, currently local wifi but eventually cloud hosted. My issue is I get disconnected at midnight and my 30 minutely scheduled reconnects don't appear to reconnect me. I've toyed with both reqCurrentTime and reqAccountSummary for heartbeat candidates but they both have their flaws. My question is, is there any good advice, material or examples anyone can point me towards to help with this 'eternal continuity' issue I've been having. Has anyone managed to set up a functioning heartbeat and reconnect process?

Daniel Ferreira

  • All Messages By This Member

#52696


When not in trading hours: what I recommend is a daily reboot of your client app, after TWS auto-restarts.

When in trading hours: you'll have to implement a 'stale detection' method by resettinga timer every time you get `reqRealTimeBars` quote. E.g. if not received in 1', trigger reconnection. Additionally, You'll have to monitor events10225 (burst events),1102 (TWS reconnection), 10182 (live updates down) and504 (not connected). If any of these trigger, you should issue a unsubscribe to quotes and then subscribe again.

Hope it helps,

Daniel.


On Fri, Apr 5, 2024 at 1:53 AM Jürgen Reinold via groups.io <TwsApiOnGroupsIo=Reinold.org@groups.io> wrote:

toggle quoted messageShow quoted text

We just had a post called "Connection / Disconnect / Reconnect Monitoring" you might want to look at.

If you then still want to go the transparent reconnect route, make sure that your client properly disconnects and cleans out the old API connection (such as socket and reader thread) and that it does not reuse any objects from the old connections once the new connections is established (a new socket, with a new reader thread, and a new message queue). Otherwise, you can run into the kind of situation you describe where messages from TWS/IBGW arrive but are not processed.

I am not sure I understand what flaws reqCurrentTime has when used as a connection health/heartbeat detector.

What causes the disconnect at mid-night? Wouldn't a simple client restart on disconnect be easier and possibly more reliable?

Jürgen

On Thu, Apr 4, 2024 at 05:27 PM, <kithaywood53@...> wrote:

Hi, I'm building an app which relies on the continuity of reqRealTimeBars. My architecture is Ubuntu hosted docker environment, using an ib-gateway docker images, and the python IBAPI client, currently local wifi but eventually cloud hosted. My issue is I get disconnected at midnight and my 30 minutely scheduled reconnects don't appear to reconnect me. I've toyed with both reqCurrentTime and reqAccountSummary for heartbeat candidates but they both have their flaws. My question is, is there any good advice, material or examples anyone can point me towards to help with this 'eternal continuity' issue I've been having. Has anyone managed to set up a functioning heartbeat and reconnect process?

--

Daniel

Kit Haywood

  • All Messages By This Member

#52697


Thank you for the advice, I have attempted both of these but missed some important components you mention.

Jurgen - your points about reusing the old connection components are well received, I suspect that is what is happening. My issue with reqCurrentTime was that I wasn't receiving the response and yet the isConnected objects were registering as True. Plus I was still chewing over how best to track this whilst avoiding blocking conditions. As the app is implemented in a thread (I think this is reasonable?) I also had issues with being in a thread, setting thread events and having threads kill themselves without bastardising the 'run' command in the client. Best to avoid that I think?. I was avoiding this as remaking the queue in the client 'emitter' required remaking the 'listener' obj at the other end. But surmontableissues I think. Thank you.

Daniel - I went down this rabbit hole, timing the wait between reqCurrentTime request and response and using this in a connection sentinel, couldn't get it to play nice - but I only gave it 90 mins last night. I suppose my architecture needs to capture the connectivity of the ibclient and marry any recreates with consumerproc at the other end. Appreciate the stale detection info, will create a handler for such things, and the restart approach seems reasonable there's nothing like a 'factory reset' to clear the baffles so to speak.

Appreciate the prompt and informative responses here, this has been a proper headscratcher.

Best,

Kit

toggle quoted messageShow quoted text


On Fri, Apr 5, 2024 at 8:16 AM Daniel Ferreira <daniel@...> wrote:

When not in trading hours: what I recommend is a daily reboot of your client app, after TWS auto-restarts.

When in trading hours: you'll have to implement a 'stale detection' method by resettinga timer every time you get `reqRealTimeBars` quote. E.g. if not received in 1', trigger reconnection. Additionally, You'll have to monitor events10225 (burst events),1102 (TWS reconnection), 10182 (live updates down) and504 (not connected). If any of these trigger, you should issue a unsubscribe to quotes and then subscribe again.

Hope it helps,

Daniel.


On Fri, Apr 5, 2024 at 1:53 AM Jürgen Reinold via groups.io <TwsApiOnGroupsIo=Reinold.org@groups.io> wrote:

We just had a post called "Connection / Disconnect / Reconnect Monitoring" you might want to look at.

If you then still want to go the transparent reconnect route, make sure that your client properly disconnects and cleans out the old API connection (such as socket and reader thread) and that it does not reuse any objects from the old connections once the new connections is established (a new socket, with a new reader thread, and a new message queue). Otherwise, you can run into the kind of situation you describe where messages from TWS/IBGW arrive but are not processed.

I am not sure I understand what flaws reqCurrentTime has when used as a connection health/heartbeat detector.

What causes the disconnect at mid-night? Wouldn't a simple client restart on disconnect be easier and possibly more reliable?

Jürgen

On Thu, Apr 4, 2024 at 05:27 PM, <kithaywood53@...> wrote:

Hi, I'm building an app which relies on the continuity of reqRealTimeBars. My architecture is Ubuntu hosted docker environment, using an ib-gateway docker images, and the python IBAPI client, currently local wifi but eventually cloud hosted. My issue is I get disconnected at midnight and my 30 minutely scheduled reconnects don't appear to reconnect me. I've toyed with both reqCurrentTime and reqAccountSummary for heartbeat candidates but they both have their flaws. My question is, is there any good advice, material or examples anyone can point me towards to help with this 'eternal continuity' issue I've been having. Has anyone managed to set up a functioning heartbeat and reconnect process?

--

Daniel

1 - 4 of 4
  • previous page
  • 1
  • next page

Previous TopicNext Topic

twsapi@groups.io | Heartbeats and Healthchecks (2024)
Top Articles
Latest Posts
Article information

Author: Dan Stracke

Last Updated:

Views: 5964

Rating: 4.2 / 5 (43 voted)

Reviews: 90% of readers found this page helpful

Author information

Name: Dan Stracke

Birthday: 1992-08-25

Address: 2253 Brown Springs, East Alla, OH 38634-0309

Phone: +398735162064

Job: Investor Government Associate

Hobby: Shopping, LARPing, Scrapbooking, Surfing, Slacklining, Dance, Glassblowing

Introduction: My name is Dan Stracke, I am a homely, gleaming, glamorous, inquisitive, homely, gorgeous, light person who loves writing and wants to share my knowledge and understanding with you.