Add Electroporation capability and BTX Gemini X2 backend - #1063

Open
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1
Open

Add Electroporation capability and BTX Gemini X2 backend#1063
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1

Conversation

@hazlamshamin

Copy link
Copy Markdown
Contributor

Summary

This PR adds a new Electroporation capability and a BTX Gemini X2 implementation for prepared electroporation runs.

The workflow is modeled around the actual Gemini X2 / HT-200 operation:

  1. Create a temporary user protocol on the Gemini.
  2. Arm that protocol on the Run Protocol screen through the touchscreen workflow.
  3. Optionally set HT-200 plate columns.
  4. Start the prepared run or cancel it.
  5. Match the new BTXDATA run log and clean up the temporary protocol.

What changed

  • Added pylabrobot.capabilities.electroporation

    • Electroporation frontend
    • ElectroporationBackend
    • serializable protocol/run/result dataclasses
    • chatterbox backend for device-free tests/docs
  • Added pylabrobot.btx

    • BTXGeminiX2 device
    • Gemini X2 driver and electroporation backend
    • Protocol Manager/file-transfer control for user protocols and BTXDATA logs
    • GhostTouch RSI touchscreen control for selecting and starting user protocols
    • HT-200 helper for plate-handler assumptions
  • Added BTX/Gemini X2 test fixtures

    • selected physical-device screen captures from a real GO run
    • portable metadata only; no local paths or host serial-port details
    • tests for screen detection, protocol selection, two-step Set Plate Columns, GO completion, and log matching
  • Added docs

    • electroporation capability notebook
    • BTX Gemini X2 user guide notebook
    • BTX install notes and pylabrobot[btx] extra
    • API docs and supported-devices listing
  • CI

    • adds btx to the extras test matrix
    • installs external tesseract-ocr for btx/all CI jobs because GhostTouch OCR depends on the tesseract executable
  • Also includes one docs-only fix for existing bulk-dispenser API module paths (peristaltic8/backend8, syringe8/backend8).

Hardware validation

Validated on a physical BTX Gemini X2 with HT-200 using a temporary square-wave protocol:

  • firmware: BTX Gemini 4.0.4
  • plate columns: 3
  • GO pressed: yes
  • run log matched under \BTXDATA
  • result: complete / no error
  • temporary protocol deleted after run

The committed screen fixtures are selected PNGs from this run and are used for digital tests so the screen-state logic can be tested without the physical device. See btx/test_data/gemini_x2/screens, as well as the attached video for "The Ghost Touch". I believe this is the first crazy backend that relies on screen detection and OCR to control lab devices. Mainly due to inability of the machine to expose firmware API that allows seamless control by a desktop. The remote desktop app by OEM literally streams the screen bytes to the desktop, and users need to click the streamed screen on their desktop to be able to control the device. Hence this is my best effort.

Tests

Run locally:

python -m pytest pylabrobot/btx pylabrobot/capabilities/electroporation -q
python -m ruff check pylabrobot/btx pylabrobot/capabilities/electroporation docs/user_guide/capabilities/electroporation.ipynb docs/user_guide/btx/gemini_x2/hello-world.ipynb
python -m ruff format --check pylabrobot/btx pylabrobot/capabilities/electroporation
python -m mypy pylabrobot/btx pylabrobot/capabilities/electroporation --check-untyped-defs
git diff --check
https://github.com/user-attachments/assets/48a1b306-9d2c-4e9f-b61b-0d91286c81ef

@hazlamshamin

Copy link
Copy Markdown
ContributorAuthor

@rickwierenga
rickwierengaforce-pushed the v1b1 branch 2 times, most recently from 6af085c to 1ae9dc6CompareAugust 1, 2026 20:32
@rickwierenga
rickwierenga changed the base branch from v1b1 to mainAugust 23, 2026 23:25
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from 1c282c8 to d8cae69CompareAugust 23, 2026 23:39
rickwierengaand others added 5 commits August 23, 2026 17:29
Active pulse delivery requires bounded framebuffer traffic. Use one
framebuffer attempt for each run-state poll while preserving configured
retries for navigation and other screen captures.
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch 2 times, most recently from 4cb4a54 to d927525CompareAugust 29, 2026 01:14
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from d927525 to 904c7b1CompareAugust 29, 2026 01:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@hazlamshamin@rickwierenga
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Add Electroporation capability and BTX Gemini X2 backend - #1063

