From 311636c22095b73c51d0c6f1bf73491ee78598d2 Mon Sep 17 00:00:00 2001 From: Witold Olechowski Date: Fri, 6 Mar 2026 16:38:01 +0100 Subject: [PATCH 1/2] z22se info --- pkg/ecu/z22se/info.go | 282 +++++++++++++++++++++++++++++++++++++++++ pkg/ecu/z22se/z22se.go | 6 - 2 files changed, 282 insertions(+), 6 deletions(-) create mode 100644 pkg/ecu/z22se/info.go diff --git a/pkg/ecu/z22se/info.go b/pkg/ecu/z22se/info.go new file mode 100644 index 0000000..44b9139 --- /dev/null +++ b/pkg/ecu/z22se/info.go @@ -0,0 +1,282 @@ +package z22se + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "log" + "strconv" + "strings" + + "github.com/roffe/gocan" + "github.com/roffe/txlogger/pkg/model" +) + +var T8Headers = []model.Header{ + {Desc: "VIN", ID: 0x90, Type: "string"}, + {Desc: "Calibration set ", ID: 0x74, Type: "string"}, + {Desc: "Codefile version", ID: 0x73, Type: "string"}, + {Desc: "ECU description", ID: 0x72, Type: "string"}, + {Desc: "ECU hardware", ID: 0x71, Type: "string"}, + {Desc: "ECU s/w number", ID: 0x95, Type: "hex"}, + {Desc: "Programming date", ID: 0x99, Type: "hex"}, + {Desc: "Build date", ID: 0x0A, Type: "string"}, + {Desc: "Serial number", ID: 0xB4, Type: "string"}, + {Desc: "Software version", ID: 0x08, Type: "string"}, + {Desc: "0F identifier ", ID: 0x0F, Type: "string"}, + {Desc: "SW identifier 1", ID: 0xC1, Type: "string"}, + {Desc: "SW identifier 2", ID: 0xC2, Type: "string"}, + {Desc: "SW identifier 3", ID: 0xC3, Type: "string"}, + {Desc: "SW identifier 4", ID: 0xC4, Type: "string"}, + {Desc: "SW identifier 5", ID: 0xC5, Type: "string"}, + {Desc: "SW identifier 6", ID: 0xC6, Type: "string"}, + {Desc: "Hardware type", ID: 0x97, Type: "string"}, + {Desc: "75 identifier", ID: 0x75, Type: "string"}, + {Desc: "Engine type", ID: 0x0C, Type: "string"}, + {Desc: "Supplier ID", ID: 0x92, Type: "string"}, + {Desc: "Speed limiter", ID: 0x02, Type: "km/h"}, + {Desc: "Oil quality", ID: 0x25, Type: "oilquality"}, + {Desc: "SAAB partnumber", ID: 0x7C, Type: "int32"}, + {Desc: "Diagnostic Data Identifier", ID: 0x9A, Type: "ddi"}, + {Desc: "End model partnumber", ID: 0xCB, Type: "rint32"}, + {Desc: "Base model partnumber", ID: 0xCC, Type: "int32"}, + {Desc: "ManufacturersEnableCounter", ID: 0xA0, Type: "hex"}, + {Desc: "Tester Serial", ID: 0x98, Type: "string"}, + //{Desc: "E85", ID: 0x7A, Type: "e85"}, +} + +func (t *Client) Info(ctx context.Context) ([]model.HeaderResult, error) { + t.cfg.OnProgress(-float64(len(T8Headers))) + t.cfg.OnMessage("Fetching ECU info") + + var out []model.HeaderResult + for i, h := range T8Headers { + switch h.Type { + case "string": + data, err := t.gm.ReadDataByIdentifierString(ctx, h.ID) + if err != nil { + t.cfg.OnError(fmt.Errorf("failed to read %s: %w", h.Desc, err)) + continue + } + res := model.HeaderResult{ + Value: data, + } + res.ID = h.ID + res.Desc = h.Desc + out = append(out, res) + case "int64": + data, err := t.RequestECUInfoAsInt64(ctx, h.ID) + if err != nil { + t.cfg.OnError(fmt.Errorf("failed to read %s: %w", h.Desc, err)) + continue + } + res := model.HeaderResult{ + Value: strconv.Itoa(int(data)), + } + res.ID = h.ID + res.Desc = h.Desc + out = append(out, res) + case "hex": + data, err := t.RequestECUInfo(ctx, h.ID) + if err != nil { + t.cfg.OnError(fmt.Errorf("failed to read %s: %w", h.Desc, err)) + continue + } + res := model.HeaderResult{ + Value: fmt.Sprintf("%X", data), + } + res.ID = h.ID + res.Desc = h.Desc + out = append(out, res) + case "int32": + data, err := t.RequestECUInfoAsInt32(ctx, h.ID) + if err != nil { + t.cfg.OnError(fmt.Errorf("failed to read %s: %w", h.Desc, err)) + continue + } + res := model.HeaderResult{ + Value: strconv.Itoa(int(data)), + } + res.ID = h.ID + res.Desc = h.Desc + out = append(out, res) + case "uint32": + data, err := t.RequestECUInfoAsUint32(ctx, h.ID) + if err != nil { + t.cfg.OnError(fmt.Errorf("failed to read %s: %w", h.Desc, err)) + continue + } + res := model.HeaderResult{ + Value: strconv.Itoa(int(data)), + } + res.ID = h.ID + res.Desc = h.Desc + out = append(out, res) + case "rint32": + resp, err := t.RequestECUInfo(ctx, h.ID) + if err != nil { + log.Println(err) + continue + } + var retval uint32 + if err := binary.Read(bytes.NewReader(resp), binary.LittleEndian, &retval); err != nil { + log.Println(err) + continue + } + res := model.HeaderResult{ + Value: strconv.Itoa(int(retval)), + } + res.ID = h.ID + res.Desc = h.Desc + out = append(out, res) + + case "km/h": + data, err := t.RequestECUInfo(ctx, h.ID) + if err != nil { + t.cfg.OnError(fmt.Errorf("failed to read %s: %w", h.Desc, err)) + continue + } + var retval uint32 + if len(data) == 2 { + retval = uint32(data[0]) * 256 + retval += uint32(data[1]) + retval /= 10 + } + res := model.HeaderResult{ + Value: fmt.Sprintf("%d km/h", retval), + } + res.ID = h.ID + res.Desc = h.Desc + out = append(out, res) + case "oilquality": + data, err := t.RequestECUInfoAsUint32(ctx, h.ID) + if err != nil { + t.cfg.OnError(fmt.Errorf("failed to read %s: %w", h.Desc, err)) + continue + } + quality := float64(data) / 256 + res := model.HeaderResult{ + Value: fmt.Sprintf("%.2f%%", quality), + } + res.ID = h.ID + res.Desc = h.Desc + out = append(out, res) + + case "ddi": + data, err := t.RequestECUInfo(ctx, h.ID) + if err != nil { + t.cfg.OnError(fmt.Errorf("failed to read %s: %w", h.Desc, err)) + continue + } + var retval string + if len(data) == 2 { + retval = fmt.Sprintf("0x%02X 0x%02X", data[0], data[1]) + } + res := model.HeaderResult{ + Value: retval, + } + res.ID = h.ID + res.Desc = h.Desc + out = append(out, res) + case "e85": + data, err := t.gm.ReadDataByPacketIdentifier(ctx, 0x01, 0x7A) + if err != nil && err.Error() != "Request out of range or session dropped" { + t.cfg.OnError(fmt.Errorf("failed to read %s: %w", h.Desc, err)) + continue + } + if len(data) == 2 { + e85 := uint32(data[2]) + res := model.HeaderResult{ + Value: fmt.Sprintf("%d%%", e85), + } + res.ID = h.ID + res.Desc = h.Desc + out = append(out, res) + } else { + res := model.HeaderResult{ + Value: "Not BioPower", + } + res.ID = h.ID + res.Desc = h.Desc + out = append(out, res) + } + } + t.cfg.OnProgress(float64(i + 1)) + } + + return out, nil +} +func (t *Client) RequestECUInfoAsString(ctx context.Context, pid byte) (string, error) { + resp, err := t.RequestECUInfo(ctx, pid) + if err != nil { + return "", err + } + return strings.ReplaceAll(string(resp[:]), "\x00", ""), nil +} + +func (t *Client) RequestECUInfoAsInt32(ctx context.Context, pid byte) (int32, error) { + resp, err := t.RequestECUInfo(ctx, pid) + if err != nil { + return 0, err + } + var retval int32 + if err := binary.Read(bytes.NewReader(resp), binary.BigEndian, &retval); err != nil { + return 0, err + } + return retval, nil +} + +func (t *Client) RequestECUInfoAsUint32(ctx context.Context, pid byte) (uint32, error) { + resp, err := t.RequestECUInfo(ctx, pid) + if err != nil { + return 0, err + } + var retval uint32 + if err := binary.Read(bytes.NewReader(resp), binary.BigEndian, &retval); err != nil { + return 0, err + } + return retval, nil +} + +func (t *Client) RequestECUInfoAsInt64(ctx context.Context, pid byte) (int64, error) { + resp, err := t.RequestECUInfo(ctx, pid) + if err != nil { + return 0, err + } + if len(resp) != 8 { + return 0, fmt.Errorf("invalid response length for int64: %d", len(resp)) + } + + var retval int64 + if err := binary.Read(bytes.NewReader(resp), binary.BigEndian, &retval); err != nil { + return 0, err + } + return retval, nil +} + +func (t *Client) RequestECUInfoAsUint64(ctx context.Context, pid byte) (uint64, error) { + resp, err := t.RequestECUInfo(ctx, pid) + if err != nil { + return 0, err + } + if len(resp) != 8 { + return 0, fmt.Errorf("invalid response length for uint64: %d", len(resp)) + } + + var retval uint64 + if err := binary.Read(bytes.NewReader(resp), binary.BigEndian, &retval); err != nil { + return 0, err + } + return retval, nil +} + +func (t *Client) RequestECUInfo(ctx context.Context, pid byte) ([]byte, error) { + return t.gm.ReadDataByIdentifier(ctx, pid) +} + +func (t *Client) SendAckMessageT8() { + if err := t.c.Send(0x7E0, []byte{0x30}, gocan.Outgoing); err != nil { + panic(err) + } +} diff --git a/pkg/ecu/z22se/z22se.go b/pkg/ecu/z22se/z22se.go index ff512d8..afbcbde 100644 --- a/pkg/ecu/z22se/z22se.go +++ b/pkg/ecu/z22se/z22se.go @@ -17,7 +17,6 @@ import ( "github.com/roffe/txlogger/pkg/ecu" "github.com/roffe/txlogger/pkg/ecu/t8legion" "github.com/roffe/txlogger/pkg/ecu/t8sec" - "github.com/roffe/txlogger/pkg/model" ) func init() { @@ -37,11 +36,6 @@ type Client struct { cfg *ecu.Config } -// Info implements [ecu.Client]. -func (t *Client) Info(context.Context) ([]model.HeaderResult, error) { - return nil, nil -} - // ReadDTC implements [ecu.Client]. func (t *Client) ReadDTC(context.Context) ([]dtc.DTC, error) { return nil, nil From 43b2e656cb2c68129262fbcf1b3af2071945baec Mon Sep 17 00:00:00 2001 From: Witold Olechowski Date: Mon, 9 Mar 2026 19:09:28 +0100 Subject: [PATCH 2/2] falback dialog when dbus fails --- go.mod | 2 ++ go.sum | 4 ++++ pkg/native/dialog_linux.go | 19 ++++++++++++++++--- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f21da37..0460032 100644 --- a/go.mod +++ b/go.mod @@ -40,6 +40,7 @@ require ( fyne.io/systray v1.12.0 // indirect github.com/BurntSushi/toml v1.6.0 // indirect github.com/FyshOS/fancyfs v0.0.1 // indirect + github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf // indirect github.com/albenik/bcd v0.0.0-20170831201648-635201416bc7 // indirect github.com/bendikro/dl v0.0.0-20190410215913-e41fdb9069d4 // indirect github.com/creack/goselect v0.1.3 // indirect @@ -73,6 +74,7 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rymdport/portal v0.4.2 // indirect + github.com/sqweek/dialog v0.0.0-20260123140253-64c163d53aac // indirect github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect github.com/stretchr/testify v1.11.1 // indirect diff --git a/go.sum b/go.sum index eee924e..2d9c101 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ github.com/FyshOS/fancyfs v0.0.1 h1:kgvm7VvwOMLkYTqSflplp62SlMVWQ2uAoHw9CXwXHYg= github.com/FyshOS/fancyfs v0.0.1/go.mod h1:S5SHVz/5R72iCXOxCqdcyTPSlg3JxNd0gaHyGBSrY8A= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf h1:FPsprx82rdrX2jiKyS17BH6IrTmUBYqZa/CXT4uvb+I= +github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf/go.mod h1:peYoMncQljjNS6tZwI9WVyQB3qZS6u79/N3mBOcnd3I= github.com/albenik/bcd v0.0.0-20170831201648-635201416bc7 h1:m3Ayfs5OcAlIMEdLIQKubBsVLGee4YMUr14+d1256WE= github.com/albenik/bcd v0.0.0-20170831201648-635201416bc7/go.mod h1:QIAMbrwsnQZ2ES3G26RubSrDB5SPyzsp9Hts5NJdTrI= github.com/avast/retry-go/v4 v4.7.0 h1:yjDs35SlGvKwRNSykujfjdMxMhMQQM0TnIjJaHB+Zio= @@ -125,6 +127,8 @@ github.com/rymdport/portal v0.4.2 h1:7jKRSemwlTyVHHrTGgQg7gmNPJs88xkbKcIL3NlcmSU github.com/rymdport/portal v0.4.2/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4= github.com/samuelbrian/can-go v0.0.2 h1:M2B5j9O97lCGlsUYTWYFBwdhHWvA9HpW+A1wfDukRN4= github.com/samuelbrian/can-go v0.0.2/go.mod h1:a1aqkRYR3BBP3u9uJvvZQjn//TtH5MnlMsAzbR9IQvM= +github.com/sqweek/dialog v0.0.0-20260123140253-64c163d53aac h1:/QqP+ajFMma4hNWQyBDVaQQhz9Z1kDyXScNWMO3owx0= +github.com/sqweek/dialog v0.0.0-20260123140253-64c163d53aac/go.mod h1:/qNPSY91qTz/8TgHEMioAUc6q7+3SOybeKczHMXFcXw= github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE= github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q= github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ= diff --git a/pkg/native/dialog_linux.go b/pkg/native/dialog_linux.go index 79117ec..89b3de9 100644 --- a/pkg/native/dialog_linux.go +++ b/pkg/native/dialog_linux.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/godbus/dbus/v5" + "github.com/sqweek/dialog" ) // GenerateDBusToken generates a random D-Bus authentication token @@ -115,11 +116,15 @@ func OpenFileDialog(title string, filters ...FileFilter) (string, error) { if len(filters) > 0 { options["filters"] = buildPortalFilters(filters) } - return portalCall( + filename, err := portalCall( "org.freedesktop.portal.FileChooser.OpenFile", options, title, ) + if err == nil { + return filename, err + } + return dialog.File().Title("Open File").Filter("bin file", "bin").Load() } func OpenFolderDialog(title string) (string, error) { @@ -128,11 +133,15 @@ func OpenFolderDialog(title string) (string, error) { "multiple": dbus.MakeVariant(false), "directory": dbus.MakeVariant(true), } - return portalCall( + filename, err := portalCall( "org.freedesktop.portal.FileChooser.OpenFile", options, title, ) + if err == nil { + return filename, err + } + return dialog.File().Title("Open folder").Load() } func SaveFileDialog(title string, defaultExt string, filters ...FileFilter) (string, error) { @@ -192,9 +201,13 @@ func SaveFileDialog(title string, defaultExt string, filters ...FileFilter) (str // If filters is non-empty but no match found, skip current_filter entirely // to avoid the portal rejecting the call. } - return portalCall( + filename, err := portalCall( "org.freedesktop.portal.FileChooser.SaveFile", options, title, ) + if err == nil { + return filename, err + } + return dialog.File().Title("Save File").Filter("bin file", "bin").Save() }