feat(web): migra Movimento com homem-morto e trava de hardware
- Controle: opt-in de movimento fisico, botao "segure para mover" (mouse, toque e teclado), sliders das articulacoes tambem so movem enquanto pressionados; velocidade e interpolacao ficam no servidor - sequencias prontas e posturas salvas: previa so na tela ou execucao segurando o botao ate o fim; soltar interrompe na hora - posturas com nome inline (sem prompt), exclusao confirmada inline, exportar/importar; posturas aparecem na paleta (Ctrl+K) - Calibracao: testes de fim de curso por canal, marcar limites e tabela editavel com validacao antes de salvar - Diagnostico (antigo Servos): ao vivo limitado a 10/s, bloqueado quando o braco esta com os motores - desenho esquematico do braco em SVG com previa animada - servidor: evento 'motores' tambem ao ligar canais; renovar no alvo devolve movendo=false para a sequencia avancar - aba escondida ou E-Stop soltam o braco e desativam o movimento Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -150,6 +150,32 @@ def testar(base, server, capturas):
|
||||
if capturas:
|
||||
page.screenshot(path=str(Path(capturas) / 'reacoes.png'), full_page=True)
|
||||
|
||||
# 6d. braço: opt-in, homem-morto e desligamento pelo prazo ao soltar
|
||||
hw = server.servos.hw
|
||||
page.get_by_role('link', name='Movimento').first.click()
|
||||
segurar = page.get_by_role('button', name='Segure para ir ao alvo')
|
||||
expect(segurar).to_be_disabled()
|
||||
page.get_by_role('switch', name='Ativar movimento físico').locator('xpath=..').click()
|
||||
expect(segurar).to_be_enabled()
|
||||
hw.calls.clear()
|
||||
segurar.hover()
|
||||
page.mouse.down()
|
||||
page.wait_for_timeout(700)
|
||||
assert server.servos.active, 'motores deveriam estar ligados enquanto segura'
|
||||
expect(page.get_by_text('Motores ligados')).to_be_visible(timeout=3000)
|
||||
page.mouse.up()
|
||||
page.wait_for_timeout(900)
|
||||
assert not server.servos.active, 'motores deveriam desligar sozinhos após soltar'
|
||||
assert any(us for _, us in hw.calls), hw.calls
|
||||
if capturas:
|
||||
page.screenshot(path=str(Path(capturas) / 'movimento.png'), full_page=True)
|
||||
# E-Stop desativa o movimento físico na hora
|
||||
page.get_by_role('button', name='Parar tudo').click()
|
||||
expect(page.get_by_role('switch', name='Ativar movimento físico')).to_be_disabled(timeout=3000)
|
||||
expect(segurar).to_be_disabled()
|
||||
page.get_by_role('button', name='PARADO · Rearmar').click()
|
||||
expect(page.get_by_role('button', name='Parar tudo')).to_be_visible(timeout=3000)
|
||||
|
||||
# 7. celular: barra inferior e nada de rolagem horizontal
|
||||
page.set_viewport_size({'width': 400, 'height': 860})
|
||||
page.get_by_role('link', name='Ao vivo').click()
|
||||
@@ -170,7 +196,7 @@ def testar(base, server, capturas):
|
||||
|
||||
assert not erros, erros
|
||||
browser.close()
|
||||
print('OK: token, SSE, dock 1-9, paleta, E-Stop, ajustes otimistas, conversa, memória, reações com desfazer, celular 400px, tema')
|
||||
print('OK: token, SSE, dock 1-9, paleta, E-Stop, ajustes otimistas, conversa, memória, reações com desfazer, braço com homem-morto, celular 400px, tema')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -215,6 +215,14 @@ class BracoMovimentoTest(unittest.TestCase):
|
||||
self.assertEqual(self.servos.active, {})
|
||||
self.assertIsNone(self.servos.dono)
|
||||
|
||||
def test_renovar_no_alvo_informa_que_chegou(self):
|
||||
self.arm.move([90] * 7)
|
||||
self.hw.calls.clear()
|
||||
r = self.arm.move([90] * 7, velocidade=60, ttl_ms=400)
|
||||
self.assertFalse(r['movendo'])
|
||||
self.assertIsNone(self.arm.alvo)
|
||||
self.assertEqual(len(self.hw.calls), 8) # reenergiza os 8 canais com o novo prazo
|
||||
|
||||
def test_parametros_invalidos(self):
|
||||
for kwargs in (dict(velocidade=1000), dict(velocidade=True), dict(ttl_ms=50), dict(ttl_ms=5000)):
|
||||
with self.assertRaises(ValueError):
|
||||
|
||||
Reference in New Issue
Block a user