Files
bj_power/bj_power_wms_client/internal/ui/mainform.go
T

1065 lines
25 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package ui
import (
"context"
"fmt"
"log"
"os"
"strconv"
"time"
"github.com/ying32/govcl/vcl"
"github.com/ying32/govcl/vcl/types"
"github.com/ying32/govcl/vcl/types/colors"
"go.bug.st/serial"
"bj_power_wms_client/internal/api"
"bj_power_wms_client/internal/app"
"bj_power_wms_client/internal/autostart"
"bj_power_wms_client/internal/config"
"bj_power_wms_client/internal/serialport"
)
type itemRow struct {
panel *vcl.TPanel
name *vcl.TLabel
value *vcl.TLabel
unit *vcl.TLabel
}
type MainForm struct {
*vcl.TForm
cfg config.Config
apiClient *api.Client
collector *app.Collector
serial *serialport.Port
// 模å—A:顶部控制æ 
portLab *vcl.TLabel
portCombo *vcl.TComboBox
statusLabel *vcl.TLabel
connectBtn *vcl.TButton
refreshBtn *vcl.TButton
labU *vcl.TLabel
rbMM *vcl.TRadioButton
rbInch *vcl.TRadioButton
autoStartChk *vcl.TCheckBox
// 模å—B:工å•ä¿¡æ?
workOrderNoLabel *vcl.TLabel
workOrderNoEdit *vcl.TEdit
queryBtn *vcl.TButton
typeLabel *vcl.TLabel
typeCombo *vcl.TComboBox
countLabel *vcl.TLabel
countValue *vcl.TLabel
statusLabel2 *vcl.TLabel
statusValue *vcl.TLabel
timeLabel *vcl.TLabel
timeValue *vcl.TLabel
// 模å—C:仪表盘显示åŒ?
rows []itemRow
currentType string
// 模å—D:æ“作与数æ®åŒ?
bottomLabel *vcl.TLabel
workpieceNoEdit *vcl.TEdit
filterBtn *vcl.TButton
exportBtn *vcl.TButton
clearBtn *vcl.TButton
submitBtn *vcl.TButton
histView *vcl.TListView
// 定时�
reconnT *vcl.TTimer
// 当å‰å·¥å•ä¿¡æ¯
currentWorkOrder *api.WorkOrderInfo
}
var MainFormPtr *MainForm
var injectedCfg config.Config
var injectedAppName string
func InitForm(c config.Config, appName string) {
injectedCfg = c
injectedAppName = appName
}
const (
baseW int32 = 1280
baseH int32 = 720
minFont int32 = 20
maxFont int32 = 96
panelH int32 = 180
gapX int32 = 8
valSize int32 = 28
nameSize int32 = 16
unitSize int32 = 16
topRowH int32 = 44
)
var squareItemDefs = []string{"â‘?å¼§é¢åŠå¾„ B"}
var circleItemDefs = []string{"â‘?çƒé¢ç›´å¾„ A", "â‘?çƒé¢è½®å»“åº?B", "â‘?çƒé¢ä½ç½® C"}
func (f *MainForm) OnFormCreate(sender vcl.IObject) {
f.cfg = injectedCfg
f.apiClient = api.NewClient(f.cfg.APIURL)
f.collector = app.NewCollector(squareItemDefs)
f.SetCaption("7315-2 åƒåˆ†å°ºé‡‡é›?)
f.SetWidth(1280)
f.SetHeight(720)
f.SetPosition(types.PoScreenCenter)
f.SetWindowState(types.WsMaximized)
f.SetOnResize(f.onResize)
f.initModuleA()
f.initModuleB()
f.initModuleC("square")
f.initModuleD()
f.reconnT = vcl.NewTimer(f)
f.reconnT.SetInterval(3000)
f.reconnT.SetOnTimer(func(s vcl.IObject) { f.tryConnectSerial() })
f.reconnT.SetEnabled(false)
f.relayout()
f.refreshPortList()
f.tryConnectSerial()
f.onFilter(nil)
}
func (f *MainForm) initModuleA() {
f.portLab = vcl.NewLabel(f)
f.portLab.SetParent(f)
f.portLab.SetLeft(16)
f.portLab.SetTop(12)
f.portLab.SetCaption("串å£:")
f.portLab.Font().SetSize(14)
f.portCombo = vcl.NewComboBox(f)
f.portCombo.SetParent(f)
f.portCombo.SetLeft(72)
f.portCombo.SetTop(8)
f.portCombo.SetWidth(140)
f.portCombo.SetHeight(topRowH)
f.portCombo.SetStyle(types.CsDropDownList)
f.portCombo.Font().SetSize(14)
f.statusLabel = vcl.NewLabel(f)
f.statusLabel.SetParent(f)
f.statusLabel.SetLeft(220)
f.statusLabel.SetTop(14)
f.statusLabel.SetWidth(140)
f.statusLabel.SetHeight(32)
f.statusLabel.SetCaption("âæœªè¿žæŽ¥")
f.statusLabel.Font().SetSize(14)
f.connectBtn = vcl.NewButton(f)
f.connectBtn.SetParent(f)
f.connectBtn.SetLeft(368)
f.connectBtn.SetTop(8)
f.connectBtn.SetWidth(80)
f.connectBtn.SetHeight(topRowH)
f.connectBtn.SetCaption("连接")
f.connectBtn.Font().SetSize(14)
f.connectBtn.SetOnClick(f.onConnect)
f.refreshBtn = vcl.NewButton(f)
f.refreshBtn.SetParent(f)
f.refreshBtn.SetLeft(456)
f.refreshBtn.SetTop(8)
f.refreshBtn.SetWidth(80)
f.refreshBtn.SetHeight(topRowH)
f.refreshBtn.SetCaption("刷新")
f.refreshBtn.Font().SetSize(14)
f.refreshBtn.SetOnClick(f.onRefreshPorts)
f.labU = vcl.NewLabel(f)
f.labU.SetParent(f)
f.labU.SetLeft(544)
f.labU.SetTop(14)
f.labU.SetCaption("åä½:")
f.labU.Font().SetSize(14)
f.rbMM = vcl.NewRadioButton(f)
f.rbMM.SetParent(f)
f.rbMM.SetLeft(600)
f.rbMM.SetTop(10)
f.rbMM.SetCaption("mm")
f.rbMM.SetChecked(f.cfg.DisplayUnit != "inch")
f.rbMM.Font().SetSize(14)
f.rbMM.SetOnClick(f.onUnitChange)
f.rbInch = vcl.NewRadioButton(f)
f.rbInch.SetParent(f)
f.rbInch.SetLeft(670)
f.rbInch.SetTop(10)
f.rbInch.SetCaption("inch")
f.rbInch.SetChecked(f.cfg.DisplayUnit == "inch")
f.rbInch.Font().SetSize(14)
f.rbInch.SetOnClick(f.onUnitChange)
f.autoStartChk = vcl.NewCheckBox(f)
f.autoStartChk.SetParent(f)
f.autoStartChk.SetLeft(740)
f.autoStartChk.SetTop(12)
f.autoStartChk.SetCaption("开机å¯åŠ?)
f.autoStartChk.Font().SetSize(14)
f.autoStartChk.SetChecked(autostart.IsEnabled(injectedAppName))
f.autoStartChk.SetOnClick(f.onAutoStartToggle)
}
func (f *MainForm) initModuleB() {
f.workOrderNoLabel = vcl.NewLabel(f)
f.workOrderNoLabel.SetParent(f)
f.workOrderNoLabel.SetLeft(16)
f.workOrderNoLabel.SetTop(60)
f.workOrderNoLabel.SetCaption("å·¥å•å?")
f.workOrderNoLabel.Font().SetSize(16)
f.workOrderNoEdit = vcl.NewEdit(f)
f.workOrderNoEdit.SetParent(f)
f.workOrderNoEdit.SetLeft(80)
f.workOrderNoEdit.SetTop(56)
f.workOrderNoEdit.SetWidth(350)
f.workOrderNoEdit.SetHeight(topRowH)
f.workOrderNoEdit.SetTextHint("请输入工å•å·")
f.workOrderNoEdit.Font().SetSize(16)
f.queryBtn = vcl.NewButton(f)
f.queryBtn.SetParent(f)
f.queryBtn.SetLeft(438)
f.queryBtn.SetTop(56)
f.queryBtn.SetWidth(100)
f.queryBtn.SetHeight(topRowH)
f.queryBtn.SetCaption("查询")
f.queryBtn.Font().SetSize(16)
f.queryBtn.SetOnClick(f.onQueryWorkOrder)
f.typeLabel = vcl.NewLabel(f)
f.typeLabel.SetParent(f)
f.typeLabel.SetLeft(540)
f.typeLabel.SetTop(60)
f.typeLabel.SetCaption("工件类型:")
f.typeLabel.Font().SetSize(14)
f.typeCombo = vcl.NewComboBox(f)
f.typeCombo.SetParent(f)
f.typeCombo.SetLeft(618)
f.typeCombo.SetTop(56)
f.typeCombo.SetWidth(120)
f.typeCombo.SetHeight(topRowH)
f.typeCombo.SetStyle(types.CsDropDownList)
f.typeCombo.SetReadOnly(true)
f.typeCombo.Font().SetSize(14)
f.typeCombo.Items().Add("方形")
f.typeCombo.Items().Add("圆形")
f.countLabel = vcl.NewLabel(f)
f.countLabel.SetParent(f)
f.countLabel.SetLeft(746)
f.countLabel.SetTop(60)
f.countLabel.SetCaption("æ•°é‡:")
f.countLabel.Font().SetSize(14)
f.countValue = vcl.NewLabel(f)
f.countValue.SetParent(f)
f.countValue.SetLeft(800)
f.countValue.SetTop(60)
f.countValue.SetWidth(60)
f.countValue.SetCaption("--")
f.countValue.Font().SetSize(14)
f.statusLabel2 = vcl.NewLabel(f)
f.statusLabel2.SetParent(f)
f.statusLabel2.SetLeft(868)
f.statusLabel2.SetTop(60)
f.statusLabel2.SetCaption("状�")
f.statusLabel2.Font().SetSize(14)
f.statusValue = vcl.NewLabel(f)
f.statusValue.SetParent(f)
f.statusValue.SetLeft(918)
f.statusValue.SetTop(60)
f.statusValue.SetWidth(80)
f.statusValue.SetCaption("--")
f.statusValue.Font().SetSize(14)
f.timeLabel = vcl.NewLabel(f)
f.timeLabel.SetParent(f)
f.timeLabel.SetLeft(1006)
f.timeLabel.SetTop(60)
f.timeLabel.SetCaption("创建时间:")
f.timeLabel.Font().SetSize(14)
f.timeValue = vcl.NewLabel(f)
f.timeValue.SetParent(f)
f.timeValue.SetLeft(1100)
f.timeValue.SetTop(60)
f.timeValue.SetWidth(170)
f.timeValue.SetCaption("--")
f.timeValue.Font().SetSize(14)
}
func (f *MainForm) initModuleC(workpieceType string) {
for _, row := range f.rows {
if row.panel != nil {
row.panel.Free()
}
}
f.rows = nil
defs := squareItemDefs
if workpieceType == "circle" {
defs = circleItemDefs
}
f.collector = app.NewCollector(defs)
f.rows = make([]itemRow, len(defs))
for i := 0; i < len(defs); i++ {
row := itemRow{}
row.panel = vcl.NewPanel(f)
row.panel.SetParent(f)
row.panel.SetBevelOuter(types.BvRaised)
row.panel.SetColor(colors.ClBtnFace)
row.name = vcl.NewLabel(row.panel)
row.name.SetParent(row.panel)
row.name.SetCaption(defs[i])
row.name.Font().SetSize(nameSize)
row.name.SetAlignment(types.TaCenter)
row.value = vcl.NewLabel(row.panel)
row.value.SetParent(row.panel)
row.value.SetCaption("----")
row.value.Font().SetSize(valSize)
row.value.SetAlignment(types.TaCenter)
row.value.SetLayout(types.TlCenter)
idx := i
row.value.SetOnClick(func(s vcl.IObject) {
initial, _ := f.collector.DisplayValue(idx, f.currentUnit())
if initial == "----" {
initial = ""
}
val, ok := showNumKeyPad(f, initial)
if ok {
v, err := strconv.ParseFloat(val, 64)
if err == nil {
mm := v
if f.rbInch.Checked() {
mm = v * app.MmPerInch
}
f.collector.SetValue(idx, mm)
f.refreshRows()
}
}
})
row.unit = vcl.NewLabel(row.panel)
row.unit.SetParent(row.panel)
row.unit.SetCaption(f.currentUnitLabel())
row.unit.Font().SetSize(unitSize)
row.unit.SetAlignment(types.TaCenter)
row.panel.SetOnClick(func(s vcl.IObject) { f.onRowClick(idx) })
f.rows[i] = row
}
f.currentType = workpieceType
}
func (f *MainForm) initModuleD() {
f.bottomLabel = vcl.NewLabel(f)
f.bottomLabel.SetParent(f)
f.bottomLabel.SetCaption("åºåˆ—å? - 末帧: -")
f.bottomLabel.Font().SetSize(12)
f.workpieceNoEdit = vcl.NewEdit(f)
f.workpieceNoEdit.SetParent(f)
f.workpieceNoEdit.SetWidth(180)
f.workpieceNoEdit.SetHeight(44)
f.workpieceNoEdit.SetTextHint("请输入工件å·")
f.workpieceNoEdit.Font().SetSize(14)
f.workpieceNoEdit.SetOnExit(f.onWorkpieceNoExit)
f.filterBtn = vcl.NewButton(f)
f.filterBtn.SetParent(f)
f.filterBtn.SetWidth(44)
f.filterBtn.SetHeight(44)
f.filterBtn.SetCaption("ðŸ”")
f.filterBtn.Font().SetSize(14)
f.filterBtn.SetOnClick(f.onFilter)
f.exportBtn = vcl.NewButton(f)
f.exportBtn.SetParent(f)
f.exportBtn.SetWidth(120)
f.exportBtn.SetHeight(44)
f.exportBtn.SetCaption("导出")
f.exportBtn.Font().SetSize(14)
f.exportBtn.SetOnClick(f.onExport)
f.clearBtn = vcl.NewButton(f)
f.clearBtn.SetParent(f)
f.clearBtn.SetWidth(120)
f.clearBtn.SetHeight(44)
f.clearBtn.SetCaption("清除")
f.clearBtn.Font().SetSize(14)
f.clearBtn.SetOnClick(f.onClear)
f.submitBtn = vcl.NewButton(f)
f.submitBtn.SetParent(f)
f.submitBtn.SetWidth(120)
f.submitBtn.SetHeight(44)
f.submitBtn.SetCaption(交")
f.submitBtn.Font().SetSize(14)
f.submitBtn.SetOnClick(f.onSubmit)
f.histView = vcl.NewListView(f)
f.histView.SetParent(f)
f.histView.SetViewStyle(types.VsReport)
f.histView.SetReadOnly(true)
f.histView.SetRowSelect(true)
f.histView.SetGridLines(true)
f.histView.Font().SetSize(12)
f.refreshHistoryColumns()
}
func (f *MainForm) refreshHistoryColumns() {
items := f.histView.Items()
items.BeginUpdate()
defer items.EndUpdate()
for i := f.histView.Columns().Count() - 1; i >= 0; i-- {
f.histView.Columns().Delete(i)
}
if f.currentType == "circle" {
cols := []string{"æ—¶é—´", "å·¥å•å?, "工件å?, "分类", ƒé¢ç›´å¾„ A", ƒé¢è½®å»“åº?B", ƒé¢ä½ç½® C"}
for _, c := range cols {
col := f.histView.Columns().Add()
col.SetCaption(c)
}
} else {
cols := []string{"æ—¶é—´", "å·¥å•å?, "工件å?, "分类", "å¼§é¢åŠå¾„ B"}
for _, c := range cols {
col := f.histView.Columns().Add()
col.SetCaption(c)
}
}
}
func (f *MainForm) currentUnit() app.Unit {
if f.rbInch.Checked() {
return app.UnitInch
}
return app.UnitMM
}
func (f *MainForm) currentUnitLabel() string {
if f.rbInch.Checked() {
return "inch"
}
return "mm"
}
func (f *MainForm) onUnitChange(sender vcl.IObject) {
for _, row := range f.rows {
row.unit.SetCaption(f.currentUnitLabel())
}
f.refreshRows()
}
func (f *MainForm) onAutoStartToggle(sender vcl.IObject) {
if f.autoStartChk.Checked() {
if err := autostart.Enable(injectedAppName); err != nil {
vcl.ShowMessage("设置开机å¯åЍ失è´? " + err.Error())
f.autoStartChk.SetChecked(false)
}
} else {
if err := autostart.Disable(injectedAppName); err != nil {
vcl.ShowMessage(–消开机å¯åЍ失è´? " + err.Error())
f.autoStartChk.SetChecked(true)
}
}
}
func (f *MainForm) onRowClick(i int) {
f.collector.SetCurrent(i)
f.refreshRows()
}
func (f *MainForm) onQueryWorkOrder(sender vcl.IObject) {
workOrderNo := f.workOrderNoEdit.Text()
if workOrderNo == "" {
vcl.ShowMessage("请输入工å•å·")
return
}
f.queryBtn.SetEnabled(false)
go func() {
ctx := context.Background()
info, err := f.apiClient.QueryWorkOrder(ctx, workOrderNo)
vcl.ThreadSync(func() {
f.queryBtn.SetEnabled(true)
if err != nil {
vcl.ShowMessage("未查询到工å•ä¿¡æ¯ï¼Œè¯·æ£€æŸ¥å·¥å•å·æ˜¯å¦æ­£ç¡®")
f.currentWorkOrder = nil
f.countValue.SetCaption("--")
f.statusValue.SetCaption("--")
f.timeValue.SetCaption("--")
return
}
f.currentWorkOrder = info
f.updateWorkOrderInfo(info)
})
}()
}
func (f *MainForm) updateWorkOrderInfo(info *api.WorkOrderInfo) {
f.typeCombo.SetItemIndex(0)
if info.WorkpieceType == "circle" || info.WorkpieceType == "GENERAL" {
f.typeCombo.SetItemIndex(1)
}
f.countValue.SetCaption(strconv.Itoa(info.WorkpieceCount))
f.statusValue.SetCaption(info.StatusName)
f.timeValue.SetCaption(info.CreatedAt)
displayType := info.WorkpieceType
if displayType == "GENERAL" {
displayType = "circle"
}
if displayType != "" && displayType != f.currentType {
f.initModuleC(displayType)
f.refreshHistoryColumns()
f.relayout()
}
}
func (f *MainForm) onWorkpieceNoExit(sender vcl.IObject) {
f.onFilter(nil)
}
func (f *MainForm) onFilter(sender vcl.IObject) {
workOrderNo := f.workOrderNoEdit.Text()
workpieceNo := f.workpieceNoEdit.Text()
f.submitBtn.SetEnabled(false)
go func() {
ctx := context.Background()
resp, err := f.apiClient.QueryMeasurements(ctx, workOrderNo, workpieceNo, 1, 100)
vcl.ThreadSync(func() {
f.submitBtn.SetEnabled(true)
if err != nil {
return
}
f.updateHistoryTable(resp.List)
})
}()
}
func (f *MainForm) updateHistoryTable(records []api.MeasurementRecord) {
items := f.histView.Items()
items.BeginUpdate()
defer items.EndUpdate()
items.Clear()
for _, r := range records {
item := f.histView.Items().Add()
item.SetCaption(formatTime(r.CreatedAt))
item.SubItems().Add(r.WorkOrderNo)
item.SubItems().Add(r.WorkpieceNo)
item.SubItems().Add(r.Category)
if f.currentType == "circle" {
item.SubItems().Add(fmt.Sprintf("%.3f", r.ValueA))
item.SubItems().Add(fmt.Sprintf("%.3f", r.ValueB))
item.SubItems().Add(fmt.Sprintf("%.3f", r.ValueC))
} else {
item.SubItems().Add(fmt.Sprintf("%.3f", r.ValueA))
}
}
}
func formatTime(s string) string {
for _, layout := range []string{time.RFC3339, "2006-01-02 15:04:05"} {
if t, err := time.Parse(layout, s); err == nil {
return t.Format("01-02 15:04:05")
}
}
return s
}
func (f *MainForm) onExport(sender vcl.IObject) {
items := f.histView.Items()
if items.Count() == 0 {
vcl.ShowMessage("没有数æ®å¯å¯¼å‡?)
return
}
filename := time.Now().Format("20060102_150405") + ".csv"
desktop, _ := os.UserHomeDir()
filepath := desktop + "/Desktop/" + filename
file, err := os.Create(filepath)
if err != nil {
vcl.ShowMessage("åˆå»ºæ–‡ä»¶å¤±è´¥: " + err.Error())
return
}
defer file.Close()
header := "æ—¶é—´,å·¥åå?工件å?åˆç±»"
if f.currentType == "circle" {
header += ",çƒé¢ç›´å¾„ A,çƒé¢è½®å»“åº?B,çƒé¢ä½ç½® C"
} else {
header += ",å¼§é¢åŠå¾„ B"
}
file.WriteString(header + "\n")
for i := int32(0); i < items.Count(); i++ {
item := items.Item(i)
line := fmt.Sprintf("%s,%s,%s,%s",
escapeCSV(item.Caption()),
escapeCSV(item.SubItems().Strings(0)),
escapeCSV(item.SubItems().Strings(1)),
escapeCSV(item.SubItems().Strings(2)))
if f.currentType == "circle" {
line += fmt.Sprintf(",%s,%s,%s",
item.SubItems().Strings(3),
item.SubItems().Strings(4),
item.SubItems().Strings(5))
} else {
line += "," + item.SubItems().Strings(3)
}
file.WriteString(line + "\n")
}
vcl.ShowMessage("å¯¼åºæˆåŠŸ: " + filepath)
}
func escapeCSV(s string) string {
if s == "" {
return ""
}
if contains(s, ",") || contains(s, "\"") || contains(s, "\n") {
return "\"" + replaceAll(s, "\"", "\"\"") + "\""
}
return s
}
func contains(s, substr string) bool {
if len(s) < len(substr) {
return false
}
if s == substr {
return true
}
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}
func replaceAll(s, old, new string) string {
result := ""
for len(s) > 0 {
if len(s) >= len(old) && s[:len(old)] == old {
result += new
s = s[len(old):]
} else {
result += s[:1]
s = s[1:]
}
}
return result
}
func (f *MainForm) onClear(sender vcl.IObject) {
r := vcl.MessageDlg("确认清空当å‰å·¥ä»¶æ•°æ®?", types.MtConfirmation, types.MbYes, types.MbNo)
if r == types.MrYes {
f.collector.Clear()
f.refreshRows()
}
}
func (f *MainForm) onSubmit(sender vcl.IObject) {
workOrderNo := f.workOrderNoEdit.Text()
if workOrderNo == "" {
vcl.ShowMessage("请先输入工å•å?)
return
}
if f.currentWorkOrder == nil {
vcl.ShowMessage("è¯·åˆæŸ¥è¯¢å·¥å")
return
}
workpieceNo := f.workpieceNoEdit.Text()
if workpieceNo == "" {
vcl.ShowMessage("请输入工件å·")
return
}
if !f.collector.AllFilled() {
vcl.ShowMessage("è¯·å®Œæˆæ‰€æœæµé‡?)
return
}
workpieceType := f.currentWorkOrder.WorkpieceType
if workpieceType == "GENERAL" {
workpieceType = "circle"
}
req := api.SubmitRequest{
WorkOrderNo: workOrderNo,
WorkpieceNo: workpieceNo,
Category: "",
WorkpieceType: workpieceType,
ValueA: f.collector.Value(0),
Unit: f.currentUnitLabel(),
Serial: f.selectedPort(),
Timestamp: time.Now().Format("2006-01-02 15:04:05"),
}
if workpieceType == "circle" {
vb := f.collector.Value(1)
vc := f.collector.Value(2)
req.ValueB = &vb
req.ValueC = &vc
}
f.submitBtn.SetEnabled(false)
go func() {
ctx := context.Background()
_, err := f.apiClient.SubmitMeasurement(ctx, req)
vcl.ThreadSync(func() {
f.submitBtn.SetEnabled(true)
if err != nil {
vcl.ShowMessage(交失败: " + err.Error())
return
}
vcl.ShowMessage(交æˆåŠŸ")
f.collector.Clear()
f.refreshRows()
f.onFilter(nil)
})
}()
}
func (f *MainForm) onConnect(sender vcl.IObject) {
if f.serial != nil {
_ = f.serial.Close()
f.serial = nil
f.statusLabel.SetCaption("â—æœªè¿žæŽ¥")
f.statusLabel.Font().SetColor(colors.ClBtnFace)
f.connectBtn.SetCaption("连接")
f.reconnT.SetEnabled(false)
return
}
f.tryConnectSerial()
}
func (f *MainForm) selectedPort() string {
idx := f.portCombo.ItemIndex()
if idx < 0 {
return ""
}
var s string
f.portCombo.GetTextBuf(&s, 64)
return s
}
func (f *MainForm) refreshPortList() {
ports, err := serial.GetPortsList()
if err != nil {
log.Printf("列出串å£å¤±è´¥: %v", err)
return
}
f.portCombo.Clear()
sel := int32(-1)
for i, p := range ports {
f.portCombo.Items().Add(p)
if p == f.cfg.SerialPort {
sel = int32(i)
}
}
if sel >= 0 {
f.portCombo.SetItemIndex(sel)
} else if len(ports) > 0 {
f.portCombo.SetItemIndex(0)
}
}
func (f *MainForm) onRefreshPorts(sender vcl.IObject) {
f.refreshPortList()
}
func (f *MainForm) tryConnectSerial() {
if f.serial != nil {
return
}
name := f.selectedPort()
if name == "" {
f.statusLabel.SetCaption("â—æœªé€‰æ‹©ä¸²å£")
f.statusLabel.Font().SetColor(colors.ClRed)
return
}
sp, err := serialport.Open(name, f.cfg.BaudRate)
if err != nil {
f.statusLabel.SetCaption(fmt.Sprintf("%s â—æœªè¿žæŽ¥", name))
f.statusLabel.Font().SetColor(colors.ClRed)
f.reconnT.SetEnabled(true)
return
}
f.serial = sp
f.statusLabel.SetCaption(fmt.Sprintf("%s â—已连接", name))
f.statusLabel.Font().SetColor(colors.ClGreen)
f.connectBtn.SetCaption("æ–­å¼€")
f.reconnT.SetEnabled(false)
go f.consumeFrames()
}
func (f *MainForm) consumeFrames() {
for {
select {
case fr, ok := <-f.serial.Frames():
if !ok {
return
}
vcl.ThreadSync(func() {
f.collector.ApplyFrame(fr)
f.refreshRows()
})
case err, ok := <-f.serial.Errors():
if !ok {
return
}
log.Printf("串å£é”™è¯¯: %v", err)
vcl.ThreadSync(func() {
f.statusLabel.SetCaption("â—已断开")
f.statusLabel.Font().SetColor(colors.ClRed)
f.serial = nil
f.connectBtn.SetCaption("连接")
f.reconnT.SetEnabled(true)
})
return
}
}
}
func (f *MainForm) refreshRows() {
u := f.currentUnit()
cur := f.collector.CurrentIndex()
for i, row := range f.rows {
s, _ := f.collector.DisplayValue(i, u)
row.value.SetCaption(s)
row.unit.SetCaption(f.currentUnitLabel())
if i == cur {
row.panel.SetColor(types.TColor(0x00FFE0C0))
} else {
row.panel.SetColor(colors.ClBtnFace)
}
}
sn, seq := f.collector.Serial()
if sn == 0 {
f.bottomLabel.SetCaption("åºåˆ—å? - 末帧: -")
} else {
f.bottomLabel.SetCaption(fmt.Sprintf("åºåˆ—å? %d 末帧 seq=%d %s",
sn, seq, time.Now().Format("15:04:05")))
}
}
func (f *MainForm) onResize(sender vcl.IObject) {
f.relayout()
}
func (f *MainForm) relayout() {
cw := f.ClientWidth()
ch := f.ClientHeight()
if cw <= 0 || ch <= 0 {
return
}
sx := float64(cw) / float64(baseW)
sy := float64(ch) / float64(baseH)
val := clamp(int32(float64(valSize)*sy), minFont, maxFont)
name := clamp(int32(float64(nameSize)*sy), minFont/2, maxFont/2)
unit := clamp(int32(float64(unitSize)*sy), minFont/2, maxFont/2)
gap := int32(float64(gapX) * sx)
if gap < 4 {
gap = 4
}
ptop := int32(108)
pH := int32(float64(panelH) * sy)
if pH < 100 {
pH = 100
}
var pW int32
if len(f.rows) == 1 {
pW = cw - 32
} else {
pW = (cw - gap*(int32(len(f.rows))+1) - 32) / int32(len(f.rows))
}
if pW < 100 {
pW = 100
}
for i, row := range f.rows {
left := int32(16) + int32(i)*(pW+gap)
row.panel.SetLeft(left)
row.panel.SetTop(ptop)
row.panel.SetWidth(pW)
row.panel.SetHeight(pH)
nameH := name + 12
unitH := unit + 12
vH := val + 8
mid := pH / 2
valTop := mid - vH/2
if valTop < 8+nameH {
valTop = 8 + nameH
}
if valTop+vH > pH-8-unitH {
valTop = pH - 8 - unitH - vH
}
row.name.SetLeft(8)
row.name.SetTop(8)
row.name.SetWidth(pW - 16)
row.name.SetHeight(nameH)
row.name.Font().SetSize(name)
row.value.SetLeft(8)
row.value.SetTop(valTop)
row.value.SetWidth(pW - 16)
row.value.SetHeight(vH)
row.value.Font().SetSize(val)
row.unit.SetLeft(8)
row.unit.SetTop(pH - 8 - unitH)
row.unit.SetWidth(pW - 16)
row.unit.SetHeight(unitH)
row.unit.Font().SetSize(unit)
}
btnH := int32(float64(44) * sy)
if btnH < 36 {
btnH = 36
}
btnW := int32(float64(120) * sx)
if btnW < 80 {
btnW = 80
}
btnFont := clamp(int32(float64(14)*sy), 12, 28)
bottomY := ptop + pH + 12
f.bottomLabel.SetLeft(16)
f.bottomLabel.SetTop(bottomY)
f.bottomLabel.SetWidth(cw - 32)
btnTop := ptop + pH + 36
f.workpieceNoEdit.SetLeft(16)
f.workpieceNoEdit.SetTop(btnTop)
f.workpieceNoEdit.SetWidth(int32(float64(180) * sx))
f.workpieceNoEdit.SetHeight(btnH)
f.filterBtn.SetLeft(16 + int32(float64(180)*sx) + 4)
f.filterBtn.SetTop(btnTop)
f.filterBtn.SetWidth(btnH)
f.filterBtn.SetHeight(btnH)
f.exportBtn.SetLeft(cw - 3*(btnW+16) - 16)
f.exportBtn.SetTop(btnTop)
f.exportBtn.SetWidth(btnW)
f.exportBtn.SetHeight(btnH)
f.exportBtn.Font().SetSize(btnFont)
f.clearBtn.SetLeft(cw - 2*(btnW+16) - 16)
f.clearBtn.SetTop(btnTop)
f.clearBtn.SetWidth(btnW)
f.clearBtn.SetHeight(btnH)
f.clearBtn.Font().SetSize(btnFont)
f.submitBtn.SetLeft(cw - btnW - 16)
f.submitBtn.SetTop(btnTop)
f.submitBtn.SetWidth(btnW)
f.submitBtn.SetHeight(btnH)
f.submitBtn.Font().SetSize(btnFont)
histTop := btnTop + btnH + 8
histH := ch - histTop - 8
if histH < 60 {
histH = 60
}
f.histView.SetLeft(16)
f.histView.SetTop(histTop)
f.histView.SetWidth(cw - 32)
f.histView.SetHeight(histH)
histFont := clamp(int32(float64(12)*sy), 10, 20)
f.histView.Font().SetSize(histFont)
var colWidths []int32
if f.currentType == "circle" {
colWidths = []int32{
int32(float64(130) * sx),
int32(float64(130) * sx),
int32(float64(110) * sx),
int32(float64(90) * sx),
int32(float64(120) * sx),
int32(float64(120) * sx),
}
} else {
colWidths = []int32{
int32(float64(130) * sx),
int32(float64(130) * sx),
int32(float64(110) * sx),
int32(float64(90) * sx),
}
}
used := int32(0)
for _, w := range colWidths {
used += w
}
lastW := cw - 32 - used
if lastW < 100 {
lastW = 100
}
for i, w := range append(colWidths, lastW) {
if int32(i) < f.histView.Columns().Count() {
f.histView.Column(int32(i)).SetWidth(w)
}
}
}
func clamp(v, lo, hi int32) int32 {
if v < lo {
return lo
}
if v > hi {
return hi
}
return v
}
func (f *MainForm) OnFormClose(sender vcl.IObject, action *types.TCloseAction) {
r := vcl.MessageDlg("确定è¦é€€å‡ºç¨‹åºå—?", types.MtConfirmation, types.MbYes, types.MbNo)
if r != types.MrYes {
*action = types.CaNone
return
}
if f.serial != nil {
_ = f.serial.Close()
}
}