Open
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1
Open

Add Electroporation capability and BTX Gemini X2 backend#1063
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1

Conversation

@hazlamshamin

Copy link
Copy Markdown
Contributor

Summary

This PR adds a new Electroporation capability and a BTX Gemini X2 implementation for prepared electroporation runs.

The workflow is modeled around the actual Gemini X2 / HT-200 operation:

  1. Create a temporary user protocol on the Gemini.
  2. Arm that protocol on the Run Protocol screen through the touchscreen workflow.
  3. Optionally set HT-200 plate columns.
  4. Start the prepared run or cancel it.
  5. Match the new BTXDATA run log and clean up the temporary protocol.

What changed

  • Added pylabrobot.capabilities.electroporation

    • Electroporation frontend
    • ElectroporationBackend
    • serializable protocol/run/result dataclasses
    • chatterbox backend for device-free tests/docs
  • Added pylabrobot.btx

    • BTXGeminiX2 device
    • Gemini X2 driver and electroporation backend
    • Protocol Manager/file-transfer control for user protocols and BTXDATA logs
    • GhostTouch RSI touchscreen control for selecting and starting user protocols
    • HT-200 helper for plate-handler assumptions
  • Added BTX/Gemini X2 test fixtures

    • selected physical-device screen captures from a real GO run
    • portable metadata only; no local paths or host serial-port details
    • tests for screen detection, protocol selection, two-step Set Plate Columns, GO completion, and log matching
  • Added docs

    • electroporation capability notebook
    • BTX Gemini X2 user guide notebook
    • BTX install notes and pylabrobot[btx] extra
    • API docs and supported-devices listing
  • CI

    • adds btx to the extras test matrix
    • installs external tesseract-ocr for btx/all CI jobs because GhostTouch OCR depends on the tesseract executable
  • Also includes one docs-only fix for existing bulk-dispenser API module paths (peristaltic8/backend8, syringe8/backend8).

Hardware validation

Validated on a physical BTX Gemini X2 with HT-200 using a temporary square-wave protocol:

  • firmware: BTX Gemini 4.0.4
  • plate columns: 3
  • GO pressed: yes
  • run log matched under \BTXDATA
  • result: complete / no error
  • temporary protocol deleted after run

The committed screen fixtures are selected PNGs from this run and are used for digital tests so the screen-state logic can be tested without the physical device. See btx/test_data/gemini_x2/screens, as well as the attached video for "The Ghost Touch". I believe this is the first crazy backend that relies on screen detection and OCR to control lab devices. Mainly due to inability of the machine to expose firmware API that allows seamless control by a desktop. The remote desktop app by OEM literally streams the screen bytes to the desktop, and users need to click the streamed screen on their desktop to be able to control the device. Hence this is my best effort.

Tests

Run locally:

python -m pytest pylabrobot/btx pylabrobot/capabilities/electroporation -q
python -m ruff check pylabrobot/btx pylabrobot/capabilities/electroporation docs/user_guide/capabilities/electroporation.ipynb docs/user_guide/btx/gemini_x2/hello-world.ipynb
python -m ruff format --check pylabrobot/btx pylabrobot/capabilities/electroporation
python -m mypy pylabrobot/btx pylabrobot/capabilities/electroporation --check-untyped-defs
git diff --check
https://github.com/user-attachments/assets/48a1b306-9d2c-4e9f-b61b-0d91286c81ef

@hazlamshamin

Copy link
Copy Markdown
ContributorAuthor

@rickwierenga
rickwierengaforce-pushed the v1b1 branch 2 times, most recently from 6af085c to 1ae9dc6CompareAugust 1, 2026 20:32
@rickwierenga
rickwierenga changed the base branch from v1b1 to mainAugust 23, 2026 23:25
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from 1c282c8 to d8cae69CompareAugust 23, 2026 23:39
rickwierengaand others added 5 commits August 23, 2026 17:29
Active pulse delivery requires bounded framebuffer traffic. Use one
framebuffer attempt for each run-state poll while preserving configured
retries for navigation and other screen captures.
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch 2 times, most recently from 4cb4a54 to d927525CompareAugust 29, 2026 01:14
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from d927525 to 904c7b1CompareAugust 29, 2026 01:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@hazlamshamin@rickwierenga
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Electroporation capability and BTX Gemini X2 backend - #1063

