package handlers import ( "context" "fmt" "net/http" "strconv" "strings" "time" "github.com/fiskerinc/cloud-services/pkg/common" orm "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/fiskerinc/cloud-services/pkg/mongo" "github.com/fiskerinc/cloud-services/pkg/utils" "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/julienschmidt/httprouter" "github.com/pkg/errors" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" "otaupdate/services" "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/fiskerinc/cloud-services/pkg/loggerdataresp" ) // HandleECUDTCGet godoc // @Summary Get ECU DTCs for a specific vehicle // @Description Get ECU diagnostic trouble codes (DTCs) for a specific vehicle within a given time range // @Tags ECU // @Accept json // @Produce json // @Param Authorization header string false "Bearer " // @Param Api-Key header string false "" // @Param vin path string true "VIN" // @Param ecu query string false "ECU" // @Param trouble_code query string false "Trouble Code" // @Param start_time query string false "Start time (RFC3339 format)" // @Param end_time query string false "End time (RFC3339 format)" // @Param limit query int false "Max number of records" // @Param offset query int false "Records offset" // @Param order query string false "Sort on column with asc or desc" // @Param decode query bool false "Return decoded dtc information" // @Success 200 {object} common.JSONDBQueryResult{data=[]common.DTC_ECU} "List of DTC ECU data" // @Failure 400 {object} common.JSONError "Bad request" // @Failure 401 {object} common.JSONError "Unauthorized" // @Failure 404 {object} common.JSONError "Not found" // @Failure 503 {object} common.JSONError "Service unavailable" // @Router /dtcs/{vin} [get] func HandleECUDTCGet(w http.ResponseWriter, r *http.Request) { params := httprouter.ParamsFromContext(r.Context()) vin := params.ByName("vin") queryParams := r.URL.Query() ecu := queryParams.Get("ecu") troubleCode := queryParams.Get("trouble_code") startStr := queryParams.Get("start_time") endStr := queryParams.Get("end_time") decode, _ := strconv.ParseBool(queryParams.Get("decode")) filter := bson.M{ "vin": vin} if ecu != "" { filter["ecu"] = ecu } if troubleCode != "" { troubleCodeInt, err := strconv.ParseInt(troubleCode, 10, 64) if err != nil { http.Error(w, "Invalid trouble_code format, use int64", http.StatusBadRequest) return } filter["dtc"] = troubleCodeInt } err := validator.GetValidator().Var(vin, "vin|vinsuffix") if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) { return } var start time.Time if startStr != "" { start, err = time.Parse(time.RFC3339, startStr) if err != nil { http.Error(w, "Invalid start_time format, use RFC3339 format", http.StatusBadRequest) return } } var end time.Time if endStr != "" { end, err = time.Parse(time.RFC3339, endStr) if err != nil { http.Error(w, "Invalid end_time format, use RFC3339 format", http.StatusBadRequest) return } } if !start.IsZero() && !end.IsZero() { filter["created_at"] = bson.M{ "$gte": start, "$lte": end, } } else if !start.IsZero() { filter["created_at"] = bson.M{ "$gte": start, } } else if !end.IsZero() { filter["created_at"] = bson.M{ "$lte": end, } } mongoOpts := options.Find() query_params, err := orm.ParsePageQuery(r) if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) { return } mongoOpts.SetLimit(int64(query_params.Limit)) if query_params.Order != "" { mongoOpts.SetSort(bson.D{ {"created_at", -1}}) // Descending order for 'created_at'. } if query_params.Offset != 0 { mongoOpts.SetSkip(int64(query_params.Offset)) } mongo, err := services.GetMongoClient() if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) { return } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() var total int64 if query_params.Offset == 0 { total, err = mongo.Collection("dtcs").CountDocuments(ctx, filter) if loggerdataresp.BadDataErrorResp(w, err, http.StatusServiceUnavailable, loggerdataresp.PostgresNoRowsErrorCheck) { return } } cursor, err := mongo.Collection("dtcs").Find(ctx, filter, mongoOpts) if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) { return } var dtcs []common.DTC_ECU for cursor.Next(ctx) { var result common.DTC_ECU err := cursor.Decode(&result) if err != nil { logger.Warn().Msg(err.Error()) continue } if decode { fetchDTCDataFromMongo(&result) } dtcs = append(dtcs, result) } utils.RespJSON(w, http.StatusOK, common.JSONDBQueryResult{ Data: dtcs, Total: int(total), }) } func fetchDTCDataFromMongo(dtc *common.DTC_ECU) (err error) { client, err := mongo.GetPDXMongoClient() if err != nil { err = errors.WithStack(err) return } // Handle the dtc string, need to drop the first byte as its the status code troubleCodeHex := fmt.Sprintf("%X", dtc.TroubleCode) troubleCodeHex = strings.ToUpper(troubleCodeHex) info, err := client.GetDTCDefinitionByHexString(troubleCodeHex, dtc.ECU) if err != nil { return } if info == nil { logger.Warn().Msgf("Failed to find dtc code from ecu: %s troubleCodeHex: %s", dtc.ECU, troubleCodeHex) } dtc.Information = info dtc.StatusByteDecode = dtc.DTCStatusByteMeaning() return }