-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmotorsdb.go
More file actions
242 lines (225 loc) · 6.99 KB
/
Copy pathmotorsdb.go
File metadata and controls
242 lines (225 loc) · 6.99 KB
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
package main
import (
"bytes"
"database/sql"
"errors"
"fmt"
"log"
"path"
"regexp"
"strings"
"text/template"
srvConfig "github.com/CHESSComputing/golib/config"
sqldb "github.com/CHESSComputing/golib/sqldb"
)
// var MotorsDb is our database pointer
var MotorsDb *sql.DB
type MotorRecord struct {
ScanId string
Motors map[string]float64
}
type MotorPositionQuery struct {
Mne string
Exact []float64
Min float64
Max float64
}
type MotorsDbQuery struct {
Sids []string
MotorPositionQueries []MotorPositionQuery
}
func InitMotorsDb() {
dbtype, dburi, dbowner := sqldb.ParseDBFile(srvConfig.Config.SpecScans.DBFile)
log.Printf("InitDB: type=%s owner=%s", dbtype, dbowner)
db, err := sqldb.InitDB(dbtype, dburi)
if err != nil {
log.Fatal(err)
}
MotorsDb = db
}
func InsertMotors(r MotorRecord, db *sql.DB) (int64, error) {
tx, err := db.Begin()
if err != nil {
return -1, fmt.Errorf("[SpecScansService.main.InsertMotors] db.Begin error: %w", err)
}
defer tx.Rollback()
// Insert the given motor record to the three tables that compose the static
// motor positions database.
log.Printf("Inserting motor record: %v", r)
result, err := tx.Exec("INSERT INTO ScanIds (sid) VALUES (?)", r.ScanId)
if err != nil {
log.Printf("Could not insert record to ScanIds table; error: %v", err)
return -1, fmt.Errorf("[SpecScansService.main.InsertMotors] tx.Exec error: %w", err)
}
scan_id, err := result.LastInsertId()
if err != nil {
log.Printf("Could not get ID of new record in ScanIds; error: %v", err)
return scan_id, fmt.Errorf("[SpecScansService.main.InsertMotors] result.LastInsertId error: %w", err)
}
var motor_id int64
for mne, pos := range r.Motors {
result, err = tx.Exec("INSERT INTO MotorMnes (motor_mne) VALUES (?)", mne)
if err != nil {
err = tx.QueryRow("SELECT motor_id FROM MotorMnes WHERE motor_mne=?", mne).Scan(&motor_id)
if err != nil {
log.Printf("Could not insert record to MotorMnes table; error: %v", err)
continue
}
} else {
motor_id, err = result.LastInsertId()
}
if err != nil {
log.Printf("Could not get ID of new record in MotorMnes; error: %v", err)
continue
}
result, err = tx.Exec("INSERT INTO MotorPositions (scan_id, motor_id, motor_position) VALUES (?, ?, ?)", scan_id, motor_id, pos)
if err != nil {
log.Printf("Could not insert record to MotorPositions table; error: %v", err)
}
}
err = tx.Commit()
if err != nil {
return scan_id, fmt.Errorf("[SpecsScanService.main.InsertMotors] tx.Commit error: %w", err)
}
return scan_id, nil
}
func QueryMotorPosition(mne string, pos float64) []MotorRecord {
query := MotorsDbQuery{
MotorPositionQueries: []MotorPositionQuery{
MotorPositionQuery{
Mne: mne,
Exact: []float64{pos},
},
},
}
return queryMotorsDb(query)
}
func GetMotorRecords(sids ...string) ([]MotorRecord, error) {
query := MotorsDbQuery{Sids: sids}
return queryMotorsDb(query), nil
}
func QueryMotorsDb(query map[string]any) []MotorRecord {
motorsdb_query := translateQuery(query)
if Verbose > 0 {
log.Printf("motorsdb_query: %+v\n", motorsdb_query)
}
return queryMotorsDb(motorsdb_query)
}
func translateQuery(query map[string]any) MotorsDbQuery {
var motorsdb_query MotorsDbQuery
// Consolidate values from user query keys like "motors" and "motors.*" so
// that we have a query map where keys are motor names only.
var position_queries []map[string]any
for key, val := range query {
if key == "motors" {
for _key, _val := range val.(map[string]any) {
position_queries = append(position_queries, map[string]any{_key: _val})
}
} else {
queryKey := strings.TrimPrefix(key, "motors.")
position_queries = append(position_queries, map[string]any{queryKey: val})
}
}
for _, v := range position_queries {
motorsdb_query.MotorPositionQueries = append(motorsdb_query.MotorPositionQueries, translatePositionQuery(v))
}
return motorsdb_query
}
func translatePositionQuery(query any) MotorPositionQuery {
var position_query MotorPositionQuery
switch query.(type) {
case string:
position_query.Mne = query.(string)
case map[string]any:
for k, v := range query.(map[string]any) {
position_query.Mne = k
switch v.(type) {
case float64, float32:
position_query.Exact = []float64{v.(float64)}
case []any:
for _, pos := range v.([]any) {
position_query.Exact = append(position_query.Exact, pos.(float64))
}
// log.Printf("value is []any: %v (%T)", v, v)
// position_query.Exact = v.([]float64)
case map[string]any:
for kk, vv := range v.(map[string]any) {
if kk == "$lt" {
position_query.Max = vv.(float64)
} else if kk == "$gt" {
position_query.Min = vv.(float64)
} else if kk == "$in" {
for _, pos := range vv.([]any) {
position_query.Exact = append(position_query.Exact, pos.(float64))
}
} else if kk == "$eq" {
position_query.Exact = []float64{vv.(float64)}
}
}
}
}
}
return position_query
}
func queryMotorsDb(query MotorsDbQuery) []MotorRecord {
var motor_records []MotorRecord
statement, err := getSqlStatement("query_motorsdb.sql", query)
if err != nil {
log.Printf("Could not get appropriate SQL query statement; error: %v", err)
return motor_records
}
if Verbose > 1 {
log.Printf("Motors db query SQL statement: %s", statement)
}
rows, err := MotorsDb.Query(statement)
if err != nil {
log.Printf("Could not query motor positions database; error: %v", err)
return motor_records
}
return parseMotorRecords(rows)
}
func parseMotorRecords(rows *sql.Rows) []MotorRecord {
// Helper for parsing non-grouped results of sql query
var motor_records []MotorRecord
record_map := make(map[string]map[string]float64) // map of scan ids: map of motor mne: position
var sid string
var motor_mne string
var motor_pos float64
for rows.Next() {
err := rows.Scan(&sid, &motor_mne, &motor_pos)
if err != nil {
log.Printf("Could not parse row of results: %v\n", err)
continue
}
_, ok := record_map[sid]
if !ok {
record_map[sid] = make(map[string]float64)
}
record_map[sid][motor_mne] = motor_pos
}
for sid := range record_map {
motor_records = append(motor_records, MotorRecord{ScanId: sid, Motors: record_map[sid]})
}
return motor_records
}
func getSqlStatement(tmpl_file string, params MotorsDbQuery) (string, error) {
tmpl, err := template.New(tmpl_file).ParseFiles(path.Join(srvConfig.Config.SpecScans.WebServer.StaticDir, tmpl_file))
if err != nil {
return "", fmt.Errorf("[SpecScansService.main.getSqlStatement] template.New error: %w", err)
}
statement := ""
buf := bytes.NewBufferString(statement)
err = tmpl.Execute(buf, params)
if err != nil {
return "", fmt.Errorf("[SpecScansService.main.getSqlStatement] tmpl.Execute error: %w", err)
}
statement = buf.String()
if statement == "" {
return "", errors.New("Statement is empty")
}
// Trim whitespace for certain sql dirver(s)
statement = strings.TrimSpace(statement)
re := regexp.MustCompile(`\s+`)
statement = string(re.ReplaceAll([]byte(statement), []byte(" ")))
return statement, nil
}