Open
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1
Open

Add Electroporation capability and BTX Gemini X2 backend#1063
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1

Conversation

@hazlamshamin

Copy link
Copy Markdown
Contributor

Summary

This PR adds a new Electroporation capability and a BTX Gemini X2 implementation for prepared electroporation runs.

The workflow is modeled around the actual Gemini X2 / HT-200 operation:

  1. Create a temporary user protocol on the Gemini.
  2. Arm that protocol on the Run Protocol screen through the touchscreen workflow.
  3. Optionally set HT-200 plate columns.
  4. Start the prepared run or cancel it.
  5. Match the new BTXDATA run log and clean up the temporary protocol.

What changed

  • Added pylabrobot.capabilities.electroporation

    • Electroporation frontend
    • ElectroporationBackend
    • serializable protocol/run/result dataclasses
    • chatterbox backend for device-free tests/docs
  • Added pylabrobot.btx

    • BTXGeminiX2 device
    • Gemini X2 driver and electroporation backend
    • Protocol Manager/file-transfer control for user protocols and BTXDATA logs
    • GhostTouch RSI touchscreen control for selecting and starting user protocols
    • HT-200 helper for plate-handler assumptions
  • Added BTX/Gemini X2 test fixtures

    • selected physical-device screen captures from a real GO run
    • portable metadata only; no local paths or host serial-port details
    • tests for screen detection, protocol selection, two-step Set Plate Columns, GO completion, and log matching
  • Added docs

    • electroporation capability notebook
    • BTX Gemini X2 user guide notebook
    • BTX install notes and pylabrobot[btx] extra
    • API docs and supported-devices listing
  • CI

    • adds btx to the extras test matrix
    • installs external tesseract-ocr for btx/all CI jobs because GhostTouch OCR depends on the tesseract executable
  • Also includes one docs-only fix for existing bulk-dispenser API module paths (peristaltic8/backend8, syringe8/backend8).

Hardware validation

Validated on a physical BTX Gemini X2 with HT-200 using a temporary square-wave protocol:

  • firmware: BTX Gemini 4.0.4
  • plate columns: 3
  • GO pressed: yes
  • run log matched under \BTXDATA
  • result: complete / no error
  • temporary protocol deleted after run

The committed screen fixtures are selected PNGs from this run and are used for digital tests so the screen-state logic can be tested without the physical device. See btx/test_data/gemini_x2/screens, as well as the attached video for "The Ghost Touch". I believe this is the first crazy backend that relies on screen detection and OCR to control lab devices. Mainly due to inability of the machine to expose firmware API that allows seamless control by a desktop. The remote desktop app by OEM literally streams the screen bytes to the desktop, and users need to click the streamed screen on their desktop to be able to control the device. Hence this is my best effort.

Tests

Run locally:

python -m pytest pylabrobot/btx pylabrobot/capabilities/electroporation -q
python -m ruff check pylabrobot/btx pylabrobot/capabilities/electroporation docs/user_guide/capabilities/electroporation.ipynb docs/user_guide/btx/gemini_x2/hello-world.ipynb
python -m ruff format --check pylabrobot/btx pylabrobot/capabilities/electroporation
python -m mypy pylabrobot/btx pylabrobot/capabilities/electroporation --check-untyped-defs
git diff --check
https://github.com/user-attachments/assets/48a1b306-9d2c-4e9f-b61b-0d91286c81ef

@hazlamshamin

Copy link
Copy Markdown
ContributorAuthor

@rickwierenga
rickwierengaforce-pushed the v1b1 branch 2 times, most recently from 6af085c to 1ae9dc6CompareAugust 1, 2026 20:32
@rickwierenga
rickwierenga changed the base branch from v1b1 to mainAugust 23, 2026 23:25
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from 1c282c8 to d8cae69CompareAugust 23, 2026 23:39
rickwierengaand others added 5 commits August 23, 2026 17:29
Active pulse delivery requires bounded framebuffer traffic. Use one
framebuffer attempt for each run-state poll while preserving configured
retries for navigation and other screen captures.
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch 2 times, most recently from 4cb4a54 to d927525CompareAugust 29, 2026 01:14
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from d927525 to 904c7b1CompareAugust 29, 2026 01:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@hazlamshamin@rickwierenga
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Electroporation capability and BTX Gemini X2 backend - #1063

Open
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1
Open

