First commit

This commit is contained in:
artemmukh
2025-05-29 10:37:21 +03:00
commit cddbb2549a
4 changed files with 453 additions and 0 deletions

282
main.go Normal file
View File

@@ -0,0 +1,282 @@
package main
import (
"fmt"
"log"
"strings"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"go.bug.st/serial"
)
type UARTApp struct {
app fyne.App
window fyne.Window
portEntry *widget.Select
baudEntry *widget.Select
lineEnding *widget.RadioGroup
connectButton *widget.Button
disconnectButton *widget.Button
sendEntry *widget.Entry
sendButton *widget.Button
receiveText *widget.Entry
statusLabel *widget.Label
port serial.Port
isConnected bool
receiveBuffer strings.Builder
}
func main() {
myApp := app.New()
uartApp := &UARTApp{
app: myApp,
window: myApp.NewWindow("UART Terminal"),
}
uartApp.createUI()
uartApp.window.Resize(fyne.NewSize(800, 600)) // Увеличиваем начальный размер окна
uartApp.window.ShowAndRun()
}
func (a *UARTApp) createUI() {
// Port selection
a.portEntry = widget.NewSelect([]string{}, nil)
a.refreshPorts()
// Baud rate selection
a.baudEntry = widget.NewSelect([]string{
"300", "600", "1200", "2400", "4800", "9600",
"14400", "19200", "28800", "38400", "57600", "115200",
}, nil)
a.baudEntry.SetSelected("9600")
// Line ending selection
a.lineEnding = widget.NewRadioGroup([]string{"None", "CR", "LF", "CR+LF"}, nil)
a.lineEnding.SetSelected("CR+LF")
// Buttons
a.connectButton = widget.NewButton("Connect", a.connect)
a.disconnectButton = widget.NewButton("Disconnect", a.disconnect)
a.disconnectButton.Disable()
// Send section
a.sendEntry = widget.NewEntry()
a.sendEntry.SetPlaceHolder("Enter message to send")
a.sendButton = widget.NewButton("Send", a.sendMessage)
a.sendButton.Disable()
// Receive section
a.receiveText = widget.NewMultiLineEntry()
a.receiveText.Wrapping = fyne.TextWrapOff
// Status bar
a.statusLabel = widget.NewLabel("Disconnected")
// Создаем контейнер с настройками
settingsBox := container.NewVBox(
widget.NewLabel("Serial Port Settings"),
container.NewHBox(widget.NewLabel("Port:"), a.portEntry, widget.NewButton("Refresh", a.refreshPorts)),
container.NewHBox(widget.NewLabel("Baud Rate:"), a.baudEntry),
container.NewHBox(widget.NewLabel("Line Ending:"), a.lineEnding),
container.NewHBox(a.connectButton, a.disconnectButton),
)
// Контейнер для отправки сообщений
sendBox := container.NewVBox(
widget.NewLabel("Send Message"),
a.sendEntry,
a.sendButton,
)
// Верхняя часть (настройки + отправка)
topSection := container.NewHSplit(
container.NewVScroll(settingsBox),
sendBox,
)
topSection.SetOffset(0.4) // Настройки занимают 40% верхней части
// Нижняя часть (полученные данные)
receiveBox := container.NewBorder(
widget.NewLabel("Received Data (one message per line)"),
nil,
nil,
nil,
container.NewScroll(a.receiveText),
)
// Главный контейнер с правильными пропорциями
mainContent := container.NewVSplit(
topSection,
receiveBox,
)
mainContent.SetOffset(0.3) // Верхняя часть занимает 30% высоты
// Создаем окончательный макет с растягиванием
finalLayout := container.NewBorder(
nil,
a.statusLabel,
nil,
nil,
mainContent,
)
a.window.SetContent(finalLayout)
}
func (a *UARTApp) refreshPorts() {
ports, err := serial.GetPortsList()
if err != nil {
log.Println("Error getting ports list:", err)
return
}
if len(ports) == 0 {
ports = []string{"No ports available"}
a.portEntry.Disable()
} else {
a.portEntry.Enable()
}
a.portEntry.Options = ports
if len(ports) > 0 && ports[0] != "No ports available" {
a.portEntry.SetSelected(ports[0])
}
}
func (a *UARTApp) connect() {
if a.isConnected {
return
}
portName := a.portEntry.Selected
if portName == "" {
a.statusLabel.SetText("Please select a port")
return
}
baudRate := a.baudEntry.Selected
if baudRate == "" {
a.statusLabel.SetText("Please select a baud rate")
return
}
mode := &serial.Mode{
BaudRate: mustParseInt(baudRate),
}
port, err := serial.Open(portName, mode)
if err != nil {
a.statusLabel.SetText("Error opening port: " + err.Error())
return
}
a.port = port
a.isConnected = true
a.connectButton.Disable()
a.disconnectButton.Enable()
a.sendButton.Enable()
a.statusLabel.SetText("Connected to " + portName + " at " + baudRate + " baud")
a.receiveBuffer.Reset()
go a.readFromPort()
}
func (a *UARTApp) disconnect() {
if !a.isConnected || a.port == nil {
return
}
err := a.port.Close()
if err != nil {
a.statusLabel.SetText("Error closing port: " + err.Error())
} else {
a.statusLabel.SetText("Disconnected")
}
a.isConnected = false
a.port = nil
a.connectButton.Enable()
a.disconnectButton.Disable()
a.sendButton.Disable()
}
func (a *UARTApp) sendMessage() {
if !a.isConnected || a.port == nil {
a.statusLabel.SetText("Not connected")
return
}
message := a.sendEntry.Text
if message == "" {
return
}
// Add line ending based on selection
switch a.lineEnding.Selected {
case "CR":
message += "\r"
case "LF":
message += "\n"
case "CR+LF":
message += "\r\n"
}
_, err := a.port.Write([]byte(message))
if err != nil {
a.statusLabel.SetText("Send error: " + err.Error())
} else {
a.sendEntry.SetText("")
}
}
func (a *UARTApp) readFromPort() {
buf := make([]byte, 1024)
for a.isConnected && a.port != nil {
n, err := a.port.Read(buf)
if err != nil {
a.disconnect()
a.app.Quit()
return
}
if n > 0 {
received := string(buf[:n])
a.processReceivedData(received)
}
time.Sleep(100 * time.Millisecond)
}
}
func (a *UARTApp) processReceivedData(data string) {
// Добавляем данные в буфер
a.receiveBuffer.WriteString(data)
// Получаем текущий текст и добавляем новые данные с новой строки
currentText := a.receiveText.Text
if currentText != "" {
currentText += "\n"
}
currentText += data
// Обновляем текстовое поле в UI
a.receiveText.SetText(currentText)
// Прокручиваем вниз
a.receiveText.CursorRow = len(strings.Split(currentText, "\n")) - 1
a.receiveText.Refresh()
}
func mustParseInt(s string) int {
var result int
_, err := fmt.Sscanf(s, "%d", &result)
if err != nil {
return 9600 // default
}
return result
}