Persistindo dispositivos e organizando nosso projeto Dart com Clean Architecture – Série Braço Robótico #4
Neste quarto artigo da nossa série sobre controle de um braço robótico usando Arduino, Dart e Flutter, vamos avançar significativamente rumo a um projeto robusto, escalável e profissional.
Nessa etapa, nosso foco é persistir e gerenciar os dispositivos conectados, além de organizar melhor nosso código aplicando os conceitos da arquitetura limpa (Clean Architecture).
📌 Todo o código desenvolvido neste artigo está disponível no repositório Git:
👉 https://github.com/brasizza/projeto_braco_robo.git
Por que persistir os dispositivos?
Ao gerenciar conexões com múltiplas placas ESP32 (Vespa), precisamos manter informações detalhadas e persistentes sobre cada dispositivo. Isso garante que nosso sistema tenha controle sobre quem está conectado, facilitando ações como identificação única, gerenciamento individualizado e remoção adequada caso haja desconexão.
No nosso projeto, optamos por utilizar um modelo simples, porém eficiente: a entidade Device
.
Entidade Device
O modelo (Device
) representa cada dispositivo conectado e contém informações essenciais para a identificação e interação com o mesmo.
Exemplo do nosso modelo:
class Device {
String deviceId;
String deviceType;
WebSocket? socket;
Device({
required this.deviceId,
required this.deviceType,
this.socket,
});
Map<String, dynamic> toMap() {
return {
'deviceId': deviceId,
'deviceType': deviceType,
};
}
factory Device.fromMap(Map<String, dynamic> map) {
return Device(
deviceId: map['deviceId'],
deviceType: map['deviceType'],
);
}
}
Observe que optamos por armazenar o deviceId
e o deviceType
, facilitando consultas futuras e gerando uma identificação clara de cada braço robótico conectado.
Gerenciando nossos Devices
Para facilitar a manipulação e armazenamento dos nossos dispositivos conectados, criamos uma classe chamada GerenciarDevices
. Ela é responsável por cadastrar novos dispositivos, removê-los caso desconectem e listar todos os dispositivos ativos.
Veja um trecho dessa classe:
class GerenciarDevices {
final Map<String, Device> _devices = {};
void cadastrarDevice(Device device) {
if (!_devices.containsKey(device.deviceId)) {
_devices[device.deviceId] = device;
}
}
void removeDevice(String deviceId) {
if (_devices.containsKey(deviceId)) {
_devices[deviceId]?.socket?.close();
_devices.remove(deviceId);
}
}
List<Device> mostrarDevices() => _devices.values.toList();
}
Implementação do SocketServer em Dart
Na camada de infraestrutura (infra
), criamos um servidor WebSocket (SocketServer
) eficiente que gerencia conexões, desconexões, e timeouts. Esse servidor interage diretamente com nossa classe de gerenciamento (GerenciarDevices
).
Pontos importantes do SocketServer
:
- Recepção das conexões:
Future<void> _pegarRequequisicao(HttpRequest request) async {
WebSocket socket = await WebSocketTransformer.upgrade(request);
socket.pingInterval = Duration(seconds: 5);
String? deviceId = request.headers.value('deviceID');
String? deviceType = request.headers.value('deviceType');
if (deviceId == null || deviceType == null) {
socket.close(WebSocketStatus.goingAway);
} else {
var device = Device(deviceId: deviceId, deviceType: deviceType, socket: socket);
gerenciarDevices.cadastrarDevice(device);
print("Socket foi conectado, bem vindo: $deviceId");
socket.listen((mensagem) {
print("Mensagem do socket: $mensagem");
}, onError: (erro) {
print("Erro na conexão: $erro");
gerenciarDevices.removeDevice(deviceId);
}, onDone: () {
print("Socket desconectado");
gerenciarDevices.removeDevice(deviceId);
});
}
}
Tratamento inteligente de conexões:
Garantimos estabilidade com reconexões automáticas em caso de perda de conexão e identificamos claramente cada conexão através do deviceId
.
Listagem de dispositivos via HTTP:
Implementamos também uma rota HTTP simples que permite listar rapidamente todos os dispositivos conectados, facilitando monitoramento externo:
void gerenciarHttp(HttpRequest request) {
if (request.uri.path == '/devices') {
request.response
..headers.contentType = ContentType.json
..statusCode = HttpStatus.ok
..write(json.encode({
'devices': gerenciarDevices.mostrarDevices().map((d) => d.toMap()).toList()
}))
..close();
}
}
Aplicando Clean Architecture (Arquitetura Limpa)
Organizar nosso projeto utilizando a Arquitetura Limpa foi essencial para torná-lo modularizado, fácil de expandir e manter. Adotamos a seguinte estrutura básica no projeto:
- Domain
- Entidades (
Device
) - Casos de uso (
GerenciarDevices
)
- Entidades (
- Infra
- Camada responsável pelo gerenciamento do servidor WebSocket (
SocketServer
)
- Camada responsável pelo gerenciamento do servidor WebSocket (
- Main
- Inicialização do projeto e injeção de dependências.
Essa separação clara de responsabilidades permite uma escalabilidade muito maior, onde mudanças em uma camada não impactam diretamente as outras.
Próximos Passos
Com nosso projeto já estruturado e organizado, estamos preparados para dar um passo adiante e desenvolver o aplicativo em Flutter. O app permitirá controlar remotamente e intuitivamente nosso braço robótico utilizando a comunicação que criamos via WebSocket.
Continue acompanhando a série e domine a criação de projetos completos usando Arduino, Dart e Flutter!
Publicar comentário