Add Electroporation capability and BTX Gemini X2 backend#1063
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1

Conversation

@hazlamshamin

Copy link
Copy Markdown
Contributor

Summary

This PR adds a new Electroporation capability and a BTX Gemini X2 implementation for prepared electroporation runs.

The workflow is modeled around the actual Gemini X2 / HT-200 operation:

  1. Create a temporary user protocol on the Gemini.
  2. Arm that protocol on the Run Protocol screen through the touchscreen workflow.
  3. Optionally set HT-200 plate columns.
  4. Start the prepared run or cancel it.
  5. Match the new BTXDATA run log and clean up the temporary protocol.

What changed

  • Added pylabrobot.capabilities.electroporation

    • Electroporation frontend
    • ElectroporationBackend
    • serializable protocol/run/result dataclasses
    • chatterbox backend for device-free tests/docs
  • Added pylabrobot.btx

    • BTXGeminiX2 device
    • Gemini X2 driver and electroporation backend
    • Protocol Manager/file-transfer control for user protocols and BTXDATA logs
    • GhostTouch RSI touchscreen control for selecting and starting user protocols
    • HT-200 helper for plate-handler assumptions
  • Added BTX/Gemini X2 test fixtures

    • selected physical-device screen captures from a real GO run
    • portable metadata only; no local paths or host serial-port details
    • tests for screen detection, protocol selection, two-step Set Plate Columns, GO completion, and log matching
  • Added docs

    • electroporation capability notebook
    • BTX Gemini X2 user guide notebook
    • BTX install notes and pylabrobot[btx] extra
    • API docs and supported-devices listing
  • CI

    • adds btx to the extras test matrix
    • installs external tesseract-ocr for btx/all CI jobs because GhostTouch OCR depends on the tesseract executable
  • Also includes one docs-only fix for existing bulk-dispenser API module paths (peristaltic8/backend8, syringe8/backend8).

Hardware validation

Validated on a physical BTX Gemini X2 with HT-200 using a temporary square-wave protocol:

  • firmware: BTX Gemini 4.0.4
  • plate columns: 3
  • GO pressed: yes
  • run log matched under \BTXDATA
  • result: complete / no error
  • temporary protocol deleted after run

The committed screen fixtures are selected PNGs from this run and are used for digital tests so the screen-state logic can be tested without the physical device. See btx/test_data/gemini_x2/screens, as well as the attached video for "The Ghost Touch". I believe this is the first crazy backend that relies on screen detection and OCR to control lab devices. Mainly due to inability of the machine to expose firmware API that allows seamless control by a desktop. The remote desktop app by OEM literally streams the screen bytes to the desktop, and users need to click the streamed screen on their desktop to be able to control the device. Hence this is my best effort.

Tests

Run locally:

python -m pytest pylabrobot/btx pylabrobot/capabilities/electroporation -q
python -m ruff check pylabrobot/btx pylabrobot/capabilities/electroporation docs/user_guide/capabilities/electroporation.ipynb docs/user_guide/btx/gemini_x2/hello-world.ipynb
python -m ruff format --check pylabrobot/btx pylabrobot/capabilities/electroporation
python -m mypy pylabrobot/btx pylabrobot/capabilities/electroporation --check-untyped-defs
git diff --check
https://github.com/user-attachments/assets/48a1b306-9d2c-4e9f-b61b-0d91286c81ef

@hazlamshamin

Copy link
Copy Markdown
ContributorAuthor

@rickwierenga
rickwierengaforce-pushed the v1b1 branch 2 times, most recently from 6af085c to 1ae9dc6CompareAugust 1, 2026 20:32
@rickwierenga
rickwierenga changed the base branch from v1b1 to mainAugust 23, 2026 23:25
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from 1c282c8 to d8cae69CompareAugust 23, 2026 23:39
rickwierengaand others added 5 commits August 23, 2026 17:29
Active pulse delivery requires bounded framebuffer traffic. Use one
framebuffer attempt for each run-state poll while preserving configured
retries for navigation and other screen captures.
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch 2 times, most recently from 4cb4a54 to d927525CompareAugust 29, 2026 01:14
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from d927525 to 904c7b1CompareAugust 29, 2026 01:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@hazlamshamin@rickwierenga
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Add Electroporation capability and BTX Gemini X2 backend - #1063

Open
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1
Open

