aboutsummaryrefslogtreecommitdiffstats
path: root/sw/cal/main.go
blob: bfd0f117096b696a2f1889d8dd9207ee3dfb5355 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package main

import (
	"flag"
	"fmt"
	"os"

	"go.einride.tech/can/pkg/dbc"
	"go.einride.tech/can/pkg/socketcan"
)

type Signals struct {
	tach, speed, an1, an2, an3, an4 *dbc.SignalDef
}

// Flags
var (
	// DBC file
	dbcFilenameFlag = "dbc"
	dbcFilename     = flag.String(dbcFilenameFlag, "", "DBC file name")

	// SocketCAN device
	canDevFlag = "can"
	canDev     = flag.String(canDevFlag, "can0", "SocketCAN device")

	// Signal names
	tachSigFlag  = "tachSig"
	speedSigFlag = "speedSig"
	an1SigFlag   = "an1Sig"
	an2SigFlag   = "an2Sig"
	an3SigFlag   = "an3Sig"
	an4SigFlag   = "an4Sig"
	tachSig      = flag.String(tachSigFlag, "", "tachometer signal name")
	speedSig     = flag.String(speedSigFlag, "", "speedometer signal name")
	an1Sig       = flag.String(an1SigFlag, "", "analog channel 1 signal name")
	an2Sig       = flag.String(an2SigFlag, "", "analog channel 2 signal name")
	an3Sig       = flag.String(an3SigFlag, "", "analog channel 3 signal name")
	an4Sig       = flag.String(an4SigFlag, "", "analog channel 4 signal name")

	// Calibration tables
	tachTblFlag  = "tachTbl"
	speedTblFlag = "speedTbl"
	an1TblFlag   = "an1Tbl"
	an2TblFlag   = "an2Tbl"
	an3TblFlag   = "an3Tbl"
	an4TblFlag   = "an4Tbl"
	tachTbl      = flag.String(tachTblFlag, "", "tachometer calibration CSV file")
	speedTbl     = flag.String(speedTblFlag, "", "speedometer calibration CSV file")
	an1Tbl       = flag.String(an1TblFlag, "", "analog channel 1 calibration CSV file")
	an2Tbl       = flag.String(an2TblFlag, "", "analog channel 2 calibration CSV file")
	an3Tbl       = flag.String(an3TblFlag, "", "analog channel 3 calibration CSV file")
	an4Tbl       = flag.String(an4TblFlag, "", "analog channel 4 calibration CSV file")
)

func main() {
	// Parse command line args
	flag.Parse()
	if *dbcFilename == "" {
		weprintf("Missing '%s' flag.\n", dbcFilenameFlag)
		flag.Usage()
		os.Exit(1)
	}
	if err := checkTablesProvided(); err != nil {
		eprintf("%v\n", err)
	}
	sigNames := nonEmpty(*tachSig, *speedSig, *an1Sig, *an2Sig, *an3Sig, *an4Sig)
	tblFilenames := nonEmpty(*tachTbl, *speedTbl, *an1Tbl, *an2Tbl, *an3Tbl, *an4Tbl)

	// Open CAN connection
	fmt.Println("Opening connection to", *canDev)
	conn, err := socketcan.Dial("can", *canDev)
	if err != nil {
		eprintf("%v\n", err)
	}
	defer conn.Close()
	tx := socketcan.NewTransmitter(conn)
	defer tx.Close()

	// Parse DBC file and transmit encoding of each signal
	if err := sendEncodings(*dbcFilename, sigNames, tx); err != nil {
		eprintf("%v\n", err)
	}

	// Parse tables and transmit them
	if err := sendTables(tblFilenames, tx); err != nil {
		eprintf("%v\n", err)
	}
}

// Return a map of non-empty strings keyed by their index in the given list.
func nonEmpty(ss ...string) map[int]string {
	m := make(map[int]string)
	for i := range ss {
		if ss[i] != "" {
			m[i] = ss[i]
		}
	}
	return m
}

// Check that the user provided a corresponding table for each signal they gave.
func checkTablesProvided() error {
	signals := []string{*tachSig, *speedSig, *an1Sig, *an2Sig, *an3Sig, *an4Sig}
	tables := []string{*tachTbl, *speedTbl, *an1Tbl, *an2Tbl, *an3Tbl, *an4Tbl}
	tableFlags := []string{tachTblFlag, speedTblFlag, an1TblFlag, an2TblFlag, an3TblFlag, an4TblFlag}
	for i := range signals {
		if signals[i] != "" && tables[i] == "" {
			// No corresponding table
			return fmt.Errorf("Missing flag -%s", tableFlags[i])
		}
	}
	return nil // all present signals have a matching table
}

// Parse DBC file and transmit encoding of each signal using Signal Control frames.
func sendEncodings(dbcFilename string, sigNames map[int]string, tx *socketcan.Transmitter) error {
	// Parse DBC file
	fmt.Println("Parsing", dbcFilename)
	sigDefs, err := parseSignals(dbcFilename, sigNames)
	if err != nil {
		return err
	}

	// Transmit Signal Control frames
	for k, sigDef := range sigDefs {
		fmt.Printf("Transmitting encoding of signal %d: %s\n", k, sigDef.name)
		fmt.Println(sigDef)
		if err := sendEncoding(sigDef, k, tx); err != nil {
			return err
		}
	}

	return nil
}

// Parse each table and transmit them using Table Control frames.
func sendTables(tblFilenames map[int]string, tx *socketcan.Transmitter) error {
	for k, filename := range tblFilenames {
		fmt.Println("Parsing", filename)
		tbl, err := parseTable(filename)
		if err != nil {
			return err
		}

		fmt.Printf("Transmitting table %d: %s\n", k, filename)
		if err := sendTable(tx, tbl, k); err != nil {
			return err
		}
	}
	return nil
}