First commit
This commit is contained in:
282
main.go
Normal file
282
main.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user