Add Electroporation capability and BTX Gemini X2 backend#1063
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1

Conversation

@hazlamshamin

Copy link
Copy Markdown
Contributor

Summary

This PR adds a new Electroporation capability and a BTX Gemini X2 implementation for prepared electroporation runs.

The workflow is modeled around the actual Gemini X2 / HT-200 operation:

  1. Create a temporary user protocol on the Gemini.
  2. Arm that protocol on the Run Protocol screen through the touchscreen workflow.
  3. Optionally set HT-200 plate columns.
  4. Start the prepared run or cancel it.
  5. Match the new BTXDATA run log and clean up the temporary protocol.

What changed

  • Added pylabrobot.capabilities.electroporation

    • Electroporation frontend
    • ElectroporationBackend
    • serializable protocol/run/result dataclasses
    • chatterbox backend for device-free tests/docs
  • Added pylabrobot.btx

    • BTXGeminiX2 device
    • Gemini X2 driver and electroporation backend
    • Protocol Manager/file-transfer control for user protocols and BTXDATA logs
    • GhostTouch RSI touchscreen control for selecting and starting user protocols
    • HT-200 helper for plate-handler assumptions
  • Added BTX/Gemini X2 test fixtures

    • selected physical-device screen captures from a real GO run
    • portable metadata only; no local paths or host serial-port details
    • tests for screen detection, protocol selection, two-step Set Plate Columns, GO completion, and log matching
  • Added docs

    • electroporation capability notebook
    • BTX Gemini X2 user guide notebook
    • BTX install notes and pylabrobot[btx] extra
    • API docs and supported-devices listing
  • CI

    • adds btx to the extras test matrix
    • installs external tesseract-ocr for btx/all CI jobs because GhostTouch OCR depends on the tesseract executable
  • Also includes one docs-only fix for existing bulk-dispenser API module paths (peristaltic8/backend8, syringe8/backend8).

Hardware validation

Validated on a physical BTX Gemini X2 with HT-200 using a temporary square-wave protocol:

  • firmware: BTX Gemini 4.0.4
  • plate columns: 3
  • GO pressed: yes
  • run log matched under \BTXDATA
  • result: complete / no error
  • temporary protocol deleted after run

The committed screen fixtures are selected PNGs from this run and are used for digital tests so the screen-state logic can be tested without the physical device. See btx/test_data/gemini_x2/screens, as well as the attached video for "The Ghost Touch". I believe this is the first crazy backend that relies on screen detection and OCR to control lab devices. Mainly due to inability of the machine to expose firmware API that allows seamless control by a desktop. The remote desktop app by OEM literally streams the screen bytes to the desktop, and users need to click the streamed screen on their desktop to be able to control the device. Hence this is my best effort.

Tests

Run locally:

python -m pytest pylabrobot/btx pylabrobot/capabilities/electroporation -q
python -m ruff check pylabrobot/btx pylabrobot/capabilities/electroporation docs/user_guide/capabilities/electroporation.ipynb docs/user_guide/btx/gemini_x2/hello-world.ipynb
python -m ruff format --check pylabrobot/btx pylabrobot/capabilities/electroporation
python -m mypy pylabrobot/btx pylabrobot/capabilities/electroporation --check-untyped-defs
git diff --check
https://github.com/user-attachments/assets/48a1b306-9d2c-4e9f-b61b-0d91286c81ef

@hazlamshamin

Copy link
Copy Markdown
ContributorAuthor

@rickwierenga
rickwierengaforce-pushed the v1b1 branch 2 times, most recently from 6af085c to 1ae9dc6CompareAugust 1, 2026 20:32
@rickwierenga
rickwierenga changed the base branch from v1b1 to mainAugust 23, 2026 23:25
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from 1c282c8 to d8cae69CompareAugust 23, 2026 23:39
rickwierengaand others added 5 commits August 23, 2026 17:29
Active pulse delivery requires bounded framebuffer traffic. Use one
framebuffer attempt for each run-state poll while preserving configured
retries for navigation and other screen captures.
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch 2 times, most recently from 4cb4a54 to d927525CompareAugust 29, 2026 01:14
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from d927525 to 904c7b1CompareAugust 29, 2026 01:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@hazlamshamin@rickwierenga
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Electroporation capability and BTX Gemini X2 backend - #1063

Open
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1
Open

Add Electroporation capability and BTX Gemini X2 backend#1063
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1

Conversation

@hazlamshamin

Copy link
Copy Markdown
Contributor

Summary

This PR adds a new Electroporation capability and a BTX Gemini X2 implementation for prepared electroporation runs.

The workflow is modeled around the actual Gemini X2 / HT-200 operation:

  1. Create a temporary user protocol on the Gemini.
  2. Arm that protocol on the Run Protocol screen through the touchscreen workflow.
  3. Optionally set HT-200 plate columns.
  4. Start the prepared run or cancel it.
  5. Match the new BTXDATA run log and clean up the temporary protocol.

What changed

  • Added pylabrobot.capabilities.electroporation

    • Electroporation frontend
    • ElectroporationBackend
    • serializable protocol/run/result dataclasses
    • chatterbox backend for device-free tests/docs
  • Added pylabrobot.btx

    • BTXGeminiX2 device
    • Gemini X2 driver and electroporation backend
    • Protocol Manager/file-transfer control for user protocols and BTXDATA logs
    • GhostTouch RSI touchscreen control for selecting and starting user protocols
    • HT-200 helper for plate-handler assumptions
  • Added BTX/Gemini X2 test fixtures

    • selected physical-device screen captures from a real GO run
    • portable metadata only; no local paths or host serial-port details
    • tests for screen detection, protocol selection, two-step Set Plate Columns, GO completion, and log matching
  • Added docs

    • electroporation capability notebook
    • BTX Gemini X2 user guide notebook
    • BTX install notes and pylabrobot[btx] extra
    • API docs and supported-devices listing
  • CI

    • adds btx to the extras test matrix
    • installs external tesseract-ocr for btx/all CI jobs because GhostTouch OCR depends on the tesseract executable
  • Also includes one docs-only fix for existing bulk-dispenser API module paths (peristaltic8/backend8, syringe8/backend8).

Hardware validation

Validated on a physical BTX Gemini X2 with HT-200 using a temporary square-wave protocol:

  • firmware: BTX Gemini 4.0.4
  • plate columns: 3
  • GO pressed: yes
  • run log matched under \BTXDATA
  • result: complete / no error
  • temporary protocol deleted after run

The committed screen fixtures are selected PNGs from this run and are used for digital tests so the screen-state logic can be tested without the physical device. See btx/test_data/gemini_x2/screens, as well as the attached video for "The Ghost Touch". I believe this is the first crazy backend that relies on screen detection and OCR to control lab devices. Mainly due to inability of the machine to expose firmware API that allows seamless control by a desktop. The remote desktop app by OEM literally streams the screen bytes to the desktop, and users need to click the streamed screen on their desktop to be able to control the device. Hence this is my best effort.

Tests

Run locally:

python -m pytest pylabrobot/btx pylabrobot/capabilities/electroporation -q
python -m ruff check pylabrobot/btx pylabrobot/capabilities/electroporation docs/user_guide/capabilities/electroporation.ipynb docs/user_guide/btx/gemini_x2/hello-world.ipynb
python -m ruff format --check pylabrobot/btx pylabrobot/capabilities/electroporation
python -m mypy pylabrobot/btx pylabrobot/capabilities/electroporation --check-untyped-defs
git diff --check
https://github.com/user-attachments/assets/48a1b306-9d2c-4e9f-b61b-0d91286c81ef

@hazlamshamin

Copy link
Copy Markdown
ContributorAuthor

@rickwierenga
rickwierengaforce-pushed the v1b1 branch 2 times, most recently from 6af085c to 1ae9dc6CompareAugust 1, 2026 20:32
@rickwierenga
rickwierenga changed the base branch from v1b1 to mainAugust 23, 2026 23:25
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from 1c282c8 to d8cae69CompareAugust 23, 2026 23:39
rickwierengaand others added 5 commits August 23, 2026 17:29
Active pulse delivery requires bounded framebuffer traffic. Use one
framebuffer attempt for each run-state poll while preserving configured
retries for navigation and other screen captures.
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch 2 times, most recently from 4cb4a54 to d927525CompareAugust 29, 2026 01:14
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from d927525 to 904c7b1CompareAugust 29, 2026 01:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@hazlamshamin@rickwierenga
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Electroporation capability and BTX Gemini X2 backend - #1063

Open
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1
Open

Add Electroporation capability and BTX Gemini X2 backend#1063
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1

Conversation

@hazlamshamin

Copy link
Copy Markdown
Contributor

Summary

This PR adds a new Electroporation capability and a BTX Gemini X2 implementation for prepared electroporation runs.

The workflow is modeled around the actual Gemini X2 / HT-200 operation:

  1. Create a temporary user protocol on the Gemini.
  2. Arm that protocol on the Run Protocol screen through the touchscreen workflow.
  3. Optionally set HT-200 plate columns.
  4. Start the prepared run or cancel it.
  5. Match the new BTXDATA run log and clean up the temporary protocol.

What changed

  • Added pylabrobot.capabilities.electroporation

    • Electroporation frontend
    • ElectroporationBackend
    • serializable protocol/run/result dataclasses
    • chatterbox backend for device-free tests/docs
  • Added pylabrobot.btx

    • BTXGeminiX2 device
    • Gemini X2 driver and electroporation backend
    • Protocol Manager/file-transfer control for user protocols and BTXDATA logs
    • GhostTouch RSI touchscreen control for selecting and starting user protocols
    • HT-200 helper for plate-handler assumptions
  • Added BTX/Gemini X2 test fixtures

    • selected physical-device screen captures from a real GO run
    • portable metadata only; no local paths or host serial-port details
    • tests for screen detection, protocol selection, two-step Set Plate Columns, GO completion, and log matching
  • Added docs

    • electroporation capability notebook
    • BTX Gemini X2 user guide notebook
    • BTX install notes and pylabrobot[btx] extra
    • API docs and supported-devices listing
  • CI

    • adds btx to the extras test matrix
    • installs external tesseract-ocr for btx/all CI jobs because GhostTouch OCR depends on the tesseract executable
  • Also includes one docs-only fix for existing bulk-dispenser API module paths (peristaltic8/backend8, syringe8/backend8).

Hardware validation

Validated on a physical BTX Gemini X2 with HT-200 using a temporary square-wave protocol:

  • firmware: BTX Gemini 4.0.4
  • plate columns: 3
  • GO pressed: yes
  • run log matched under \BTXDATA
  • result: complete / no error
  • temporary protocol deleted after run

The committed screen fixtures are selected PNGs from this run and are used for digital tests so the screen-state logic can be tested without the physical device. See btx/test_data/gemini_x2/screens, as well as the attached video for "The Ghost Touch". I believe this is the first crazy backend that relies on screen detection and OCR to control lab devices. Mainly due to inability of the machine to expose firmware API that allows seamless control by a desktop. The remote desktop app by OEM literally streams the screen bytes to the desktop, and users need to click the streamed screen on their desktop to be able to control the device. Hence this is my best effort.

Tests

Run locally:

python -m pytest pylabrobot/btx pylabrobot/capabilities/electroporation -q
python -m ruff check pylabrobot/btx pylabrobot/capabilities/electroporation docs/user_guide/capabilities/electroporation.ipynb docs/user_guide/btx/gemini_x2/hello-world.ipynb
python -m ruff format --check pylabrobot/btx pylabrobot/capabilities/electroporation
python -m mypy pylabrobot/btx pylabrobot/capabilities/electroporation --check-untyped-defs
git diff --check
https://github.com/user-attachments/assets/48a1b306-9d2c-4e9f-b61b-0d91286c81ef

@hazlamshamin

Copy link
Copy Markdown
ContributorAuthor

@rickwierenga
rickwierengaforce-pushed the v1b1 branch 2 times, most recently from 6af085c to 1ae9dc6CompareAugust 1, 2026 20:32
@rickwierenga
rickwierenga changed the base branch from v1b1 to mainAugust 23, 2026 23:25
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from 1c282c8 to d8cae69CompareAugust 23, 2026 23:39
rickwierengaand others added 5 commits August 23, 2026 17:29
Active pulse delivery requires bounded framebuffer traffic. Use one
framebuffer attempt for each run-state poll while preserving configured
retries for navigation and other screen captures.
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch 2 times, most recently from 4cb4a54 to d927525CompareAugust 29, 2026 01:14
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from d927525 to 904c7b1CompareAugust 29, 2026 01:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@hazlamshamin@rickwierenga
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Add Electroporation capability and BTX Gemini X2 backend - #1063

Open
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1
Open

Add Electroporation capability and BTX Gemini X2 backend#1063
hazlamshamin wants to merge 9 commits into
PyLabRobot:mainfrom
hazlamshamin:btx-electroporation-v1b1

Conversation

@hazlamshamin

Copy link
Copy Markdown
Contributor

Summary

This PR adds a new Electroporation capability and a BTX Gemini X2 implementation for prepared electroporation runs.

The workflow is modeled around the actual Gemini X2 / HT-200 operation:

  1. Create a temporary user protocol on the Gemini.
  2. Arm that protocol on the Run Protocol screen through the touchscreen workflow.
  3. Optionally set HT-200 plate columns.
  4. Start the prepared run or cancel it.
  5. Match the new BTXDATA run log and clean up the temporary protocol.

What changed

  • Added pylabrobot.capabilities.electroporation

    • Electroporation frontend
    • ElectroporationBackend
    • serializable protocol/run/result dataclasses
    • chatterbox backend for device-free tests/docs
  • Added pylabrobot.btx

    • BTXGeminiX2 device
    • Gemini X2 driver and electroporation backend
    • Protocol Manager/file-transfer control for user protocols and BTXDATA logs
    • GhostTouch RSI touchscreen control for selecting and starting user protocols
    • HT-200 helper for plate-handler assumptions
  • Added BTX/Gemini X2 test fixtures

    • selected physical-device screen captures from a real GO run
    • portable metadata only; no local paths or host serial-port details
    • tests for screen detection, protocol selection, two-step Set Plate Columns, GO completion, and log matching
  • Added docs

    • electroporation capability notebook
    • BTX Gemini X2 user guide notebook
    • BTX install notes and pylabrobot[btx] extra
    • API docs and supported-devices listing
  • CI

    • adds btx to the extras test matrix
    • installs external tesseract-ocr for btx/all CI jobs because GhostTouch OCR depends on the tesseract executable
  • Also includes one docs-only fix for existing bulk-dispenser API module paths (peristaltic8/backend8, syringe8/backend8).

Hardware validation

Validated on a physical BTX Gemini X2 with HT-200 using a temporary square-wave protocol:

  • firmware: BTX Gemini 4.0.4
  • plate columns: 3
  • GO pressed: yes
  • run log matched under \BTXDATA
  • result: complete / no error
  • temporary protocol deleted after run

The committed screen fixtures are selected PNGs from this run and are used for digital tests so the screen-state logic can be tested without the physical device. See btx/test_data/gemini_x2/screens, as well as the attached video for "The Ghost Touch". I believe this is the first crazy backend that relies on screen detection and OCR to control lab devices. Mainly due to inability of the machine to expose firmware API that allows seamless control by a desktop. The remote desktop app by OEM literally streams the screen bytes to the desktop, and users need to click the streamed screen on their desktop to be able to control the device. Hence this is my best effort.

Tests

Run locally:

python -m pytest pylabrobot/btx pylabrobot/capabilities/electroporation -q
python -m ruff check pylabrobot/btx pylabrobot/capabilities/electroporation docs/user_guide/capabilities/electroporation.ipynb docs/user_guide/btx/gemini_x2/hello-world.ipynb
python -m ruff format --check pylabrobot/btx pylabrobot/capabilities/electroporation
python -m mypy pylabrobot/btx pylabrobot/capabilities/electroporation --check-untyped-defs
git diff --check
https://github.com/user-attachments/assets/48a1b306-9d2c-4e9f-b61b-0d91286c81ef

@hazlamshamin

Copy link
Copy Markdown
ContributorAuthor

@rickwierenga
rickwierengaforce-pushed the v1b1 branch 2 times, most recently from 6af085c to 1ae9dc6CompareAugust 1, 2026 20:32
@rickwierenga
rickwierenga changed the base branch from v1b1 to mainAugust 23, 2026 23:25
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from 1c282c8 to d8cae69CompareAugust 23, 2026 23:39
rickwierengaand others added 5 commits August 23, 2026 17:29
Active pulse delivery requires bounded framebuffer traffic. Use one
framebuffer attempt for each run-state poll while preserving configured
retries for navigation and other screen captures.
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch 2 times, most recently from 4cb4a54 to d927525CompareAugust 29, 2026 01:14
@rickwierenga
rickwierengaforce-pushed the btx-electroporation-v1b1 branch from d927525 to 904c7b1CompareAugust 29, 2026 01:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@hazlamshamin@rickwierenga