diff --git a/README.md b/README.md index 961de94..feb5460 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,85 @@ -# cloud-services +# Cloud Services -Go microservices for the vehicle cloud platform. - -## Quick Start -```bash -# Install devbox (if needed) -curl -fsSL https://get.jetify.com/devbox | bash - -# Enter dev environment -devbox shell - -# Run a service locally -cd services/gateway -go run . -``` +Refactored cloud microservices from project-ai. ## Structure + ``` -services/ # Individual Go microservices -shared/ # Shared Go modules -deploy/ # Kubernetes manifests (kustomize) - base/ # Base configs - overlays/ # Environment-specific (development, etc.) +cloud-services/ +├── pkg/ # Shared Go packages +│ ├── kafka/ # Pure Go Kafka client (franz-go) +│ ├── dbc/ # CAN database signal definitions +│ ├── can-go/ # CAN protocol library +│ └── ... # Other shared modules +├── services/ +│ └── gateway/ # API gateway service +├── deploy/ +│ ├── base/ # Base k8s manifests +│ └── overlays/ # Environment-specific configs +└── scripts/ # Build and utility scripts +``` + +## Quick Start + +```bash +# Build all +go build ./... + +# Build gateway +go build ./services/gateway + +# Run tests +go test ./... + +# Build Docker image +docker build -t gateway -f services/gateway/Dockerfile . ``` ## Services -| Service | Description | -|---------|-------------| -| gateway | API gateway, routes requests | -| auth | Authentication (Keycloak integration) | -| ota | OTA update management | -| depot | Vehicle registration & management | -| attendant | Event processing | -| cargo | Data ingestion to storage | -| ditto | Digital twin state | -| manufacture | Manufacturing integration | -| aftersales | Aftersales/diagnostic services | -## Local Development -Services connect to: -- PostgreSQL: `cloud-dev-rw.cnpg-system.svc:5432` -- MongoDB: `cloud-dev-svc.mongodb.svc:27017` -- Redis: `cloud-dev.redis.svc:6379` -- Kafka: `cloud-dev-kafka-bootstrap.kafka.svc:9092` -- Keycloak: `https://keycloak.mini.cloud.fiskerinc.com` +### Gateway +WebSocket gateway for TRex, HMI, and Mobile connections. Handles auth, message routing to Kafka. + +- Port 8077: HTTP/WebSocket +- Port 11011: Health check + +## Development + +### Prerequisites +- Go 1.25+ +- Docker (for container builds) + +### Module Structure +Uses Go workspaces (`go.work`) for local development: +- `./pkg` - shared packages +- `./pkg/can-go` - CAN protocol library +- `./services/gateway` - gateway service + +### Generating DBC Code +CAN signal definitions are generated from DBC files. See `pkg/dbc/README.md`. + +```bash +./scripts/generate-dbc.sh /path/to/dbc/files +``` ## Deployment -ArgoCD syncs from this repo. Push to main → auto-deploy to mini cluster. + +Kubernetes manifests in `deploy/` use Kustomize overlays: + +```bash +# Development +kubectl apply -k deploy/overlays/development + +# Or via ArgoCD +# See k8s-gitops-setup repo +``` + +## Environment Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `KAFKA_HOSTS` | `localhost:9092` | Kafka brokers | +| `REDIS_HOST` | `localhost` | Redis host | +| `REDIS_PORT` | `6379` | Redis port | +| `JWK_URL` | - | JWKS endpoint for JWT validation | +| `LOG_LEVEL` | `info` | Log level | diff --git a/deploy/base/configmap-common.yaml b/deploy/base/configmap-common.yaml index 1f8fc12..5f52ee0 100644 --- a/deploy/base/configmap-common.yaml +++ b/deploy/base/configmap-common.yaml @@ -23,6 +23,7 @@ data: # Redis REDIS_HOST: cloud-dev.redis.svc.cluster.local REDIS_PORT: "6379" + REDIS_PASSWORD: "" REDIS_IDLETIMEOUT_MS: "3600000" REDIS_MAXIDLECONN: "10" REDIS_MAXACTIVECONN: "10" @@ -38,6 +39,7 @@ data: # Auth (Keycloak) OIDC_ISSUER: https://keycloak.mini.cloud.fiskerinc.com/realms/compute-auth OIDC_JWK_URL: https://keycloak.mini.cloud.fiskerinc.com/realms/compute-auth/protocol/openid-connect/certs + JWK_URL: https://keycloak.mini.cloud.fiskerinc.com/realms/compute-auth/protocol/openid-connect/certs # Vault VAULT_URL: http://vault.vault.svc.cluster.local:8200/v1 diff --git a/deploy/overlays/development/kustomization.yaml b/deploy/overlays/development/kustomization.yaml index d32ca0c..6e87278 100644 --- a/deploy/overlays/development/kustomization.yaml +++ b/deploy/overlays/development/kustomization.yaml @@ -6,9 +6,7 @@ namespace: cloud-services resources: - ../../base - secrets.yaml - # Services (uncomment as migrated) - # - services/gateway/ - # - services/auth/ + - services/gateway/ commonLabels: environment: development diff --git a/deploy/overlays/development/services/gateway/deployment.yaml b/deploy/overlays/development/services/gateway/deployment.yaml new file mode 100644 index 0000000..31394e7 --- /dev/null +++ b/deploy/overlays/development/services/gateway/deployment.yaml @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gateway + namespace: cloud-services + labels: + app: gateway +spec: + replicas: 1 + selector: + matchLabels: + app: gateway + template: + metadata: + labels: + app: gateway + spec: + containers: + - name: gateway + image: localhost:32000/gateway:latest + imagePullPolicy: Always + ports: + - containerPort: 8077 + name: http + - containerPort: 11011 + name: health + envFrom: + - configMapRef: + name: cloud-common-config + - secretRef: + name: cloud-db-credentials + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + memory: 256Mi + livenessProbe: + httpGet: + path: /liveness + port: 11011 + initialDelaySeconds: 10 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /readiness + port: 11011 + initialDelaySeconds: 5 + periodSeconds: 10 +--- +apiVersion: v1 +kind: Service +metadata: + name: gateway + namespace: cloud-services +spec: + selector: + app: gateway + ports: + - port: 8077 + targetPort: 8077 + name: http diff --git a/deploy/overlays/development/services/gateway/kustomization.yaml b/deploy/overlays/development/services/gateway/kustomization.yaml new file mode 100644 index 0000000..88a04b5 --- /dev/null +++ b/deploy/overlays/development/services/gateway/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - deployment.yaml diff --git a/go.work b/go.work index 7c8d6da..a4bae62 100644 --- a/go.work +++ b/go.work @@ -1,6 +1,7 @@ -go 1.24 +go 1.25 use ( ./pkg + ./pkg/can-go ./services/gateway ) diff --git a/pkg/adminroles/roles.go b/pkg/adminroles/roles.go index 7cc5272..f66a833 100644 --- a/pkg/adminroles/roles.go +++ b/pkg/adminroles/roles.go @@ -1,6 +1,6 @@ package adminroles -import "fiskerinc.com/modules/utils/envtool" +import "github.com/fiskerinc/cloud-services/pkg/utils/envtool" // RoleID for groups type RoleID string diff --git a/pkg/adminroles/roles_checker.go b/pkg/adminroles/roles_checker.go index 2125374..98aae82 100644 --- a/pkg/adminroles/roles_checker.go +++ b/pkg/adminroles/roles_checker.go @@ -3,7 +3,7 @@ package adminroles import ( "strings" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/pkg/errors" ) diff --git a/pkg/adminroles/roles_checker_test.go b/pkg/adminroles/roles_checker_test.go index 842f5d2..249a558 100644 --- a/pkg/adminroles/roles_checker_test.go +++ b/pkg/adminroles/roles_checker_test.go @@ -3,8 +3,8 @@ package adminroles_test import ( "testing" - "fiskerinc.com/modules/adminroles" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/adminroles" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) const testRole = "7bcdcdb2-3279-44bf-a998-771bab4b33e1" diff --git a/pkg/americanlease/americanLeaseFilter_test.go b/pkg/americanlease/americanLeaseFilter_test.go index d585e80..98faf49 100644 --- a/pkg/americanlease/americanLeaseFilter_test.go +++ b/pkg/americanlease/americanLeaseFilter_test.go @@ -3,8 +3,8 @@ package americanlease import ( "testing" - "fiskerinc.com/modules/validator" - "fiskerinc.com/modules/vindecoder" + "github.com/fiskerinc/cloud-services/pkg/validator" + "github.com/fiskerinc/cloud-services/pkg/vindecoder" ) diff --git a/pkg/auth/get_users_list.go b/pkg/auth/get_users_list.go index fe87e22..4bb5ebe 100644 --- a/pkg/auth/get_users_list.go +++ b/pkg/auth/get_users_list.go @@ -4,9 +4,9 @@ import ( "strings" "sync" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" diff --git a/pkg/auth/user.go b/pkg/auth/user.go index e7421b2..fe0113d 100644 --- a/pkg/auth/user.go +++ b/pkg/auth/user.go @@ -7,14 +7,14 @@ import ( "net/http" "strings" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" - "fiskerinc.com/modules/httpclient" - "fiskerinc.com/modules/jwt" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" - "fiskerinc.com/modules/utils" + "github.com/fiskerinc/cloud-services/pkg/httpclient" + "github.com/fiskerinc/cloud-services/pkg/jwt" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/utils" ) var getUserURL string = envtool.GetEnv("AUTH_GET_USER", "https://dev-auth.fiskerdps.com/auth/me") diff --git a/pkg/auth/user_consent_token.go b/pkg/auth/user_consent_token.go index 2a414af..c950b6a 100644 --- a/pkg/auth/user_consent_token.go +++ b/pkg/auth/user_consent_token.go @@ -10,10 +10,10 @@ import ( "sync" "time" - "fiskerinc.com/modules/httpclient" - "fiskerinc.com/modules/jwt" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/httpclient" + "github.com/fiskerinc/cloud-services/pkg/jwt" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/pkg/errors" ) diff --git a/pkg/auth/user_consent_token_test.go b/pkg/auth/user_consent_token_test.go index 47467fc..a43e31a 100644 --- a/pkg/auth/user_consent_token_test.go +++ b/pkg/auth/user_consent_token_test.go @@ -6,10 +6,10 @@ import ( "net/http" "testing" - auth "fiskerinc.com/modules/auth" - "fiskerinc.com/modules/httpclient" - "fiskerinc.com/modules/httpclient/mock" - "fiskerinc.com/modules/testhelper" + auth "github.com/fiskerinc/cloud-services/pkg/auth" + "github.com/fiskerinc/cloud-services/pkg/httpclient" + "github.com/fiskerinc/cloud-services/pkg/httpclient/mock" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) const responseUserConsentJSON = `{"access_token":"eyJraWQiOiJqSXowUVRjc0tDVCtoeEd6MlMwK0NoUHlON3c4cmlQXC9sNm1xekFYUmw2bz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIyZGQ2ZmVkOS1lNTgyLTQ1MWItYTkzYi01Yjk0MTBkZmJjNDMiLCJjb2duaXRvOmdyb3VwcyI6WyJ1cy13ZXN0LTJfQVd3akxYeW0yX0F6dXJlQUQiXSwidG9rZW5fdXNlIjoiYWNjZXNzIiwic2NvcGUiOiJodHRwczpcL1wvZmlza2VyaW5jLmNvbVwvb3RhdXBkYXRlLnJlYWQgaHR0cHM6XC9cL2Zpc2tlcmluYy5jb21cL290YXVwZGF0ZS5jcmVhdGUgb3BlbmlkIGVtYWlsIiwiYXV0aF90aW1lIjoxNjEzNjA3NTc2LCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAudXMtd2VzdC0yLmFtYXpvbmF3cy5jb21cL3VzLXdlc3QtMl9BV3dqTFh5bTIiLCJleHAiOjE2MTM2MTExNzYsImlhdCI6MTYxMzYwNzU3NiwidmVyc2lvbiI6MiwianRpIjoiYzUyNjI0YjItYmJkYi00N2RiLTllNTgtOGU5ZmU3Yjg1ODMxIiwiY2xpZW50X2lkIjoiN2NrMnRmb3FhdmM3MmM0NWhoN3RnZTQya2QiLCJ1c2VybmFtZSI6ImF6dXJlYWRfand1QGZpc2tlcmluYy5jb20ifQ.FvlES5AgjhymQKnHP41D2Ude0Ten6L8REBRXTyu5dyWGrG4vTfBGoxlkGE2-MEFc0s6uhbdST_E2Mc5QNlXG47ibK14tFl6kOqDd74TCfg5sWghb_nSjC-M769eUHQSQcs4L8jcnEt0bjqMmPtt8lZwu3VS7mkSRXD6_hX43rPLGUpMaz5RqKlfHX8YUyD6UnENW9Gg3zonPRsPWVtupc494B_pSZGuFs-jVzBDgb_SdrGt5wb3GazsNcB8KeAf0m0QoEiApsCYxKGUG9eQZw_CAUrhCj9mFT-xJuyvEp0t6B8HDHrdW4mIHblKqhZok1mPwCntJmOfyOs3niNaILg","id_token":"eyJraWQiOiJlUTNuZFJLaUVcL084VUZ5RHFsYjN0S1RzWG00SzVPMlc4NXd3VWkzT2tNZz0iLCJhbGciOiJSUzI1NiJ9.eyJhdF9oYXNoIjoiMHFmcmdyVlZfOW1XRWp5MVdOZDl5QSIsInN1YiI6IjJkZDZmZWQ5LWU1ODItNDUxYi1hOTNiLTViOTQxMGRmYmM0MyIsImNvZ25pdG86Z3JvdXBzIjpbInVzLXdlc3QtMl9BV3dqTFh5bTJfQXp1cmVBRCJdLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC51cy13ZXN0LTIuYW1hem9uYXdzLmNvbVwvdXMtd2VzdC0yX0FXd2pMWHltMiIsImNvZ25pdG86dXNlcm5hbWUiOiJhenVyZWFkX2p3dUBmaXNrZXJpbmMuY29tIiwiYXVkIjoiN2NrMnRmb3FhdmM3MmM0NWhoN3RnZTQya2QiLCJpZGVudGl0aWVzIjpbeyJ1c2VySWQiOiJqd3VAZmlza2VyaW5jLmNvbSIsInByb3ZpZGVyTmFtZSI6IkF6dXJlQUQiLCJwcm92aWRlclR5cGUiOiJTQU1MIiwiaXNzdWVyIjoiaHR0cHM6XC9cL3N0cy53aW5kb3dzLm5ldFwvNWFhNGI2NDAtYzlmYy00YTliLWIzYTMtZDRhN2QwMDhmYjVlXC8iLCJwcmltYXJ5IjoidHJ1ZSIsImRhdGVDcmVhdGVkIjoiMTYxMjkwMjQxMzM4MyJ9XSwidG9rZW5fdXNlIjoiaWQiLCJhdXRoX3RpbWUiOjE2MTM2MDc1NzYsImV4cCI6MTYxMzYxMTE3NiwiaWF0IjoxNjEzNjA3NTc2LCJlbWFpbCI6Imp3dUBmaXNrZXJpbmMuY29tIn0.NbEWEgX48Z-zz3gREEH44OpnvhoYDcm9RlVdqKVoSJ777g0A0LDpGwz7UGcqvZLeQLPsHaMyV8-sblLvKQvpsenJfq81XddVWCAqI55VCdbnouCphIDYOEPNbWs9ORdrXxciALTt1AAehsF0dTDG0V5fce5Vku2qZZbpELdq9r4CBJQXWtFiV8lUaZPEMNJbZVdh1KjwJSpeF8CtJGKUXIIm6tAYVVKc27YWgxe2fh3zhke5MUnGYrb98-RLmDUwpUQ4eBnXu9gtA-9qIpOumXkftogWpeNZ7Rc0tAI8ZvOmG8plFyYoRMrKuC4kECeUdrsRJlCv4ijpK_L7GwEL9Q","refresh_token":"eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.LHo8ysGz7T3sJtwf8aHpWFzH8B84yDvfL4Q0YuRd0kfKSA51z4hKFPSLpo4PiFodJ-VPugJQWfSYXXpe4Tjd3bdTH-oYDJcJvRHIV3ZIID0EApt53lkxsFWV_9b33bltLYyJ7DnclQq1GnfgohDhD9F2CpnN3Xa-ntVmF9ntLe6wxZvk_zdBlbhIPwCc4FuPDIB_skNaciWCzU9LUfzvcfZJAR8KztM8ofDm3YJGZrRJltz6In78ZlN1sIlFuPSIRy56sg3yG3wMfe9Lrst0VacG_2fy6Ccg9VuLqD_xnzMmzjwMF9PGdnO5DlCblWwrHsDE6FkTuDy7ojnPJpPJlw.gzFbDwAmMKp-4eiE.AfY0IecXygmkDUkUGIIG7JmhBSzk4VD_sEAwuTeOufKD_duvNXFTQYNU_QvDc7M-9Vssbbb35dMMw3KLxW2IbC7fll8lNvHHMm1gkxlVxK1h5uRhmgt0q7tyMwLw1iKUDqOa177faHZJISN_gvfh-rlbNswswDGU061dyFh-w6Ck8SXoPnWfp9GxZJBgxzZ5uBV1D7_1bAghqWYNMsMUTSvOYyeWvVJHap-gjtGc491Vf97z6mh9PDBvIi734D90NbV5idZ11CCW7liI5L7kgRwuHZVxiu_NpkPED7dWcaBhOATur4r3P28U39JC5P5FD4JXlqyPl9FXVBkW049E1vdJrrkV3IbiqUMVXlkUeq6G87YUTdmt8qRPgiOc-G6g84RxSPQE55uojbuSSlON2CKZYmSmFVM0X7bBU42wP1wNP7Jq3LTjHcj4rOaN1ozffJxyGs54r7NP4D9u3nt2ozNkjk_DNK3UmxDPaQtZAtFO1d-T7UXv2BvzoCN2LGilzxVi04p9LcvoTDzI5GUY9OsjGUsdSZJvISylHAMMDi8nSxsBBSPD18fzV0tLhdjGM0-XljiM4bjZWNR4Nvraus33p8U4k5lmn2bx13JfHvDa3Zqf_aK57lam6Zf_6mvAK4I7A40WmiolJCxeEeDD54ljF0kAluT4sw6sxVY8It80A95TGFd0lm5e-tGrFKIoqRyPV5uwzzz3XT1HVPJda1ufdGhSUj8slsyqTUrnphh6JWbRfA9mrLdKQKuqM3xEslAwYZOhX6qOzADbo5WQMneTwn34QixMT4A6imaDBc6P4cOaLo7hNyS3e1h6SfwigEX9H42wkC3TWOiITakFq3tKkVwahMkdeds_uxloNoeicdGePjob6BfU8xq0IKxJh9UoeCsSX4KVtIrErHyYuoU-_ENZXYArSwfqorKgdjmQAa13NQjOiHzpgA5HngSCK1xy6zq9NvNA7hUe9O0gTqrFZDsYhRWSYEuOt5QpxJYalPGKIPXlsUJOURfR_J0iT52UxiOZIuXmpk-X8fgDhM_0fZm8GQ2GaIsf3nR49h7QnZRG9azTZV5q9Bs0bqPvP7wRL5xenByeIBsdwP6Bwaqd3n5BkFn-LE7eo6UPn_9o7Gx3g9VlN8pG7SIo_3a07L0yauJIO4ahL5aC07uBCLu3pJQW0ftlVpLdAA8gh3XPhfvOuH4XV7yU1fQhqGKich1hhHx2dFHyVr5mJPxc1VQvAyyAhxjvyQ2TTLfvSiYLOP1vFCVjUwb_RjF5tuh3ArH9IlXH9kIBVQbiSOWfgQ1PqD-go4jqDR_ie3aGc5Fm8Vd6lSh_2HW2GR0Ht0ASoCbl3C5roXCRkyTTaGl3nX6uwfg.thw4B0ug4OIZsZrDzCtJcQ","expires_in": 3600,"token_type": "Bearer"}` diff --git a/pkg/azurestoragecontainer/asc.go b/pkg/azurestoragecontainer/asc.go index 4fc6b9d..ef551ae 100644 --- a/pkg/azurestoragecontainer/asc.go +++ b/pkg/azurestoragecontainer/asc.go @@ -8,7 +8,7 @@ import ( "strings" "time" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/Azure/azure-storage-blob-go/azblob" "github.com/pkg/errors" ) diff --git a/pkg/cache/apitokens.go b/pkg/cache/apitokens.go index d83110a..8c8c42c 100644 --- a/pkg/cache/apitokens.go +++ b/pkg/cache/apitokens.go @@ -4,10 +4,10 @@ import ( "sync" "time" - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/pkg/errors" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/ReneKroon/ttlcache/v2" ) diff --git a/pkg/cache/apitokens_test.go b/pkg/cache/apitokens_test.go index f09508a..b285dc4 100644 --- a/pkg/cache/apitokens_test.go +++ b/pkg/cache/apitokens_test.go @@ -3,13 +3,13 @@ package cache_test import ( "testing" - "fiskerinc.com/modules/adminroles" - "fiskerinc.com/modules/cache" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/adminroles" + "github.com/fiskerinc/cloud-services/pkg/cache" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestIntegration(t *testing.T) { diff --git a/pkg/cache/car_dtcs.go b/pkg/cache/car_dtcs.go index 5ea27fd..0854aa7 100644 --- a/pkg/cache/car_dtcs.go +++ b/pkg/cache/car_dtcs.go @@ -1,7 +1,7 @@ package cache import ( - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" ) type CarDTCsCacheInterface interface { diff --git a/pkg/cache/car_dtcs_test.go b/pkg/cache/car_dtcs_test.go index 29c6b02..2d5299d 100644 --- a/pkg/cache/car_dtcs_test.go +++ b/pkg/cache/car_dtcs_test.go @@ -3,8 +3,8 @@ package cache_test import ( "testing" - m "fiskerinc.com/modules/common" - "fiskerinc.com/modules/cache" + m "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/cache" "github.com/stretchr/testify/assert" ) diff --git a/pkg/cache/digital_twin.go b/pkg/cache/digital_twin.go index 3500f05..72fb1a1 100644 --- a/pkg/cache/digital_twin.go +++ b/pkg/cache/digital_twin.go @@ -7,10 +7,10 @@ import ( "strings" "time" - "fiskerinc.com/modules/dbc/state" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/utils/querystring" + "github.com/fiskerinc/cloud-services/pkg/dbc/state" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/utils/querystring" redigo "github.com/gomodule/redigo/redis" "github.com/pkg/errors" ) diff --git a/pkg/cache/drivers.go b/pkg/cache/drivers.go index 75149fa..036f938 100644 --- a/pkg/cache/drivers.go +++ b/pkg/cache/drivers.go @@ -1,10 +1,10 @@ package cache import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redis" "github.com/pkg/errors" ) diff --git a/pkg/cache/drivers_test.go b/pkg/cache/drivers_test.go index 941ad7c..d6488a2 100644 --- a/pkg/cache/drivers_test.go +++ b/pkg/cache/drivers_test.go @@ -4,12 +4,12 @@ import ( "encoding/json" "testing" - "fiskerinc.com/modules/cache" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/db/queries/mocks" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/redis/tester" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/cache" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/redis/tester" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) var mockRedis redis.Client diff --git a/pkg/cache/fileids.go b/pkg/cache/fileids.go index eda2700..2b9fc93 100644 --- a/pkg/cache/fileids.go +++ b/pkg/cache/fileids.go @@ -3,10 +3,10 @@ package cache import ( "encoding/json" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redis" r "github.com/gomodule/redigo/redis" "github.com/pkg/errors" diff --git a/pkg/cache/fileids_test.go b/pkg/cache/fileids_test.go index 333ea4a..9341c96 100644 --- a/pkg/cache/fileids_test.go +++ b/pkg/cache/fileids_test.go @@ -3,12 +3,12 @@ package cache_test import ( "testing" - "fiskerinc.com/modules/cache" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/db/queries/mocks" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/cache" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks" + "github.com/fiskerinc/cloud-services/pkg/redis" ) func TestRetrieveFileEncryptionParams(t *testing.T) { diff --git a/pkg/cache/filters.go b/pkg/cache/filters.go index a71921d..f1156f7 100644 --- a/pkg/cache/filters.go +++ b/pkg/cache/filters.go @@ -1,8 +1,8 @@ package cache import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/redis" ) func FillCarFilterOnline(redisCLI redis.Client, filter *common.CarSearch) error { diff --git a/pkg/cache/filters_test.go b/pkg/cache/filters_test.go index 9ee4339..a7ba948 100644 --- a/pkg/cache/filters_test.go +++ b/pkg/cache/filters_test.go @@ -3,9 +3,9 @@ package cache_test import ( "testing" - "fiskerinc.com/modules/cache" - m "fiskerinc.com/modules/common" - "fiskerinc.com/modules/redis/tester" + "github.com/fiskerinc/cloud-services/pkg/cache" + m "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/redis/tester" "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) diff --git a/pkg/cache/ringmap_test.go b/pkg/cache/ringmap_test.go index 3946602..e2a3194 100644 --- a/pkg/cache/ringmap_test.go +++ b/pkg/cache/ringmap_test.go @@ -5,7 +5,7 @@ import ( "strconv" "testing" - "fiskerinc.com/modules/cache" + "github.com/fiskerinc/cloud-services/pkg/cache" "github.com/stretchr/testify/assert" ) diff --git a/pkg/cache/subscription_types.go b/pkg/cache/subscription_types.go index 87002a9..00afd48 100644 --- a/pkg/cache/subscription_types.go +++ b/pkg/cache/subscription_types.go @@ -3,11 +3,11 @@ package cache import ( "encoding/json" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/duration" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/duration" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redis" "github.com/google/uuid" "github.com/pkg/errors" ) diff --git a/pkg/cache/subscription_types_test.go b/pkg/cache/subscription_types_test.go index f93ec58..f67b076 100644 --- a/pkg/cache/subscription_types_test.go +++ b/pkg/cache/subscription_types_test.go @@ -4,11 +4,11 @@ import ( "fmt" "testing" - "fiskerinc.com/modules/cache" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/cache" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/google/uuid" ) diff --git a/pkg/cache/vehicle_config.go b/pkg/cache/vehicle_config.go index 8096bb2..c62db22 100644 --- a/pkg/cache/vehicle_config.go +++ b/pkg/cache/vehicle_config.go @@ -3,13 +3,13 @@ package cache import ( "encoding/json" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/mongo" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/mongo" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" redigo "github.com/gomodule/redigo/redis" "github.com/pkg/errors" ) diff --git a/pkg/cache/vehicle_config_test.go b/pkg/cache/vehicle_config_test.go index 6774e7a..06b01f8 100644 --- a/pkg/cache/vehicle_config_test.go +++ b/pkg/cache/vehicle_config_test.go @@ -5,12 +5,12 @@ import ( "sort" "testing" - "fiskerinc.com/modules/cache" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/mongo" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/cache" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/mongo" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" redigo "github.com/gomodule/redigo/redis" "github.com/stretchr/testify/assert" ) diff --git a/pkg/cache/vehicle_state.go b/pkg/cache/vehicle_state.go index b9d2130..078d1e7 100644 --- a/pkg/cache/vehicle_state.go +++ b/pkg/cache/vehicle_state.go @@ -6,11 +6,11 @@ import ( "strings" "time" - "fiskerinc.com/modules/common" - dt "fiskerinc.com/modules/dbc/state" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/utils/querystring" + "github.com/fiskerinc/cloud-services/pkg/common" + dt "github.com/fiskerinc/cloud-services/pkg/dbc/state" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/utils/querystring" redigo "github.com/gomodule/redigo/redis" "github.com/pkg/errors" diff --git a/pkg/cache/vehicle_state_multi.go b/pkg/cache/vehicle_state_multi.go index d85aef6..840ae28 100644 --- a/pkg/cache/vehicle_state_multi.go +++ b/pkg/cache/vehicle_state_multi.go @@ -1,9 +1,9 @@ package cache import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redis" redigo "github.com/gomodule/redigo/redis" "github.com/pkg/errors" diff --git a/pkg/cache/vehicle_state_test.go b/pkg/cache/vehicle_state_test.go index e26c198..5d15fd1 100644 --- a/pkg/cache/vehicle_state_test.go +++ b/pkg/cache/vehicle_state_test.go @@ -5,10 +5,10 @@ import ( "testing" "time" - "fiskerinc.com/modules/cache" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/redis/tester" + "github.com/fiskerinc/cloud-services/pkg/cache" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/redis/tester" "github.com/stretchr/testify/assert" ) diff --git a/pkg/cache/vehicles.go b/pkg/cache/vehicles.go index 0593289..3e57ca2 100644 --- a/pkg/cache/vehicles.go +++ b/pkg/cache/vehicles.go @@ -2,8 +2,8 @@ package cache import ( "encoding/json" - "fiskerinc.com/modules/common" - orm "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + orm "github.com/fiskerinc/cloud-services/pkg/db/queries" "fmt" "github.com/ReneKroon/ttlcache/v2" "github.com/pkg/errors" diff --git a/pkg/cache/verify.go b/pkg/cache/verify.go index 3625c0d..3440194 100644 --- a/pkg/cache/verify.go +++ b/pkg/cache/verify.go @@ -1,10 +1,10 @@ package cache import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redis" redigo "github.com/gomodule/redigo/redis" "github.com/pkg/errors" diff --git a/pkg/cache/verify_test.go b/pkg/cache/verify_test.go index d47235e..602203b 100644 --- a/pkg/cache/verify_test.go +++ b/pkg/cache/verify_test.go @@ -3,12 +3,12 @@ package cache_test import ( "testing" - "fiskerinc.com/modules/cache" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries/mocks" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/redis/tester" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/cache" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/redis/tester" + "github.com/fiskerinc/cloud-services/pkg/testhelper" redigo "github.com/gomodule/redigo/redis" ) diff --git a/pkg/cache/vins.go b/pkg/cache/vins.go index a05c7b2..b7547e0 100644 --- a/pkg/cache/vins.go +++ b/pkg/cache/vins.go @@ -3,10 +3,10 @@ package cache import ( "errors" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redis" ) // RetrieveVINs retrieves VINs from redis or from DB based on driver ID and proceeds to cache VINs diff --git a/pkg/cache/vins_test.go b/pkg/cache/vins_test.go index a1714d6..69733ea 100644 --- a/pkg/cache/vins_test.go +++ b/pkg/cache/vins_test.go @@ -4,9 +4,9 @@ import ( "encoding/json" "testing" - "fiskerinc.com/modules/cache" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/cache" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) type mockRedisCacheVINs struct { diff --git a/pkg/cachev2/digital_twin.go b/pkg/cachev2/digital_twin.go index 9ad384a..6f0f592 100644 --- a/pkg/cachev2/digital_twin.go +++ b/pkg/cachev2/digital_twin.go @@ -7,10 +7,10 @@ import ( "strings" "time" - "fiskerinc.com/modules/dbc/state" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/utils/querystring" + "github.com/fiskerinc/cloud-services/pkg/dbc/state" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/utils/querystring" redigo "github.com/gomodule/redigo/redis" "github.com/pkg/errors" ) diff --git a/pkg/cachev2/drivers.go b/pkg/cachev2/drivers.go index 6f8530c..8a20af1 100644 --- a/pkg/cachev2/drivers.go +++ b/pkg/cachev2/drivers.go @@ -1,10 +1,10 @@ package cachev2 import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/logger" - redis "fiskerinc.com/modules/redisv2" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/logger" + redis "github.com/fiskerinc/cloud-services/pkg/redisv2" "github.com/pkg/errors" ) diff --git a/pkg/cachev2/drivers_test.go b/pkg/cachev2/drivers_test.go index 2c8e82a..fdb6b9c 100644 --- a/pkg/cachev2/drivers_test.go +++ b/pkg/cachev2/drivers_test.go @@ -4,12 +4,12 @@ import ( "encoding/json" "testing" - cache "fiskerinc.com/modules/cachev2" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/db/queries/mocks" - "fiskerinc.com/modules/redis/tester" - redis "fiskerinc.com/modules/redisv2" - "fiskerinc.com/modules/testhelper" + cache "github.com/fiskerinc/cloud-services/pkg/cachev2" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks" + "github.com/fiskerinc/cloud-services/pkg/redis/tester" + redis "github.com/fiskerinc/cloud-services/pkg/redisv2" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) var mockRedis redis.Client diff --git a/pkg/cachev2/vehicle_config.go b/pkg/cachev2/vehicle_config.go index d921fa8..6628c25 100644 --- a/pkg/cachev2/vehicle_config.go +++ b/pkg/cachev2/vehicle_config.go @@ -3,13 +3,13 @@ package cachev2 import ( "encoding/json" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/mongo" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/mongo" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" redigo "github.com/gomodule/redigo/redis" "github.com/pkg/errors" ) diff --git a/pkg/cachev2/vehicle_config_test.go b/pkg/cachev2/vehicle_config_test.go index 87ac7cc..0fae005 100644 --- a/pkg/cachev2/vehicle_config_test.go +++ b/pkg/cachev2/vehicle_config_test.go @@ -5,12 +5,12 @@ import ( "sort" "testing" - cache "fiskerinc.com/modules/cachev2" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/mongo" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/utils/elptr" + cache "github.com/fiskerinc/cloud-services/pkg/cachev2" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/mongo" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" redigo "github.com/gomodule/redigo/redis" "github.com/stretchr/testify/assert" ) diff --git a/pkg/cachev2/vehicle_state.go b/pkg/cachev2/vehicle_state.go index d294e1d..b51396a 100644 --- a/pkg/cachev2/vehicle_state.go +++ b/pkg/cachev2/vehicle_state.go @@ -6,11 +6,11 @@ import ( "strings" "time" - "fiskerinc.com/modules/common" - dt "fiskerinc.com/modules/dbc/state" - "fiskerinc.com/modules/logger" - redis "fiskerinc.com/modules/redisv2" - "fiskerinc.com/modules/utils/querystring" + "github.com/fiskerinc/cloud-services/pkg/common" + dt "github.com/fiskerinc/cloud-services/pkg/dbc/state" + "github.com/fiskerinc/cloud-services/pkg/logger" + redis "github.com/fiskerinc/cloud-services/pkg/redisv2" + "github.com/fiskerinc/cloud-services/pkg/utils/querystring" redispkg "github.com/redis/go-redis/v9" "github.com/pkg/errors" diff --git a/pkg/cachev2/vehicle_state_multi.go b/pkg/cachev2/vehicle_state_multi.go index 05968e4..7b879f3 100644 --- a/pkg/cachev2/vehicle_state_multi.go +++ b/pkg/cachev2/vehicle_state_multi.go @@ -3,12 +3,12 @@ package cachev2 import ( "context" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/logger" - redis "fiskerinc.com/modules/redisv2" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/logger" + redis "github.com/fiskerinc/cloud-services/pkg/redisv2" "github.com/pkg/errors" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/db/queries" ) func GetVINListDigitalTwin(vins []string, redisClient *redis.Connection) (digitalTwins map[string]common.CarState, errorList []error) { diff --git a/pkg/cachev2/vehicle_state_test.go b/pkg/cachev2/vehicle_state_test.go index e8d99a3..f7df15b 100644 --- a/pkg/cachev2/vehicle_state_test.go +++ b/pkg/cachev2/vehicle_state_test.go @@ -5,9 +5,9 @@ import ( "testing" "time" - cache "fiskerinc.com/modules/cachev2" - "fiskerinc.com/modules/common" - redis "fiskerinc.com/modules/redisv2" + cache "github.com/fiskerinc/cloud-services/pkg/cachev2" + "github.com/fiskerinc/cloud-services/pkg/common" + redis "github.com/fiskerinc/cloud-services/pkg/redisv2" "github.com/go-redis/redismock/v9" "github.com/stretchr/testify/assert" ) diff --git a/pkg/cachev2/vehicle_state_towman.go b/pkg/cachev2/vehicle_state_towman.go index 01a001d..7340aff 100644 --- a/pkg/cachev2/vehicle_state_towman.go +++ b/pkg/cachev2/vehicle_state_towman.go @@ -4,9 +4,9 @@ import ( "context" "errors" - "fiskerinc.com/modules/common" - redis "fiskerinc.com/modules/redisv2" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/common" + redis "github.com/fiskerinc/cloud-services/pkg/redisv2" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" ) func GetTowManDigitalTwin(vin string, redisClient *redis.Connection)(tdt common.TowmanDigitalTwin, err error){ diff --git a/pkg/cachev2/verify.go b/pkg/cachev2/verify.go index 471f4b5..de848c3 100644 --- a/pkg/cachev2/verify.go +++ b/pkg/cachev2/verify.go @@ -3,9 +3,9 @@ package cachev2 import ( "fmt" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - redis "fiskerinc.com/modules/redisv2" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + redis "github.com/fiskerinc/cloud-services/pkg/redisv2" "github.com/pkg/errors" ) diff --git a/pkg/cachev2/verify_test.go b/pkg/cachev2/verify_test.go index 3750797..70ac5c8 100644 --- a/pkg/cachev2/verify_test.go +++ b/pkg/cachev2/verify_test.go @@ -3,12 +3,12 @@ package cachev2_test import ( "testing" - "fiskerinc.com/modules/cache" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries/mocks" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/redis/tester" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/cache" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/redis/tester" + "github.com/fiskerinc/cloud-services/pkg/testhelper" redigo "github.com/gomodule/redigo/redis" ) diff --git a/pkg/can-go/.gitignore b/pkg/can-go/.gitignore new file mode 100644 index 0000000..6e3e885 --- /dev/null +++ b/pkg/can-go/.gitignore @@ -0,0 +1,3 @@ +# generated DBC files +*.dbc.go +*.dbc \ No newline at end of file diff --git a/pkg/can-go/CODE_OF_CONDUCT.md b/pkg/can-go/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..a899c5e --- /dev/null +++ b/pkg/can-go/CODE_OF_CONDUCT.md @@ -0,0 +1,129 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or + advances of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email + address, without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +. + +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/pkg/can-go/LICENSE b/pkg/can-go/LICENSE new file mode 100644 index 0000000..933e120 --- /dev/null +++ b/pkg/can-go/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Einride AB + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pkg/can-go/Makefile b/pkg/can-go/Makefile new file mode 100644 index 0000000..340fde5 --- /dev/null +++ b/pkg/can-go/Makefile @@ -0,0 +1,70 @@ +SHELL := /bin/bash + +all: \ + commitlint \ + stringer-generate \ + mockgen-generate \ + testdata \ + go-lint \ + go-review \ + go-test \ + go-mod-tidy \ + git-verify-nodiff + +include tools/commitlint/rules.mk +include tools/git-verify-nodiff/rules.mk +include tools/golangci-lint/rules.mk +include tools/goreview/rules.mk +include tools/semantic-release/rules.mk +include tools/stringer/rules.mk + +.PHONY: clean +clean: + $(info [$@] removing build files...) + @rm -rf tools/*/*/ build + +.PHONY: mockgen-generate +mockgen-generate: \ + internal/gen/mock/mockcanrunner/mocks.go \ + internal/gen/mock/mockclock/mocks.go \ + internal/gen/mock/mocksocketcan/mocks.go + +internal/gen/mock/mockcanrunner/mocks.go: pkg/canrunner/run.go go.mod + go run github.com/golang/mock/mockgen \ + -destination $@ -package mockcanrunner github.com/Fisker-Inc/project-ai-can-go/pkg/canrunner \ + Node,TransmittedMessage,ReceivedMessage,FrameTransmitter,FrameReceiver + +internal/gen/mock/mockclock/mocks.go: internal/clock/clock.go go.mod + go run github.com/golang/mock/mockgen \ + -destination $@ -package mockclock github.com/Fisker-Inc/project-ai-can-go/internal/clock \ + Clock,Ticker + +internal/gen/mock/mocksocketcan/mocks.go: pkg/socketcan/fileconn.go go.mod + go run github.com/golang/mock/mockgen \ + -destination $@ -package mocksocketcan -source $< + +.PHONY: stringer-generate +stringer-generate: \ + pkg/descriptor/sendtype_string.go \ + pkg/socketcan/errorclass_string.go \ + pkg/socketcan/protocolviolationerrorlocation_string.go \ + pkg/socketcan/protocolviolationerror_string.go \ + pkg/socketcan/controllererror_string.go \ + pkg/socketcan/transceivererror_string.go + +%_string.go: %.go $(stringer) + go generate $< + +.PHONY: testdata +testdata: + go run cmd/cantool/main.go generate testdata/dbc testdata/gen/go + +.PHONY: go-test +go-test: + $(info [$@] running Go tests...) + @mkdir -p build/coverage + @go test -short -race -coverprofile=build/coverage/$@.txt -covermode=atomic ./... + +.PHONY: go-mod-tidy +go-mod-tidy: + go mod tidy -v diff --git a/pkg/can-go/README.md b/pkg/can-go/README.md new file mode 100644 index 0000000..a65c359 --- /dev/null +++ b/pkg/can-go/README.md @@ -0,0 +1,168 @@ +# :electric_plug: CAN Go + +[![PkgGoDev][pkg-badge]][pkg] +[![GoReportCard][report-badge]][report] +[![Codecov][codecov-badge]][codecov] + +[pkg-badge]: https://pkg.go.dev/badge/github.com/Fisker-Inc/project-ai-can-go +[pkg]: https://pkg.go.dev/github.com/Fisker-Inc/project-ai-can-go +[report-badge]: https://goreportcard.com/badge/github.com/Fisker-Inc/project-ai-can-go +[report]: https://goreportcard.com/report/github.com/Fisker-Inc/project-ai-can-go +[codecov-badge]: https://codecov.io/gh/einride/can-go/branch/master/graph/badge.svg +[codecov]: https://codecov.io/gh/einride/can-go + +CAN toolkit for Go programmers. + +can-go makes use of the Linux SocketCAN abstraction for CAN communication. +(See the [SocketCAN][socketcan] documentation for more details). + +[socketcan]: https://www.kernel.org/doc/Documentation/networking/can.txt + +## Modifications to the Original Repo +Original repo: `https://github.com/jshiv/can-go/tree/623b1140d54a845026249a5bbbaf23f44c614173`. + +Comment out the value descriptor generation code within `can-go/internal/generate/file.go`, lines 128-141. This code creates compile errors due to duplicate values within the `.dbc` file provided. + +## DBC Go Code Library Generation +1. Place `.dbc` file into `orig/` folder. +2. Run `scripts/main.go`: +``` +go run scripts/main.go +``` +This will translate all Chinese variable values within the file to English. Any non-alphanumeric characters are also removed. **NOTE** The default read file is labelled `orig/121-N60AB_ADASBUS_Matrix_CANFD_V2.3.dbc` and the default write file is labelled `dbc/n60.dbc`. + +3. There will still be leftover Chinese characters in the `.dbc` file, I chose to just remove them. +4. Generate Go code from cleaned `.dbc` file: +``` +can-go> go run cmd/cantool/main.go generate data/dbc dbc +``` + +## Examples + +### Decoding CAN messages + +Decoding CAN messages from byte arrays can be done using `can.Payload` + +```go +func main() { + // DBC file + var dbcFile = []byte(` + VERSION "" + NS_ : + BS_: + BU_: DBG DRIVER IO MOTOR SENSOR + + BO_ 1530 DisconnectState: 14 MOTOR + SG_ LockCountRearRight : 91|20@0+ (1,0) [0|1048575] "" IO + SG_ DisconnectStateRearRight : 95|4@0+ (1,0) [0|5] "" IO + SG_ CurrentRearRight : 79|16@0+ (1,0) [0|65535] "" IO + SG_ DisconnectStateRearRightTarget : 64|1@0+ (1,0) [0|1] "" IO + SG_ TargetSpeedRearRight : 63|15@0+ (0.125,-2048) [-2048|2047.875] "rad/s" IO + SG_ LockCountRearLeft : 35|20@0+ (1,0) [0|1048575] "" IO + SG_ DisconnectStateRearLeft : 39|4@0+ (1,0) [0|5] "" IO + SG_ CurrentRearLeft : 23|16@0+ (1,0) [0|65535] "" IO + SG_ DisconnectStateRearLeftTarget : 8|1@0+ (1,0) [0|1] "" IO + SG_ TargetSpeedRearLeft : 7|15@0+ (0.125,-2048) [-2048|2047.875] "rad/s" IO + + VAL_ 1530 DisconnectStateRearRight 0 "Undefined" 1 "Locked" 2 "Unlocked" 3 "Locking" 4 "Unlocking" 5 "Faulted" ; + VAL_ 1530 DisconnectStateRearLeft 0 "Undefined" 1 "Locked" 2 "Unlocked" 3 "Locking" 4 "Unlocking" 5 "Faulted" ; + `) + + // Create payload from hex string + byteStringHex := "8000000420061880000005200600" + p, _ := can.PayloadFromHex(byteStringHex) + + // Load example dbc file + c, _ := generate.Compile("test.dbc", dbcFile) + db := *c.Database + + // Decode message frame ID 1530 + message, _ := db.Message(uint32(1530)) + decodedSignals := message.Decode(&p) + for _, signal := range decodedSignals { + fmt.Printf("Signal: %s, Value: %f, Description: %s\n", signal.Signal.Name, signal.Value, signal.Description) + } +} +``` + +``` +Signal: TargetSpeedRearLeft, Value: 0.000000, Description: +Signal: DisconnectStateRearLeftTarget, Value: 0.000000, Description: +Signal: CurrentRearLeft, Value: 4.000000, Description: +Signal: LockCountRearLeft, Value: 1560.000000, Description: +Signal: DisconnectStateRearLeft, Value: 2.000000, Description: Unlocked +Signal: TargetSpeedRearRight, Value: 0.000000, Description: +Signal: DisconnectStateRearRightTarget, Value: 0.000000, Description: +Signal: CurrentRearRight, Value: 5.000000, Description: +Signal: LockCountRearRight, Value: 1536.000000, Description: +Signal: DisconnectStateRearRight, Value: 2.000000, Description: Unlocked +``` + + +### Receiving CAN frames + +Receiving CAN frames from a socketcan interface. + +```go +func main() { + // Error handling omitted to keep example simple + conn, _ := socketcan.DialContext(context.Background(), "can", "can0") + + recv := socketcan.NewReceiver(conn) + for recv.Receive() { + frame := recv.Frame() + fmt.Println(frame.String()) + } +} +``` + +### Sending CAN frames/messages + +Sending CAN frames to a socketcan interface. + +```go +func main() { + // Error handling omitted to keep example simple + + conn, _ := socketcan.DialContext(context.Background(), "can", "can0") + + frame := can.Frame{} + tx := socketcan.NewTransmitter(conn) + _ = tx.TransmitFrame(context.Background(), frame) +} +``` + +### Generating Go code from a DBC file + +It is possible to generate Go code from a `.dbc` file. + +``` +$ go run github.com/Fisker-Inc/project-ai-can-go/cmd/cantool generate +``` + +In order to generate Go code that makes sense, we currently perform some +validations when parsing the DBC file so there may need to be some changes +on the DBC file to make it work + +After generating Go code we can marshal a message to a frame: + +```go +// import etruckcan "github.com/myproject/myrepo/gen" + +auxMsg := etruckcan.NewAuxiliary().SetHeadLights(etruckcan.Auxiliary_HeadLights_LowBeam) +frame := auxMsg.Frame() +``` + +Or unmarshal a frame to a message: + +```go +// import etruckcan "github.com/myproject/myrepo/gen" + +// Error handling omitted for simplicity +_ := recv.Receive() +frame := recv.Frame() + +var auxMsg *etruckcan.Auxiliary +_ = auxMsg.UnmarshalFrame(frame) + +``` diff --git a/pkg/can-go/can.go b/pkg/can-go/can.go new file mode 100644 index 0000000..9c5bab6 --- /dev/null +++ b/pkg/can-go/can.go @@ -0,0 +1,4 @@ +// Package can provides primitives for working with CAN. +// +// See: https://en.wikipedia.org/wiki/CAN_bus +package can // import "github.com/fiskerinc/cloud-services/pkg/can-go" diff --git a/pkg/can-go/cmd/cantool/main.go b/pkg/can-go/cmd/cantool/main.go new file mode 100644 index 0000000..97ac5c4 --- /dev/null +++ b/pkg/can-go/cmd/cantool/main.go @@ -0,0 +1,212 @@ +package main + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/internal/generate" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/definitiontypeorder" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/intervals" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/lineendings" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/messagenames" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/multiplexedsignals" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/newsymbols" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/nodereferences" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/noreservedsignals" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/requireddefinitions" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/signalbounds" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/signalnames" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/singletondefinitions" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/siunits" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/uniquenodenames" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/uniquesignalnames" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/unitsuffixes" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/valuedescriptions" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/version" + "github.com/fatih/color" + "gopkg.in/alecthomas/kingpin.v2" +) + +func main() { + app := kingpin.New("cantool", "CAN tool for Go programmers") + generateCommand(app) + lintCommand(app) + kingpin.MustParse(app.Parse(os.Args[1:])) +} + +func generateCommand(app *kingpin.Application) { + command := app.Command("generate", "generate CAN messages") + inputDir := command. + Arg("input-dir", "input directory"). + Required(). + ExistingDir() + outputDir := command. + Arg("output-dir", "output directory"). + Required(). + String() + command.Action(func(c *kingpin.ParseContext) error { + return filepath.Walk(*inputDir, func(p string, i os.FileInfo, err error) error { + if err != nil { + return err + } + if i.IsDir() || filepath.Ext(p) != ".dbc" { + return nil + } + relPath, err := filepath.Rel(*inputDir, p) + if err != nil { + return err + } + outputFile := relPath + ".go" + outputPath := filepath.Join(*outputDir, outputFile) + return genGo(p, outputPath) + }) + }) +} + +func lintCommand(app *kingpin.Application) { + command := app.Command("lint", "lint DBC files") + fileOrDir := command. + Arg("file-or-dir", "DBC file or directory"). + Required(). + ExistingFileOrDir() + command.Action(func(context *kingpin.ParseContext) error { + filesToLint, err := resolveFileOrDirectory(*fileOrDir) + if err != nil { + return err + } + var hasFailed bool + for _, lintFile := range filesToLint { + f, err := os.Open(lintFile) + if err != nil { + return err + } + source, err := ioutil.ReadAll(f) + if err != nil { + return err + } + p := dbc.NewParser(f.Name(), source) + if err := p.Parse(); err != nil { + printError(source, err.Position(), err.Reason(), "parse") + continue + } + for _, a := range analyzers() { + pass := &analysis.Pass{ + Analyzer: a, + File: p.File(), + } + if err := a.Run(pass); err != nil { + return err + } + hasFailed = hasFailed || len(pass.Diagnostics) > 0 + for _, d := range pass.Diagnostics { + printError(source, d.Pos, d.Message, a.Name) + } + } + } + if hasFailed { + return errors.New("one or more lint errors") + } + return nil + }) +} + +func analyzers() []*analysis.Analyzer { + return []*analysis.Analyzer{ + // TODO: Re-evaluate if we want boolprefix.Analyzer(), since it creates a lot of churn in vendor schemas + definitiontypeorder.Analyzer(), + intervals.Analyzer(), + lineendings.Analyzer(), + messagenames.Analyzer(), + multiplexedsignals.Analyzer(), + newsymbols.Analyzer(), + nodereferences.Analyzer(), + noreservedsignals.Analyzer(), + requireddefinitions.Analyzer(), + signalbounds.Analyzer(), + signalnames.Analyzer(), + singletondefinitions.Analyzer(), + siunits.Analyzer(), + uniquenodenames.Analyzer(), + uniquesignalnames.Analyzer(), + unitsuffixes.Analyzer(), + valuedescriptions.Analyzer(), + version.Analyzer(), + } +} + +func genGo(inputFile, outputFile string) error { + if err := os.MkdirAll(filepath.Dir(outputFile), 0o755); err != nil { + return err + } + input, err := ioutil.ReadFile(inputFile) + if err != nil { + return err + } + result, err := generate.Compile(inputFile, input) + if err != nil { + return err + } + for _, warning := range result.Warnings { + return warning + } + output, err := generate.Database(result.Hash, result.Database) + if err != nil { + return err + } + if err := ioutil.WriteFile(outputFile, output, 0o600); err != nil { + return err + } + fmt.Println("hash:", result.Hash) + fmt.Println("version:", result.Database.Version) + fmt.Println("wrote:", outputFile) + return nil +} + +func resolveFileOrDirectory(fileOrDirectory string) ([]string, error) { + fileInfo, err := os.Stat(fileOrDirectory) + if err != nil { + return nil, err + } + if !fileInfo.IsDir() { + return []string{fileOrDirectory}, nil + } + var files []string + if err := filepath.Walk(fileOrDirectory, func(path string, info os.FileInfo, err error) error { + if !info.IsDir() && filepath.Ext(path) == ".dbc" { + files = append(files, path) + } + return nil + }); err != nil { + return nil, err + } + return files, nil +} + +func printError(source []byte, pos scanner.Position, msg, name string) { + fmt.Printf("\n%s: %s (%s)\n", pos, color.RedString("%s", msg), name) + fmt.Printf("%s\n", getSourceLine(source, pos)) + fmt.Printf("%s\n", caretAtPosition(pos)) +} + +func getSourceLine(source []byte, pos scanner.Position) []byte { + lineStart := pos.Offset + for lineStart > 0 && source[lineStart-1] != '\n' { + lineStart-- + } + lineEnd := pos.Offset + for lineEnd < len(source) && source[lineEnd] != '\n' { + lineEnd++ + } + return source[lineStart:lineEnd] +} + +func caretAtPosition(pos scanner.Position) string { + return strings.Repeat(" ", pos.Column-1) + color.YellowString("^") +} diff --git a/pkg/can-go/data.go b/pkg/can-go/data.go new file mode 100644 index 0000000..e2e6e73 --- /dev/null +++ b/pkg/can-go/data.go @@ -0,0 +1,309 @@ +package can + +import ( + "fmt" + + "github.com/fiskerinc/cloud-services/pkg/can-go/internal/reinterpret" +) + +const MaxDataLength = 8 + +// Data holds the data in a CAN frame. +// +// Layout +// +// Individual bits in the data are numbered according to the following scheme: +// +// BIT +// NUMBER +// +------+------+------+------+------+------+------+------+ +// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +// BYTE +------+------+------+------+------+------+------+------+ +// NUMBER +// +-----+ +------+------+------+------+------+------+------+------+ +// | 0 | | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 1 | | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 2 | | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 3 | | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 4 | | 39 | 38 | 37 | 36 | 35 | 34 | 33 | 32 | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 5 | | 47 | 46 | 45 | 44 | 43 | 42 | 41 | 40 | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 6 | | 55 | 54 | 53 | 52 | 51 | 50 | 49 | 48 | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 7 | | 63 | 62 | 61 | 60 | 59 | 58 | 57 | 56 | +// +-----+ +------+------+------+------+------+------+------+------+ +// +// Bit ranges can be manipulated using little-endian and big-endian bit ordering. +// +// Little-endian bit ranges +// +// Example range of length 32 starting at bit 29: +// +// BIT +// NUMBER +// +------+------+------+------+------+------+------+------+ +// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +// BYTE +------+------+------+------+------+------+------+------+ +// NUMBER +// +-----+ +------+------+------+------+------+------+------+------+ +// | 0 | | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 1 | | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 2 | | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 3 | | <-------------LSb | 28 | 27 | 26 | 25 | 24 | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 4 | | <-------------------------------------------------- | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 5 | | <-------------------------------------------------- | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 6 | | <-------------------------------------------------- | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 7 | | 63 | 62 | 61 | <-MSb--------------------------- | +// +-----+ +------+------+------+------+------+------+------+------+ +// +// Big-endian bit ranges +// +// Example range of length 32 starting at bit 29: +// +// BIT +// NUMBER +// +------+------+------+------+------+------+------+------+ +// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +// BYTE +------+------+------+------+------+------+------+------+ +// NUMBER +// +-----+ +------+------+------+------+------+------+------+------+ +// | 0 | | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 1 | | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 2 | | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 3 | | 31 | 30 | <-MSb--------------------------------- | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 4 | | <-------------------------------------------------- | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 5 | | <-------------------------------------------------- | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 6 | | <-------------------------------------------------- | +// +-----+ +------+------+------+------+------+------+------+------+ +// | 7 | | <------LSb | 61 | 60 | 59 | 58 | 57 | 56 | +// +-----+ +------+------+------+------+------+------+------+------+ +type Data [MaxDataLength]byte + +// UnsignedBitsLittleEndian returns the little-endian bit range [start, start+length) as an unsigned value. +func (d *Data) UnsignedBitsLittleEndian(start, length uint8) uint64 { + // pack bits into one continuous value + packed := d.PackLittleEndian() + // lsb index in the packed value is the start bit + lsbIndex := start + // shift away lower bits + shifted := packed >> lsbIndex + // mask away higher bits + masked := shifted & ((1 << length) - 1) + // done + return masked +} + +// UnsignedBitsBigEndian returns the big-endian bit range [start, start+length) as an unsigned value. +func (d *Data) UnsignedBitsBigEndian(start, length uint8) uint64 { + // pack bits into one continuous value + packed := d.PackBigEndian() + // calculate msb index in the packed value + msbIndex := invertEndian(start) + // calculate lsb index in the packed value + lsbIndex := msbIndex - length + 1 + // shift away lower bits + shifted := packed >> lsbIndex + // mask away higher bits + masked := shifted & ((1 << length) - 1) + // done + return masked +} + +// SignedBitsLittleEndian returns little-endian bit range [start, start+length) as a signed value. +func (d *Data) SignedBitsLittleEndian(start, length uint8) int64 { + unsigned := d.UnsignedBitsLittleEndian(start, length) + return reinterpret.AsSigned(unsigned, length) +} + +// SignedBitsBigEndian returns little-endian bit range [start, start+length) as a signed value. +func (d *Data) SignedBitsBigEndian(start, length uint8) int64 { + unsigned := d.UnsignedBitsBigEndian(start, length) + return reinterpret.AsSigned(unsigned, length) +} + +// SetUnsignedBitsBigEndian sets the little-endian bit range [start, start+length) to the provided unsigned value. +func (d *Data) SetUnsignedBitsLittleEndian(start, length uint8, value uint64) { + // pack bits into one continuous value + packed := d.PackLittleEndian() + // lsb index in the packed value is the start bit + lsbIndex := start + // calculate bit mask for zeroing the bit range to set + unsetMask := ^uint64(((1 << length) - 1) << lsbIndex) + // calculate bit mask for setting the new value + setMask := value << lsbIndex + // calculate the new packed value + newPacked := packed&unsetMask | setMask + // unpack the new packed value into the data + d.UnpackLittleEndian(newPacked) +} + +// SetUnsignedBitsBigEndian sets the big-endian bit range [start, start+length) to the provided unsigned value. +func (d *Data) SetUnsignedBitsBigEndian(start, length uint8, value uint64) { + // pack bits into one continuous value + packed := d.PackBigEndian() + // calculate msb index in the packed value + msbIndex := invertEndian(start) + // calculate lsb index in the packed value + lsbIndex := msbIndex - length + 1 + // calculate bit mask for zeroing the bit range to set + unsetMask := ^uint64(((1 << length) - 1) << lsbIndex) + // calculate bit mask for setting the new value + setMask := value << lsbIndex + // calculate the new packed value + newPacked := packed&unsetMask | setMask + // unpack the new packed value into the data + d.UnpackBigEndian(newPacked) +} + +// SetSignedBitsLittleEndian sets the little-endian bit range [start, start+length) to the provided signed value. +func (d *Data) SetSignedBitsLittleEndian(start, length uint8, value int64) { + d.SetUnsignedBitsLittleEndian(start, length, reinterpret.AsUnsigned(value, length)) +} + +// SetSignedBitsBigEndian sets the big-endian bit range [start, start+length) to the provided signed value. +func (d *Data) SetSignedBitsBigEndian(start, length uint8, value int64) { + d.SetUnsignedBitsBigEndian(start, length, reinterpret.AsUnsigned(value, length)) +} + +// Bit returns the value of the i:th bit in the data as a bool. +func (d *Data) Bit(i uint8) bool { + if i > 63 { + return false + } + // calculate which byte the bit belongs to + byteIndex := i / 8 + // calculate bit mask for extracting the bit + bitMask := uint8(1 << (i % 8)) + // mocks the bit + bit := d[byteIndex]&bitMask > 0 + // done + return bit +} + +// SetBit sets the value of the i:th bit in the data. +func (d *Data) SetBit(i uint8, value bool) { + if i > 63 { + return + } + byteIndex := i / 8 + bitIndex := i % 8 + if value { + d[byteIndex] |= uint8(1 << bitIndex) + } else { + d[byteIndex] &= ^uint8(1 << bitIndex) + } +} + +// PackLittleEndian packs the data into a contiguous uint64 value for little-endian signals. +func (d *Data) PackLittleEndian() uint64 { + var packed uint64 + packed |= uint64(d[0]) << (0 * 8) + packed |= uint64(d[1]) << (1 * 8) + packed |= uint64(d[2]) << (2 * 8) + packed |= uint64(d[3]) << (3 * 8) + packed |= uint64(d[4]) << (4 * 8) + packed |= uint64(d[5]) << (5 * 8) + packed |= uint64(d[6]) << (6 * 8) + packed |= uint64(d[7]) << (7 * 8) + return packed +} + +// PackBigEndian packs the data into a contiguous uint64 value for big-endian signals. +func (d *Data) PackBigEndian() uint64 { + var packed uint64 + packed |= uint64(d[0]) << (7 * 8) + packed |= uint64(d[1]) << (6 * 8) + packed |= uint64(d[2]) << (5 * 8) + packed |= uint64(d[3]) << (4 * 8) + packed |= uint64(d[4]) << (3 * 8) + packed |= uint64(d[5]) << (2 * 8) + packed |= uint64(d[6]) << (1 * 8) + packed |= uint64(d[7]) << (0 * 8) + return packed +} + +// UnpackLittleEndian sets the value of d.Bytes by unpacking the provided value as sequential little-endian bits. +func (d *Data) UnpackLittleEndian(packed uint64) { + d[0] = uint8(packed >> (0 * 8)) + d[1] = uint8(packed >> (1 * 8)) + d[2] = uint8(packed >> (2 * 8)) + d[3] = uint8(packed >> (3 * 8)) + d[4] = uint8(packed >> (4 * 8)) + d[5] = uint8(packed >> (5 * 8)) + d[6] = uint8(packed >> (6 * 8)) + d[7] = uint8(packed >> (7 * 8)) +} + +// UnpackBigEndian sets the value of d.Bytes by unpacking the provided value as sequential big-endian bits. +func (d *Data) UnpackBigEndian(packed uint64) { + d[0] = uint8(packed >> (7 * 8)) + d[1] = uint8(packed >> (6 * 8)) + d[2] = uint8(packed >> (5 * 8)) + d[3] = uint8(packed >> (4 * 8)) + d[4] = uint8(packed >> (3 * 8)) + d[5] = uint8(packed >> (2 * 8)) + d[6] = uint8(packed >> (1 * 8)) + d[7] = uint8(packed >> (0 * 8)) +} + +// invertEndian converts from big-endian to little-endian bit indexing and vice versa. +func invertEndian(i uint8) uint8 { + row := i / 8 + col := i % 8 + oppositeRow := 7 - row + bitIndex := (oppositeRow * 8) + col + return bitIndex +} + +// CheckBitRangeLittleEndian checks that a little-endian bit range fits in the data. +func CheckBitRangeLittleEndian(frameLength, rangeStart, rangeLength uint8) error { + lsbIndex := rangeStart + msbIndex := rangeStart + rangeLength - 1 + upperBound := frameLength * 8 + if msbIndex >= upperBound { + return fmt.Errorf("bit range out of bounds [0, %v): [%v, %v]", upperBound, lsbIndex, msbIndex) + } + return nil +} + +// CheckBitRangeBigEndian checks that a big-endian bit range fits in the data. +func CheckBitRangeBigEndian(frameLength, rangeStart, rangeLength uint8) error { + upperBound := frameLength * 8 + if rangeStart >= upperBound { + return fmt.Errorf("bit range starts out of bounds [0, %v): %v", upperBound, rangeStart) + } + msbIndex := invertEndian(rangeStart) + lsbIndex := msbIndex - rangeLength + 1 + end := invertEndian(lsbIndex) + if end >= upperBound { + return fmt.Errorf("bit range ends out of bounds [0, %v): %v", upperBound, end) + } + return nil +} + +// CheckValue checks that a value fits in a number of bits. +func CheckValue(value uint64, bits uint8) error { + upperBound := uint64(1 << bits) + if value >= upperBound { + return fmt.Errorf("value out of bounds [0, %v): %v", upperBound, value) + } + return nil +} diff --git a/pkg/can-go/data_test.go b/pkg/can-go/data_test.go new file mode 100644 index 0000000..63a1394 --- /dev/null +++ b/pkg/can-go/data_test.go @@ -0,0 +1,347 @@ +package can + +import ( + "fmt" + "testing" + "testing/quick" + + "gotest.tools/v3/assert" +) + +func TestData_Bit(t *testing.T) { + for i, tt := range []struct { + data Data + bits []struct { + i uint8 + bit bool + } + }{ + { + data: Data{0x01, 0x23}, + bits: []struct { + i uint8 + bit bool + }{ + // nibble 1: 0x1 + {bit: true, i: 0}, + {bit: false, i: 1}, + {bit: false, i: 2}, + {bit: false, i: 3}, + // nibble 2: 0x0 + {bit: false, i: 4}, + {bit: false, i: 5}, + {bit: false, i: 6}, + {bit: false, i: 7}, + // nibble 3: 0x3 + {bit: true, i: 8}, + {bit: true, i: 9}, + {bit: false, i: 10}, + {bit: false, i: 11}, + // nibble 4: 0x2 + {bit: false, i: 12}, + {bit: true, i: 13}, + {bit: false, i: 14}, + {bit: false, i: 15}, + }, + }, + } { + i, tt := i, tt + t.Run("Get", func(t *testing.T) { + i, tt := i, tt + for j, ttBit := range tt.bits { + j, ttBit := j, ttBit + t.Run(fmt.Sprintf("tt=%v,bit=%v", i, j), func(t *testing.T) { + bit := tt.data.Bit(ttBit.i) + assert.Equal(t, ttBit.bit, bit) + }) + } + }) + t.Run("Set", func(t *testing.T) { + i, tt := i, tt + t.Run(fmt.Sprintf("data=%v", i), func(t *testing.T) { + var data Data + for _, tBit := range tt.bits { + data.SetBit(tBit.i, tBit.bit) + } + assert.DeepEqual(t, tt.data, data) + }) + }) + } +} + +func TestData_Property_SetGetBit(t *testing.T) { + f := func(_ Data, _ uint8, bit bool) bool { + return bit + } + g := func(data Data, i uint8, bit bool) bool { + i %= 64 + data.SetBit(i, bit) + return data.Bit(i) + } + assert.NilError(t, quick.CheckEqual(f, g, nil)) +} + +func TestData_LittleEndian(t *testing.T) { + for i, tt := range []struct { + data Data + signals []struct { + start uint8 + length uint8 + unsigned uint64 + signed int64 + } + }{ + { + data: Data{0x80, 0x01}, + signals: []struct { + start uint8 + length uint8 + unsigned uint64 + signed int64 + }{ + {start: 7, length: 2, unsigned: 0x3, signed: -1}, + }, + }, + { + data: Data{0x01, 0x02, 0x03}, + signals: []struct { + start uint8 + length uint8 + unsigned uint64 + signed int64 + }{ + {start: 0, length: 24, unsigned: 0x030201, signed: 197121}, + }, + }, + { + data: Data{0x40, 0x23, 0x01, 0x12}, + signals: []struct { + start uint8 + length uint8 + unsigned uint64 + signed int64 + }{ + {start: 24, length: 8, unsigned: 0x12, signed: 18}, + {start: 8, length: 8, unsigned: 0x23, signed: 35}, + {start: 4, length: 16, unsigned: 0x1234, signed: 4660}, + }, + }, + } { + i, tt := i, tt + t.Run(fmt.Sprintf("UnsignedBits:%v", i), func(t *testing.T) { + for j, signal := range tt.signals { + j, signal := j, signal + t.Run(fmt.Sprintf("signal:%v", j), func(t *testing.T) { + actual := tt.data.UnsignedBitsLittleEndian(signal.start, signal.length) + assert.Equal(t, signal.unsigned, actual) + }) + } + }) + t.Run(fmt.Sprintf("SignedBits:%v", i), func(t *testing.T) { + for j, signal := range tt.signals { + j, signal := j, signal + t.Run(fmt.Sprintf("signal:%v", j), func(t *testing.T) { + actual := tt.data.SignedBitsLittleEndian(signal.start, signal.length) + assert.Equal(t, signal.signed, actual) + }) + } + }) + t.Run(fmt.Sprintf("SetUnsignedBits:%v", i), func(t *testing.T) { + var data Data + for j, signal := range tt.signals { + j, signal := j, signal + t.Run(fmt.Sprintf("data:%v", j), func(t *testing.T) { + data.SetUnsignedBitsLittleEndian(signal.start, signal.length, signal.unsigned) + }) + } + assert.DeepEqual(t, tt.data, data) + }) + t.Run(fmt.Sprintf("SetSignedBits:%v", i), func(t *testing.T) { + var data Data + for j, signal := range tt.signals { + j, signal := j, signal + t.Run(fmt.Sprintf("data:%v", j), func(t *testing.T) { + data.SetSignedBitsLittleEndian(signal.start, signal.length, signal.signed) + }) + } + assert.DeepEqual(t, tt.data, data) + }) + } +} + +func TestData_BigEndian(t *testing.T) { + for i, tt := range []struct { + data Data + signals []struct { + start uint8 + length uint8 + unsigned uint64 + signed int64 + } + }{ + { + data: Data{0x3f, 0xf7, 0x0d, 0xc4, 0x0c, 0x93, 0xff, 0xff}, + signals: []struct { + start uint8 + length uint8 + unsigned uint64 + signed int64 + }{ + {start: 7, length: 3, unsigned: 0x1, signed: 1}, + {start: 4, length: 1, unsigned: 0x1, signed: -1}, + {start: 55, length: 16, unsigned: 0xffff, signed: -1}, + {start: 39, length: 16, unsigned: 0xc93, signed: 3219}, + {start: 23, length: 16, unsigned: 0xdc4, signed: 3524}, + {start: 3, length: 12, unsigned: 0xff7, signed: -9}, + }, + }, + { + data: Data{0x3f, 0xe4, 0x0e, 0xb6, 0x0c, 0xba, 0x00, 0x05}, + signals: []struct { + start uint8 + length uint8 + unsigned uint64 + signed int64 + }{ + {start: 7, length: 3, unsigned: 0x1, signed: 1}, + {start: 4, length: 1, unsigned: 0x1, signed: -1}, + {start: 55, length: 16, unsigned: 0x5, signed: 5}, + {start: 39, length: 16, unsigned: 0xcba, signed: 3258}, + {start: 23, length: 16, unsigned: 0xeb6, signed: 3766}, + {start: 3, length: 12, unsigned: 0xfe4, signed: -28}, + }, + }, + { + data: Data{0x30, 0x53, 0x23, 0xe5, 0x0e, 0x11, 0xff, 0xff}, + signals: []struct { + start uint8 + length uint8 + unsigned uint64 + signed int64 + }{ + {start: 7, length: 3, unsigned: 0x1, signed: 1}, + {start: 4, length: 1, unsigned: 0x1, signed: -1}, + {start: 55, length: 16, unsigned: 0xffff, signed: -1}, + {start: 39, length: 16, unsigned: 0xe11, signed: 3601}, + {start: 23, length: 16, unsigned: 0x23e5, signed: 9189}, + {start: 3, length: 12, unsigned: 0x53, signed: 83}, + }, + }, + } { + i, tt := i, tt + t.Run(fmt.Sprintf("UnsignedBits:%v", i), func(t *testing.T) { + for j, signal := range tt.signals { + j, signal := j, signal + t.Run(fmt.Sprintf("signal:%v", j), func(t *testing.T) { + actual := tt.data.UnsignedBitsBigEndian(signal.start, signal.length) + assert.Equal(t, signal.unsigned, actual) + }) + } + }) + t.Run(fmt.Sprintf("SignedBits:%v", i), func(t *testing.T) { + for j, signal := range tt.signals { + j, signal := j, signal + t.Run(fmt.Sprintf("signal:%v", j), func(t *testing.T) { + actual := tt.data.SignedBitsBigEndian(signal.start, signal.length) + assert.Equal(t, signal.signed, actual) + }) + } + }) + t.Run(fmt.Sprintf("SetUnsignedBits:%v", i), func(t *testing.T) { + var data Data + for j, signal := range tt.signals { + j, signal := j, signal + t.Run(fmt.Sprintf("data:%v", j), func(t *testing.T) { + data.SetUnsignedBitsBigEndian(signal.start, signal.length, signal.unsigned) + }) + } + assert.DeepEqual(t, tt.data, data) + }) + t.Run(fmt.Sprintf("SetSignedBits:%v", i), func(t *testing.T) { + var data Data + for j, signal := range tt.signals { + j, signal := j, signal + t.Run(fmt.Sprintf("data:%v", j), func(t *testing.T) { + data.SetSignedBitsBigEndian(signal.start, signal.length, signal.signed) + }) + } + assert.DeepEqual(t, tt.data, data) + }) + } +} + +func TestInvertEndian_Property_Idempotent(t *testing.T) { + for i := uint8(0); i < 64; i++ { + assert.Equal(t, i, invertEndian(invertEndian(i))) + } +} + +func TestPackUnpackBigEndian(t *testing.T) { + f := func(data Data) Data { + return data + } + g := func(data Data) Data { + data.UnpackBigEndian(data.PackBigEndian()) + return data + } + assert.NilError(t, quick.CheckEqual(f, g, nil)) +} + +func TestPackUnpackLittleEndian(t *testing.T) { + f := func(data Data) Data { + return data + } + g := func(data Data) Data { + data.UnpackLittleEndian(data.PackLittleEndian()) + return data + } + assert.NilError(t, quick.CheckEqual(f, g, nil)) +} + +func TestData_CheckBitRange(t *testing.T) { + // example case that big-endian signals and little-endian signals use different indexing + assert.NilError(t, CheckBitRangeBigEndian(8, 55, 16)) + assert.ErrorContains(t, CheckBitRangeLittleEndian(8, 55, 16), "bit range out of bounds") +} + +func BenchmarkData_UnpackLittleEndian(b *testing.B) { + var data Data + for i := 0; i < b.N; i++ { + data.UnpackLittleEndian(0) + } +} + +func BenchmarkData_UnpackBigEndian(b *testing.B) { + var data Data + for i := 0; i < b.N; i++ { + data.UnpackBigEndian(0) + } +} + +func BenchmarkData_PackBigEndian(b *testing.B) { + var data Data + for i := 0; i < b.N; i++ { + _ = data.PackBigEndian() + } +} + +func BenchmarkData_PackLittleEndian(b *testing.B) { + var data Data + for i := 0; i < b.N; i++ { + _ = data.PackLittleEndian() + } +} + +func BenchmarkData_UnsignedBitsBigEndian(b *testing.B) { + var data Data + for i := 0; i < b.N; i++ { + _ = data.UnsignedBitsBigEndian(0, 16) + } +} + +func BenchmarkData_UnsignedBitsLittleEndian(b *testing.B) { + var data Data + for i := 0; i < b.N; i++ { + _ = data.UnsignedBitsLittleEndian(0, 16) + } +} diff --git a/pkg/can-go/frame.go b/pkg/can-go/frame.go new file mode 100644 index 0000000..791739f --- /dev/null +++ b/pkg/can-go/frame.go @@ -0,0 +1,135 @@ +package can + +import ( + "encoding/hex" + "fmt" + "strconv" + "strings" +) + +const ( + idBits = 11 + extendedIDBits = 29 +) + +// CAN format constants. +const ( + MaxID = 0x7ff + MaxExtendedID = 0x1fffffff +) + +// Frame represents a CAN frame. +// +// A Frame is intentionally designed to fit into 16 bytes on common architectures +// and is therefore amenable to pass-by-value and judicious copying. +type Frame struct { + // ID is the CAN ID + ID uint32 + // Length is the number of bytes of data in the frame. + Length uint16 + // Data is the frame data. + Data Data + // IsRemote is true for remote frames. + IsRemote bool + // IsExtended is true for extended frames, i.e. frames with 29-bit IDs. + IsExtended bool +} + +// Validate returns an error if the Frame is not a valid CAN frame. +func (f *Frame) Validate() error { + // Validate: ID + if f.IsExtended && f.ID > MaxExtendedID { + return fmt.Errorf( + "invalid extended CAN id: %v does not fit in %v bits", + f.ID, + extendedIDBits, + ) + } else if !f.IsExtended && f.ID > MaxID { + return fmt.Errorf( + "invalid standard CAN id: %v does not fit in %v bits", + f.ID, + idBits, + ) + } + // Validate: Data + if f.Length > MaxDataLength { + return fmt.Errorf("invalid data length: %v", f.Length) + } + return nil +} + +// String returns an ASCII representation the CAN frame. +// +// Format: +// +// ([0-9A-F]{3}|[0-9A-F]{3})#(R[0-8]?|[0-9A-F]{0,16}) +// +// The format is compatible with the candump(1) log file format. +func (f Frame) String() string { + var id string + if f.IsExtended { + id = fmt.Sprintf("%08X", f.ID) + } else { + id = fmt.Sprintf("%03X", f.ID) + } + if f.IsRemote && f.Length == 0 { + return id + "#R" + } else if f.IsRemote { + return id + "#R" + strconv.Itoa(int(f.Length)) + } + return id + "#" + strings.ToUpper(hex.EncodeToString(f.Data[:f.Length])) +} + +// UnmarshalString sets *f using the provided ASCII representation of a Frame. +func (f *Frame) UnmarshalString(s string) error { + // Split split into parts + parts := strings.Split(s, "#") + if len(parts) != 2 { + return fmt.Errorf("invalid frame format: %v", s) + } + idPart, dataPart := parts[0], parts[1] + var frame Frame + // Parse: IsExtended + if len(idPart) != 3 && len(idPart) != 8 { + return fmt.Errorf("invalid ID length: %v", s) + } + frame.IsExtended = len(idPart) == 8 + // Parse: ID + id, err := strconv.ParseUint(idPart, 16, 32) + if err != nil { + return fmt.Errorf("invalid frame ID: %v", s) + } + frame.ID = uint32(id) + if len(dataPart) == 0 { + *f = frame + return nil + } + // Parse: IsRemote + if dataPart[0] == 'R' { + frame.IsRemote = true + if len(dataPart) > 2 { + return fmt.Errorf("invalid remote length: %v", s) + } else if len(dataPart) == 2 { + dataLength, err := strconv.Atoi(dataPart[1:2]) + if err != nil { + return fmt.Errorf("invalid remote length: %v: %w", s, err) + } + frame.Length = uint16(dataLength) + } + *f = frame + return nil + } + // Parse: Length + if len(dataPart) > 16 || len(dataPart)%2 != 0 { + return fmt.Errorf("invalid data length: %v", s) + } + frame.Length = uint16(len(dataPart) / 2) + // Parse: Data + decodedData, err := hex.DecodeString(dataPart) + if err != nil { + return fmt.Errorf("invalid data: %v: %w", s, err) + } + copy(frame.Data[:], decodedData) + *f = frame + return nil +} diff --git a/pkg/can-go/frame_json.go b/pkg/can-go/frame_json.go new file mode 100644 index 0000000..9c46a7c --- /dev/null +++ b/pkg/can-go/frame_json.go @@ -0,0 +1,95 @@ +package can + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "strconv" +) + +type jsonFrame struct { + ID uint32 `json:"id"` + Data *string `json:"data"` + Length *uint16 `json:"length"` + Extended *bool `json:"extended"` + Remote *bool `json:"remote"` +} + +// JSON returns the JSON-encoding of f, using hex-encoding for the data. +// +// Examples: +// +// {"id":32,"data":"0102030405060708"} +// {"id":32,"extended":true,"remote":true,"length":4} +func (f Frame) JSON() string { + switch { + case f.IsRemote && f.IsExtended: + return `{"id":` + strconv.Itoa(int(f.ID)) + + `,"extended":true,"remote":true,"length":` + + strconv.Itoa(int(f.Length)) + `}` + case f.IsRemote: + return `{"id":` + strconv.Itoa(int(f.ID)) + + `,"remote":true,"length":` + + strconv.Itoa(int(f.Length)) + `}` + case f.IsExtended && f.Length == 0: + return `{"id":` + strconv.Itoa(int(f.ID)) + `,"extended":true}` + case f.IsExtended: + return `{"id":` + strconv.Itoa(int(f.ID)) + + `,"data":"` + hex.EncodeToString(f.Data[:f.Length]) + `"` + + `,"extended":true}` + case f.Length == 0: + return `{"id":` + strconv.Itoa(int(f.ID)) + `}` + default: + return `{"id":` + strconv.Itoa(int(f.ID)) + + `,"data":"` + hex.EncodeToString(f.Data[:f.Length]) + `"}` + } +} + +// MarshalJSON returns the JSON-encoding of f, using hex-encoding for the data. +// +// See JSON for an example of the JSON schema. +func (f Frame) MarshalJSON() ([]byte, error) { + return []byte(f.JSON()), nil +} + +// UnmarshalJSON sets *f using the provided JSON-encoded values. +// +// See MarshalJSON for an example of the expected JSON schema. +// +// The result should be checked with Validate to guard against invalid JSON data. +func (f *Frame) UnmarshalJSON(jsonData []byte) error { + jf := jsonFrame{} + if err := json.Unmarshal(jsonData, &jf); err != nil { + return err + } + if jf.Data != nil { + data, err := hex.DecodeString(*jf.Data) + if err != nil { + return fmt.Errorf("failed to hex-decode CAN data: %v: %w", string(jsonData), err) + } + f.Data = Data{} + copy(f.Data[:], data) + f.Length = uint16(len(data)) + } else { + f.Data = Data{} + f.Length = 0 + } + f.ID = jf.ID + if jf.Remote != nil { + f.IsRemote = *jf.Remote + } else { + f.IsRemote = false + } + if f.IsRemote { + if jf.Length == nil { + return fmt.Errorf("missing length field for remote JSON frame: %v", string(jsonData)) + } + f.Length = *jf.Length + } + if jf.Extended != nil { + f.IsExtended = *jf.Extended + } else { + f.IsExtended = false + } + return nil +} diff --git a/pkg/can-go/frame_json_test.go b/pkg/can-go/frame_json_test.go new file mode 100644 index 0000000..256268f --- /dev/null +++ b/pkg/can-go/frame_json_test.go @@ -0,0 +1,126 @@ +package can + +import ( + "encoding/json" + "fmt" + "math/rand" + "reflect" + "testing" + "testing/quick" + + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestFrame_JSON(t *testing.T) { + for _, tt := range []struct { + jsonFrame string + frame Frame + }{ + { + // Standard frame + jsonFrame: `{"id":42,"data":"00010203"}`, + frame: Frame{ + ID: 42, + Length: 4, + Data: Data{0x00, 0x01, 0x02, 0x03}, + }, + }, + { + // Standard frame, no data + jsonFrame: `{"id":42}`, + frame: Frame{ID: 42}, + }, + { + // Standard remote frame + jsonFrame: `{"id":42,"remote":true,"length":4}`, + frame: Frame{ + ID: 42, + IsRemote: true, + Length: 4, + }, + }, + { + // Extended frame + jsonFrame: `{"id":42,"data":"0001020304050607","extended":true}`, + frame: Frame{ + ID: 42, + IsExtended: true, + Length: 8, + Data: Data{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, + }, + }, + { + // Extended frame, no data + jsonFrame: `{"id":42,"extended":true}`, + frame: Frame{ID: 42, IsExtended: true}, + }, + { + // Extended remote frame + jsonFrame: `{"id":42,"extended":true,"remote":true,"length":8}`, + frame: Frame{ + ID: 42, + IsExtended: true, + IsRemote: true, + Length: 8, + }, + }, + } { + tt := tt + t.Run(fmt.Sprintf("JSON|frame=%v", tt.frame), func(t *testing.T) { + assert.Check(t, is.Equal(tt.jsonFrame, tt.frame.JSON())) + }) + t.Run(fmt.Sprintf("UnmarshalJSON|frame=%v", tt.frame), func(t *testing.T) { + var frame Frame + if err := json.Unmarshal([]byte(tt.jsonFrame), &frame); err != nil { + t.Fatal(err) + } + assert.Check(t, is.DeepEqual(tt.frame, frame)) + }) + } +} + +func TestFrame_UnmarshalJSON_Invalid(t *testing.T) { + var f Frame + t.Run("invalid JSON", func(t *testing.T) { + data := `foobar` + assert.Check(t, f.UnmarshalJSON([]uint8(data)) != nil) + }) + t.Run("invalid payload", func(t *testing.T) { + data := `{"id":1,"data":"foobar","extended":false,"remote":false}` + assert.Check(t, f.UnmarshalJSON([]uint8(data)) != nil) + }) +} + +func (Frame) Generate(rand *rand.Rand, size int) reflect.Value { + f := Frame{ + IsExtended: rand.Intn(2) == 0, + IsRemote: rand.Intn(2) == 0, + } + if f.IsExtended { + f.ID = rand.Uint32() & MaxExtendedID + } else { + f.ID = rand.Uint32() & MaxID + } + f.Length = uint16(rand.Intn(9)) + if !f.IsRemote { + _, _ = rand.Read(f.Data[:f.Length]) + } + return reflect.ValueOf(f) +} + +func TestPropertyFrame_MarshalUnmarshalJSON(t *testing.T) { + f := func(f Frame) Frame { + return f + } + g := func(f Frame) Frame { + f2 := Frame{} + if err := json.Unmarshal([]uint8(f.JSON()), &f2); err != nil { + t.Fatal(err) + } + return f2 + } + if err := quick.CheckEqual(f, g, nil); err != nil { + t.Fatal(err) + } +} diff --git a/pkg/can-go/frame_string_test.go b/pkg/can-go/frame_string_test.go new file mode 100644 index 0000000..fe6896f --- /dev/null +++ b/pkg/can-go/frame_string_test.go @@ -0,0 +1,86 @@ +package can + +import ( + "fmt" + "testing" + + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestFrame_String(t *testing.T) { + for _, tt := range []struct { + frame Frame + str string + }{ + { + frame: Frame{ + ID: 0x62e, + Length: 2, + Data: Data{0x10, 0x44}, + }, + str: "62E#1044", + }, + { + frame: Frame{ + ID: 0x410, + IsRemote: true, + Length: 3, + }, + str: "410#R3", + }, + { + frame: Frame{ + ID: 0xd2, + Length: 2, + Data: Data{0xf0, 0x31}, + }, + str: "0D2#F031", + }, + { + frame: Frame{ID: 0xee}, + str: "0EE#", + }, + { + frame: Frame{ID: 0}, + str: "000#", + }, + { + frame: Frame{ID: 0, IsExtended: true}, + str: "00000000#", + }, + { + frame: Frame{ID: 0x1234abcd, IsExtended: true}, + str: "1234ABCD#", + }, + } { + tt := tt + t.Run(fmt.Sprintf("String|frame=%v,str=%v", tt.frame, tt.str), func(t *testing.T) { + assert.Check(t, is.Equal(tt.str, tt.frame.String())) + }) + t.Run(fmt.Sprintf("UnmarshalString|frame=%v,str=%v", tt.frame, tt.str), func(t *testing.T) { + var actual Frame + if err := actual.UnmarshalString(tt.str); err != nil { + t.Fatal(err) + } + assert.Check(t, is.DeepEqual(actual, tt.frame)) + }) + } +} + +func TestParseFrame_Errors(t *testing.T) { + for _, tt := range []string{ + "foo", // invalid + "foo#", // invalid ID + "0D23#F031", // invalid ID length + "62E#104400000000000000", // invalid data length + } { + tt := tt + t.Run(fmt.Sprintf("str=%v", tt), func(t *testing.T) { + var frame Frame + err := frame.UnmarshalString(tt) + assert.ErrorContains(t, err, "invalid") + assert.Check(t, is.DeepEqual(Frame{}, frame)) + }) + } +} diff --git a/pkg/can-go/frame_test.go b/pkg/can-go/frame_test.go new file mode 100644 index 0000000..950965f --- /dev/null +++ b/pkg/can-go/frame_test.go @@ -0,0 +1,27 @@ +package can + +import ( + "fmt" + "testing" + "unsafe" + + "gotest.tools/v3/assert" +) + +// If this mocks ever starts failing, the documentation needs to be updated +// to prefer pass-by-pointer over pass-by-value. +func TestFrame_Size(t *testing.T) { + assert.Assert(t, unsafe.Sizeof(Frame{}) <= 16, "Frame size is <= 16 bytes") +} + +func TestFrame_Validate_Error(t *testing.T) { + for _, tt := range []Frame{ + {ID: MaxID + 1}, + {ID: MaxExtendedID + 1, IsExtended: true}, + } { + tt := tt + t.Run(fmt.Sprintf("%v", tt), func(t *testing.T) { + assert.Check(t, tt.Validate() != nil, "should return validation error") + }) + } +} diff --git a/pkg/can-go/go.mod b/pkg/can-go/go.mod new file mode 100644 index 0000000..6b8b993 --- /dev/null +++ b/pkg/can-go/go.mod @@ -0,0 +1,29 @@ +module github.com/fiskerinc/cloud-services/pkg/can-go + +go 1.24.0 + +require ( + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc + github.com/fatih/color v1.15.0 + github.com/golang/mock v1.7.0-rc.1 + github.com/shurcooL/go-goon v0.0.0-20210110234559-7585751d9a17 + go.uber.org/goleak v1.3.0 + golang.org/x/net v0.47.0 + golang.org/x/sync v0.18.0 + golang.org/x/sys v0.38.0 + golang.org/x/tools v0.38.0 + gopkg.in/alecthomas/kingpin.v2 v2.2.6 + gotest.tools/v3 v3.5.1 +) + +require ( + github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect + github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636 // indirect + github.com/stretchr/testify v1.10.0 // indirect + golang.org/x/mod v0.29.0 // indirect +) diff --git a/pkg/can-go/go.sum b/pkg/can-go/go.sum new file mode 100644 index 0000000..4547f6d --- /dev/null +++ b/pkg/can-go/go.sum @@ -0,0 +1,45 @@ +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 h1:AUNCr9CiJuwrRYS3XieqF+Z9B9gNxo/eANAJCF2eiN4= +github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636 h1:aSISeOcal5irEhJd1M+IrApc0PdcN7e7Aj4yuEnOrfQ= +github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20210110234559-7585751d9a17 h1:lRAUE0dIvigSSFAmaM2dfg7OH8T+a8zJ5smEh09a/GI= +github.com/shurcooL/go-goon v0.0.0-20210110234559-7585751d9a17/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= diff --git a/pkg/can-go/internal/clock/clock.go b/pkg/can-go/internal/clock/clock.go new file mode 100644 index 0000000..f95393c --- /dev/null +++ b/pkg/can-go/internal/clock/clock.go @@ -0,0 +1,27 @@ +// Package clock provides primitives for mocking time. +package clock + +import ( + "time" +) + +// Clock provides capabilities from the time standard library package. +type Clock interface { + // After waits for the duration to elapse and then sends the current time on the returned channel. + After(duration time.Duration) <-chan time.Time + + // NewTicker returns a new Ticker. + NewTicker(d time.Duration) Ticker + + // Now returns the current local time. + Now() time.Time +} + +// Ticker wraps the time.Ticker class. +type Ticker interface { + // C returns the channel on which the ticks are delivered. + C() <-chan time.Time + + // Stop the Ticker. + Stop() +} diff --git a/pkg/can-go/internal/clock/system.go b/pkg/can-go/internal/clock/system.go new file mode 100644 index 0000000..f824a13 --- /dev/null +++ b/pkg/can-go/internal/clock/system.go @@ -0,0 +1,34 @@ +package clock + +import ( + "time" +) + +// System returns a Clock implementation that delegate to the time package. +func System() Clock { + return &systemClock{} +} + +type systemClock struct{} + +var _ Clock = &systemClock{} + +func (c systemClock) After(d time.Duration) <-chan time.Time { + return time.After(d) +} + +func (c systemClock) NewTicker(d time.Duration) Ticker { + return &systemTicker{Ticker: *time.NewTicker(d)} +} + +func (c systemClock) Now() time.Time { + return time.Now() +} + +type systemTicker struct { + time.Ticker +} + +func (t systemTicker) C() <-chan time.Time { + return t.Ticker.C +} diff --git a/pkg/can-go/internal/gen/mock/mockcanrunner/mocks.go b/pkg/can-go/internal/gen/mock/mockcanrunner/mocks.go new file mode 100644 index 0000000..7ac9353 --- /dev/null +++ b/pkg/can-go/internal/gen/mock/mockcanrunner/mocks.go @@ -0,0 +1,529 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/fiskerinc/cloud-services/pkg/can-go/pkg/canrunner (interfaces: Node,TransmittedMessage,ReceivedMessage,FrameTransmitter,FrameReceiver) + +// Package mockcanrunner is a generated GoMock package. +package mockcanrunner + +import ( + context "context" + can "github.com/fiskerinc/cloud-services/pkg/can-go" + canrunner "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/canrunner" + descriptor "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" + gomock "github.com/golang/mock/gomock" + net "net" + reflect "reflect" + time "time" +) + +// MockNode is a mock of Node interface. +type MockNode struct { + ctrl *gomock.Controller + recorder *MockNodeMockRecorder +} + +// MockNodeMockRecorder is the mock recorder for MockNode. +type MockNodeMockRecorder struct { + mock *MockNode +} + +// NewMockNode creates a new mock instance. +func NewMockNode(ctrl *gomock.Controller) *MockNode { + mock := &MockNode{ctrl: ctrl} + mock.recorder = &MockNodeMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockNode) EXPECT() *MockNodeMockRecorder { + return m.recorder +} + +// Connect mocks base method. +func (m *MockNode) Connect() (net.Conn, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Connect") + ret0, _ := ret[0].(net.Conn) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Connect indicates an expected call of Connect. +func (mr *MockNodeMockRecorder) Connect() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connect", reflect.TypeOf((*MockNode)(nil).Connect)) +} + +// Descriptor mocks base method. +func (m *MockNode) Descriptor() *descriptor.Node { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Descriptor") + ret0, _ := ret[0].(*descriptor.Node) + return ret0 +} + +// Descriptor indicates an expected call of Descriptor. +func (mr *MockNodeMockRecorder) Descriptor() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Descriptor", reflect.TypeOf((*MockNode)(nil).Descriptor)) +} + +// Lock mocks base method. +func (m *MockNode) Lock() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Lock") +} + +// Lock indicates an expected call of Lock. +func (mr *MockNodeMockRecorder) Lock() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Lock", reflect.TypeOf((*MockNode)(nil).Lock)) +} + +// ReceivedMessage mocks base method. +func (m *MockNode) ReceivedMessage(arg0 uint32) (canrunner.ReceivedMessage, bool) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReceivedMessage", arg0) + ret0, _ := ret[0].(canrunner.ReceivedMessage) + ret1, _ := ret[1].(bool) + return ret0, ret1 +} + +// ReceivedMessage indicates an expected call of ReceivedMessage. +func (mr *MockNodeMockRecorder) ReceivedMessage(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedMessage", reflect.TypeOf((*MockNode)(nil).ReceivedMessage), arg0) +} + +// TransmittedMessages mocks base method. +func (m *MockNode) TransmittedMessages() []canrunner.TransmittedMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TransmittedMessages") + ret0, _ := ret[0].([]canrunner.TransmittedMessage) + return ret0 +} + +// TransmittedMessages indicates an expected call of TransmittedMessages. +func (mr *MockNodeMockRecorder) TransmittedMessages() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransmittedMessages", reflect.TypeOf((*MockNode)(nil).TransmittedMessages)) +} + +// Unlock mocks base method. +func (m *MockNode) Unlock() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Unlock") +} + +// Unlock indicates an expected call of Unlock. +func (mr *MockNodeMockRecorder) Unlock() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unlock", reflect.TypeOf((*MockNode)(nil).Unlock)) +} + +// MockTransmittedMessage is a mock of TransmittedMessage interface. +type MockTransmittedMessage struct { + ctrl *gomock.Controller + recorder *MockTransmittedMessageMockRecorder +} + +// MockTransmittedMessageMockRecorder is the mock recorder for MockTransmittedMessage. +type MockTransmittedMessageMockRecorder struct { + mock *MockTransmittedMessage +} + +// NewMockTransmittedMessage creates a new mock instance. +func NewMockTransmittedMessage(ctrl *gomock.Controller) *MockTransmittedMessage { + mock := &MockTransmittedMessage{ctrl: ctrl} + mock.recorder = &MockTransmittedMessageMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockTransmittedMessage) EXPECT() *MockTransmittedMessageMockRecorder { + return m.recorder +} + +// BeforeTransmitHook mocks base method. +func (m *MockTransmittedMessage) BeforeTransmitHook() func(context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BeforeTransmitHook") + ret0, _ := ret[0].(func(context.Context) error) + return ret0 +} + +// BeforeTransmitHook indicates an expected call of BeforeTransmitHook. +func (mr *MockTransmittedMessageMockRecorder) BeforeTransmitHook() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeforeTransmitHook", reflect.TypeOf((*MockTransmittedMessage)(nil).BeforeTransmitHook)) +} + +// Descriptor mocks base method. +func (m *MockTransmittedMessage) Descriptor() *descriptor.Message { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Descriptor") + ret0, _ := ret[0].(*descriptor.Message) + return ret0 +} + +// Descriptor indicates an expected call of Descriptor. +func (mr *MockTransmittedMessageMockRecorder) Descriptor() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Descriptor", reflect.TypeOf((*MockTransmittedMessage)(nil).Descriptor)) +} + +// Frame mocks base method. +func (m *MockTransmittedMessage) Frame() can.Frame { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Frame") + ret0, _ := ret[0].(can.Frame) + return ret0 +} + +// Frame indicates an expected call of Frame. +func (mr *MockTransmittedMessageMockRecorder) Frame() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Frame", reflect.TypeOf((*MockTransmittedMessage)(nil).Frame)) +} + +// IsCyclicTransmissionEnabled mocks base method. +func (m *MockTransmittedMessage) IsCyclicTransmissionEnabled() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsCyclicTransmissionEnabled") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsCyclicTransmissionEnabled indicates an expected call of IsCyclicTransmissionEnabled. +func (mr *MockTransmittedMessageMockRecorder) IsCyclicTransmissionEnabled() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsCyclicTransmissionEnabled", reflect.TypeOf((*MockTransmittedMessage)(nil).IsCyclicTransmissionEnabled)) +} + +// MarshalFrame mocks base method. +func (m *MockTransmittedMessage) MarshalFrame() (can.Frame, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MarshalFrame") + ret0, _ := ret[0].(can.Frame) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// MarshalFrame indicates an expected call of MarshalFrame. +func (mr *MockTransmittedMessageMockRecorder) MarshalFrame() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarshalFrame", reflect.TypeOf((*MockTransmittedMessage)(nil).MarshalFrame)) +} + +// Reset mocks base method. +func (m *MockTransmittedMessage) Reset() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Reset") +} + +// Reset indicates an expected call of Reset. +func (mr *MockTransmittedMessageMockRecorder) Reset() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reset", reflect.TypeOf((*MockTransmittedMessage)(nil).Reset)) +} + +// SetTransmitTime mocks base method. +func (m *MockTransmittedMessage) SetTransmitTime(arg0 time.Time) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetTransmitTime", arg0) +} + +// SetTransmitTime indicates an expected call of SetTransmitTime. +func (mr *MockTransmittedMessageMockRecorder) SetTransmitTime(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTransmitTime", reflect.TypeOf((*MockTransmittedMessage)(nil).SetTransmitTime), arg0) +} + +// String mocks base method. +func (m *MockTransmittedMessage) String() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "String") + ret0, _ := ret[0].(string) + return ret0 +} + +// String indicates an expected call of String. +func (mr *MockTransmittedMessageMockRecorder) String() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockTransmittedMessage)(nil).String)) +} + +// TransmitEventChan mocks base method. +func (m *MockTransmittedMessage) TransmitEventChan() <-chan struct{} { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TransmitEventChan") + ret0, _ := ret[0].(<-chan struct{}) + return ret0 +} + +// TransmitEventChan indicates an expected call of TransmitEventChan. +func (mr *MockTransmittedMessageMockRecorder) TransmitEventChan() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransmitEventChan", reflect.TypeOf((*MockTransmittedMessage)(nil).TransmitEventChan)) +} + +// UnmarshalFrame mocks base method. +func (m *MockTransmittedMessage) UnmarshalFrame(arg0 can.Frame) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnmarshalFrame", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// UnmarshalFrame indicates an expected call of UnmarshalFrame. +func (mr *MockTransmittedMessageMockRecorder) UnmarshalFrame(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnmarshalFrame", reflect.TypeOf((*MockTransmittedMessage)(nil).UnmarshalFrame), arg0) +} + +// WakeUpChan mocks base method. +func (m *MockTransmittedMessage) WakeUpChan() <-chan struct{} { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WakeUpChan") + ret0, _ := ret[0].(<-chan struct{}) + return ret0 +} + +// WakeUpChan indicates an expected call of WakeUpChan. +func (mr *MockTransmittedMessageMockRecorder) WakeUpChan() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WakeUpChan", reflect.TypeOf((*MockTransmittedMessage)(nil).WakeUpChan)) +} + +// MockReceivedMessage is a mock of ReceivedMessage interface. +type MockReceivedMessage struct { + ctrl *gomock.Controller + recorder *MockReceivedMessageMockRecorder +} + +// MockReceivedMessageMockRecorder is the mock recorder for MockReceivedMessage. +type MockReceivedMessageMockRecorder struct { + mock *MockReceivedMessage +} + +// NewMockReceivedMessage creates a new mock instance. +func NewMockReceivedMessage(ctrl *gomock.Controller) *MockReceivedMessage { + mock := &MockReceivedMessage{ctrl: ctrl} + mock.recorder = &MockReceivedMessageMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockReceivedMessage) EXPECT() *MockReceivedMessageMockRecorder { + return m.recorder +} + +// AfterReceiveHook mocks base method. +func (m *MockReceivedMessage) AfterReceiveHook() func(context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AfterReceiveHook") + ret0, _ := ret[0].(func(context.Context) error) + return ret0 +} + +// AfterReceiveHook indicates an expected call of AfterReceiveHook. +func (mr *MockReceivedMessageMockRecorder) AfterReceiveHook() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AfterReceiveHook", reflect.TypeOf((*MockReceivedMessage)(nil).AfterReceiveHook)) +} + +// Descriptor mocks base method. +func (m *MockReceivedMessage) Descriptor() *descriptor.Message { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Descriptor") + ret0, _ := ret[0].(*descriptor.Message) + return ret0 +} + +// Descriptor indicates an expected call of Descriptor. +func (mr *MockReceivedMessageMockRecorder) Descriptor() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Descriptor", reflect.TypeOf((*MockReceivedMessage)(nil).Descriptor)) +} + +// Frame mocks base method. +func (m *MockReceivedMessage) Frame() can.Frame { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Frame") + ret0, _ := ret[0].(can.Frame) + return ret0 +} + +// Frame indicates an expected call of Frame. +func (mr *MockReceivedMessageMockRecorder) Frame() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Frame", reflect.TypeOf((*MockReceivedMessage)(nil).Frame)) +} + +// MarshalFrame mocks base method. +func (m *MockReceivedMessage) MarshalFrame() (can.Frame, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MarshalFrame") + ret0, _ := ret[0].(can.Frame) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// MarshalFrame indicates an expected call of MarshalFrame. +func (mr *MockReceivedMessageMockRecorder) MarshalFrame() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarshalFrame", reflect.TypeOf((*MockReceivedMessage)(nil).MarshalFrame)) +} + +// Reset mocks base method. +func (m *MockReceivedMessage) Reset() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Reset") +} + +// Reset indicates an expected call of Reset. +func (mr *MockReceivedMessageMockRecorder) Reset() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reset", reflect.TypeOf((*MockReceivedMessage)(nil).Reset)) +} + +// SetReceiveTime mocks base method. +func (m *MockReceivedMessage) SetReceiveTime(arg0 time.Time) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetReceiveTime", arg0) +} + +// SetReceiveTime indicates an expected call of SetReceiveTime. +func (mr *MockReceivedMessageMockRecorder) SetReceiveTime(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetReceiveTime", reflect.TypeOf((*MockReceivedMessage)(nil).SetReceiveTime), arg0) +} + +// String mocks base method. +func (m *MockReceivedMessage) String() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "String") + ret0, _ := ret[0].(string) + return ret0 +} + +// String indicates an expected call of String. +func (mr *MockReceivedMessageMockRecorder) String() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockReceivedMessage)(nil).String)) +} + +// UnmarshalFrame mocks base method. +func (m *MockReceivedMessage) UnmarshalFrame(arg0 can.Frame) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnmarshalFrame", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// UnmarshalFrame indicates an expected call of UnmarshalFrame. +func (mr *MockReceivedMessageMockRecorder) UnmarshalFrame(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnmarshalFrame", reflect.TypeOf((*MockReceivedMessage)(nil).UnmarshalFrame), arg0) +} + +// MockFrameTransmitter is a mock of FrameTransmitter interface. +type MockFrameTransmitter struct { + ctrl *gomock.Controller + recorder *MockFrameTransmitterMockRecorder +} + +// MockFrameTransmitterMockRecorder is the mock recorder for MockFrameTransmitter. +type MockFrameTransmitterMockRecorder struct { + mock *MockFrameTransmitter +} + +// NewMockFrameTransmitter creates a new mock instance. +func NewMockFrameTransmitter(ctrl *gomock.Controller) *MockFrameTransmitter { + mock := &MockFrameTransmitter{ctrl: ctrl} + mock.recorder = &MockFrameTransmitterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockFrameTransmitter) EXPECT() *MockFrameTransmitterMockRecorder { + return m.recorder +} + +// TransmitFrame mocks base method. +func (m *MockFrameTransmitter) TransmitFrame(arg0 context.Context, arg1 can.Frame) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TransmitFrame", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// TransmitFrame indicates an expected call of TransmitFrame. +func (mr *MockFrameTransmitterMockRecorder) TransmitFrame(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransmitFrame", reflect.TypeOf((*MockFrameTransmitter)(nil).TransmitFrame), arg0, arg1) +} + +// MockFrameReceiver is a mock of FrameReceiver interface. +type MockFrameReceiver struct { + ctrl *gomock.Controller + recorder *MockFrameReceiverMockRecorder +} + +// MockFrameReceiverMockRecorder is the mock recorder for MockFrameReceiver. +type MockFrameReceiverMockRecorder struct { + mock *MockFrameReceiver +} + +// NewMockFrameReceiver creates a new mock instance. +func NewMockFrameReceiver(ctrl *gomock.Controller) *MockFrameReceiver { + mock := &MockFrameReceiver{ctrl: ctrl} + mock.recorder = &MockFrameReceiverMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockFrameReceiver) EXPECT() *MockFrameReceiverMockRecorder { + return m.recorder +} + +// Err mocks base method. +func (m *MockFrameReceiver) Err() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Err") + ret0, _ := ret[0].(error) + return ret0 +} + +// Err indicates an expected call of Err. +func (mr *MockFrameReceiverMockRecorder) Err() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Err", reflect.TypeOf((*MockFrameReceiver)(nil).Err)) +} + +// Frame mocks base method. +func (m *MockFrameReceiver) Frame() can.Frame { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Frame") + ret0, _ := ret[0].(can.Frame) + return ret0 +} + +// Frame indicates an expected call of Frame. +func (mr *MockFrameReceiverMockRecorder) Frame() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Frame", reflect.TypeOf((*MockFrameReceiver)(nil).Frame)) +} + +// Receive mocks base method. +func (m *MockFrameReceiver) Receive() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Receive") + ret0, _ := ret[0].(bool) + return ret0 +} + +// Receive indicates an expected call of Receive. +func (mr *MockFrameReceiverMockRecorder) Receive() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Receive", reflect.TypeOf((*MockFrameReceiver)(nil).Receive)) +} diff --git a/pkg/can-go/internal/gen/mock/mockclock/mocks.go b/pkg/can-go/internal/gen/mock/mockclock/mocks.go new file mode 100644 index 0000000..540e58d --- /dev/null +++ b/pkg/can-go/internal/gen/mock/mockclock/mocks.go @@ -0,0 +1,126 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/fiskerinc/cloud-services/pkg/can-go/internal/clock (interfaces: Clock,Ticker) + +// Package mockclock is a generated GoMock package. +package mockclock + +import ( + clock "github.com/fiskerinc/cloud-services/pkg/can-go/internal/clock" + gomock "github.com/golang/mock/gomock" + reflect "reflect" + time "time" +) + +// MockClock is a mock of Clock interface. +type MockClock struct { + ctrl *gomock.Controller + recorder *MockClockMockRecorder +} + +// MockClockMockRecorder is the mock recorder for MockClock. +type MockClockMockRecorder struct { + mock *MockClock +} + +// NewMockClock creates a new mock instance. +func NewMockClock(ctrl *gomock.Controller) *MockClock { + mock := &MockClock{ctrl: ctrl} + mock.recorder = &MockClockMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClock) EXPECT() *MockClockMockRecorder { + return m.recorder +} + +// After mocks base method. +func (m *MockClock) After(arg0 time.Duration) <-chan time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "After", arg0) + ret0, _ := ret[0].(<-chan time.Time) + return ret0 +} + +// After indicates an expected call of After. +func (mr *MockClockMockRecorder) After(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "After", reflect.TypeOf((*MockClock)(nil).After), arg0) +} + +// NewTicker mocks base method. +func (m *MockClock) NewTicker(arg0 time.Duration) clock.Ticker { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewTicker", arg0) + ret0, _ := ret[0].(clock.Ticker) + return ret0 +} + +// NewTicker indicates an expected call of NewTicker. +func (mr *MockClockMockRecorder) NewTicker(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewTicker", reflect.TypeOf((*MockClock)(nil).NewTicker), arg0) +} + +// Now mocks base method. +func (m *MockClock) Now() time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Now") + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// Now indicates an expected call of Now. +func (mr *MockClockMockRecorder) Now() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Now", reflect.TypeOf((*MockClock)(nil).Now)) +} + +// MockTicker is a mock of Ticker interface. +type MockTicker struct { + ctrl *gomock.Controller + recorder *MockTickerMockRecorder +} + +// MockTickerMockRecorder is the mock recorder for MockTicker. +type MockTickerMockRecorder struct { + mock *MockTicker +} + +// NewMockTicker creates a new mock instance. +func NewMockTicker(ctrl *gomock.Controller) *MockTicker { + mock := &MockTicker{ctrl: ctrl} + mock.recorder = &MockTickerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockTicker) EXPECT() *MockTickerMockRecorder { + return m.recorder +} + +// C mocks base method. +func (m *MockTicker) C() <-chan time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "C") + ret0, _ := ret[0].(<-chan time.Time) + return ret0 +} + +// C indicates an expected call of C. +func (mr *MockTickerMockRecorder) C() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "C", reflect.TypeOf((*MockTicker)(nil).C)) +} + +// Stop mocks base method. +func (m *MockTicker) Stop() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Stop") +} + +// Stop indicates an expected call of Stop. +func (mr *MockTickerMockRecorder) Stop() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockTicker)(nil).Stop)) +} diff --git a/pkg/can-go/internal/gen/mock/mocksocketcan/mocks.go b/pkg/can-go/internal/gen/mock/mocksocketcan/mocks.go new file mode 100644 index 0000000..f8cabad --- /dev/null +++ b/pkg/can-go/internal/gen/mock/mocksocketcan/mocks.go @@ -0,0 +1,120 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: pkg/socketcan/fileconn.go + +// Package mocksocketcan is a generated GoMock package. +package mocksocketcan + +import ( + gomock "github.com/golang/mock/gomock" + reflect "reflect" + time "time" +) + +// Mockfile is a mock of file interface. +type Mockfile struct { + ctrl *gomock.Controller + recorder *MockfileMockRecorder +} + +// MockfileMockRecorder is the mock recorder for Mockfile. +type MockfileMockRecorder struct { + mock *Mockfile +} + +// NewMockfile creates a new mock instance. +func NewMockfile(ctrl *gomock.Controller) *Mockfile { + mock := &Mockfile{ctrl: ctrl} + mock.recorder = &MockfileMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *Mockfile) EXPECT() *MockfileMockRecorder { + return m.recorder +} + +// Read mocks base method. +func (m *Mockfile) Read(arg0 []byte) (int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Read", arg0) + ret0, _ := ret[0].(int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Read indicates an expected call of Read. +func (mr *MockfileMockRecorder) Read(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*Mockfile)(nil).Read), arg0) +} + +// Write mocks base method. +func (m *Mockfile) Write(arg0 []byte) (int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Write", arg0) + ret0, _ := ret[0].(int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Write indicates an expected call of Write. +func (mr *MockfileMockRecorder) Write(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*Mockfile)(nil).Write), arg0) +} + +// SetDeadline mocks base method. +func (m *Mockfile) SetDeadline(arg0 time.Time) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetDeadline", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetDeadline indicates an expected call of SetDeadline. +func (mr *MockfileMockRecorder) SetDeadline(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDeadline", reflect.TypeOf((*Mockfile)(nil).SetDeadline), arg0) +} + +// SetReadDeadline mocks base method. +func (m *Mockfile) SetReadDeadline(arg0 time.Time) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetReadDeadline", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetReadDeadline indicates an expected call of SetReadDeadline. +func (mr *MockfileMockRecorder) SetReadDeadline(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetReadDeadline", reflect.TypeOf((*Mockfile)(nil).SetReadDeadline), arg0) +} + +// SetWriteDeadline mocks base method. +func (m *Mockfile) SetWriteDeadline(arg0 time.Time) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetWriteDeadline", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetWriteDeadline indicates an expected call of SetWriteDeadline. +func (mr *MockfileMockRecorder) SetWriteDeadline(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWriteDeadline", reflect.TypeOf((*Mockfile)(nil).SetWriteDeadline), arg0) +} + +// Close mocks base method. +func (m *Mockfile) Close() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close") + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockfileMockRecorder) Close() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*Mockfile)(nil).Close)) +} diff --git a/pkg/can-go/internal/generate/compile.go b/pkg/can-go/internal/generate/compile.go new file mode 100644 index 0000000..c5d28c7 --- /dev/null +++ b/pkg/can-go/internal/generate/compile.go @@ -0,0 +1,231 @@ +package generate + +import ( + "crypto/sha256" + "fmt" + "regexp" + "sort" + "time" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" +) + +type CompileResult struct { + Hash string + Database *descriptor.Database + Warnings []error +} + +func Compile(sourceFile string, data []byte) (result *CompileResult, err error) { + p := dbc.NewParser(sourceFile, data) + if err := p.Parse(); err != nil { + return nil, fmt.Errorf("failed to parse DBC source file: %w", err) + } + defs := p.Defs() + c := &compiler{ + db: &descriptor.Database{SourceFile: sourceFile, ECUs: make(map[string]bool)}, + defs: defs, + } + c.collectDescriptors() + c.addMetadata() + c.sortDescriptors() + + hash := ComputeHash(data) + return &CompileResult{Hash: hash, Database: c.db, Warnings: c.warnings}, nil +} + +type compileError struct { + def dbc.Def + reason string +} + +func (e *compileError) Error() string { + return fmt.Sprintf("failed to compile: %v (%v)", e.reason, e.def) +} + +type compiler struct { + db *descriptor.Database + defs []dbc.Def + warnings []error +} + +func (c *compiler) addWarning(warning error) { + c.warnings = append(c.warnings, warning) +} + +func (c *compiler) collectDescriptors() { + // find ECU names from messages + re := regexp.MustCompile(`^[0-9A-Za-z]+`) + + for _, def := range c.defs { + switch def := def.(type) { + case *dbc.VersionDef: + c.db.Version = def.Version + case *dbc.MessageDef: + if def.MessageID == dbc.IndependentSignalsMessageID { + continue // don't compile + } + + // add ECU names to set + ecu := re.FindString(string(def.Name)) + if ecu != "" { + c.db.ECUs[ecu] = true + } + + message := &descriptor.Message{ + Name: string(def.Name), + ID: def.MessageID.ToCAN(), + IsExtended: def.MessageID.IsExtended(), + Length: uint16(def.Size), + SenderNode: string(def.Transmitter), + } + for _, signalDef := range def.Signals { + signal := &descriptor.Signal{ + Name: string(signalDef.Name), + IsBigEndian: signalDef.IsBigEndian, + IsSigned: signalDef.IsSigned, + IsMultiplexer: signalDef.IsMultiplexerSwitch, + IsMultiplexed: signalDef.IsMultiplexed, + MultiplexerValue: uint(signalDef.MultiplexerSwitch), + Start: uint16(signalDef.StartBit), + Length: uint16(signalDef.Size), + Scale: signalDef.Factor, + Offset: signalDef.Offset, + Min: signalDef.Minimum, + Max: signalDef.Maximum, + Unit: signalDef.Unit, + } + for _, receiver := range signalDef.Receivers { + signal.ReceiverNodes = append(signal.ReceiverNodes, string(receiver)) + } + message.Signals = append(message.Signals, signal) + } + c.db.Messages[message.ID] = message + case *dbc.NodesDef: + for _, node := range def.NodeNames { + c.db.Nodes = append(c.db.Nodes, &descriptor.Node{Name: string(node)}) + } + } + } +} + +func (c *compiler) addMetadata() { + for _, def := range c.defs { + switch def := def.(type) { + case *dbc.CommentDef: + switch def.ObjectType { + case dbc.ObjectTypeMessage: + if def.MessageID == dbc.IndependentSignalsMessageID { + continue // don't compile + } + message, ok := c.db.Message(def.MessageID.ToCAN()) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared message"}) + continue + } + message.Description = def.Comment + case dbc.ObjectTypeSignal: + if def.MessageID == dbc.IndependentSignalsMessageID { + continue // don't compile + } + signal, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName)) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared signal"}) + continue + } + signal.Description = def.Comment + case dbc.ObjectTypeNetworkNode: + node, ok := c.db.Node(string(def.NodeName)) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared node"}) + continue + } + node.Description = def.Comment + } + case *dbc.ValueDescriptionsDef: + if def.MessageID == dbc.IndependentSignalsMessageID { + continue // don't compile + } + if def.ObjectType != dbc.ObjectTypeSignal { + continue // don't compile + } + signal, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName)) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared signal"}) + continue + } + for _, valueDescription := range def.ValueDescriptions { + signal.ValueDescriptions = append(signal.ValueDescriptions, &descriptor.ValueDescription{ + Description: valueDescription.Description, + Value: int(valueDescription.Value), + }) + } + case *dbc.AttributeValueForObjectDef: + switch def.ObjectType { + case dbc.ObjectTypeMessage: + msg, ok := c.db.Message(def.MessageID.ToCAN()) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared message"}) + continue + } + switch def.AttributeName { + case "GenMsgSendType": + if err := msg.SendType.UnmarshalString(def.StringValue); err != nil { + c.addWarning(&compileError{def: def, reason: err.Error()}) + continue + } + case "GenMsgCycleTime": + msg.CycleTime = time.Duration(def.IntValue) * time.Millisecond + case "GenMsgDelayTime": + msg.DelayTime = time.Duration(def.IntValue) * time.Millisecond + } + case dbc.ObjectTypeSignal: + sig, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName)) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared signal"}) + } + if def.AttributeName == "GenSigStartValue" { + sig.DefaultValue = int(def.IntValue) + } + } + } + } +} + +func (c *compiler) sortDescriptors() { + // Sort nodes by name + sort.Slice(c.db.Nodes, func(i, j int) bool { + return c.db.Nodes[i].Name < c.db.Nodes[j].Name + }) + // Sort messages by ID + // sort.Slice(c.db.Messages, func(i, j int) bool { + // return c.db.Messages[i].ID < c.db.Messages[j].ID + // }) + for _, m := range c.db.Messages { + if m == nil { + continue + } + m := m + // Sort signals by start (and multiplexer value) + sort.Slice(m.Signals, func(j, k int) bool { + if m.Signals[j].MultiplexerValue < m.Signals[k].MultiplexerValue { + return true + } + return m.Signals[j].Start < m.Signals[k].Start + }) + // Sort value descriptions by value + for _, s := range m.Signals { + s := s + sort.Slice(s.ValueDescriptions, func(k, l int) bool { + return s.ValueDescriptions[k].Value < s.ValueDescriptions[l].Value + }) + } + } +} + +func ComputeHash(input []byte) string { + h := sha256.New() + h.Write(input) + return fmt.Sprintf("%x", h.Sum(nil)) +} diff --git a/pkg/can-go/internal/generate/compile_test.go b/pkg/can-go/internal/generate/compile_test.go new file mode 100644 index 0000000..16e056b --- /dev/null +++ b/pkg/can-go/internal/generate/compile_test.go @@ -0,0 +1,306 @@ +package generate + +import ( + "io/ioutil" + "testing" + "time" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" + examplecan "github.com/fiskerinc/cloud-services/pkg/can-go/testdata/gen/go/example" + "gotest.tools/v3/assert" +) + +func TestCompile_ExampleDBC(t *testing.T) { + finish := runTestInDir(t, "../..") + defer finish() + const exampleDBCFile = "testdata/dbc/example/example.dbc" + exampleDatabase := &descriptor.Database{ + SourceFile: exampleDBCFile, + Version: "", + Nodes: []*descriptor.Node{ + { + Name: "DBG", + }, + { + Name: "DRIVER", + Description: "The driver controller driving the car", + }, + { + Name: "IO", + }, + { + Name: "MOTOR", + Description: "The motor controller of the car", + }, + { + Name: "SENSOR", + Description: "The sensor controller of the car", + }, + }, + } + + exampleDatabase.Messages[1] = &descriptor.Message{ + ID: 1, + Name: "EmptyMessage", + SenderNode: "DBG", + } + + exampleDatabase.Messages[100] = &descriptor.Message{ + ID: 100, + Name: "DriverHeartbeat", + Length: 1, + SenderNode: "DRIVER", + Description: "Sync message used to synchronize the controllers", + SendType: descriptor.SendTypeCyclic, + CycleTime: time.Second, + Signals: []*descriptor.Signal{ + { + Name: "Command", + Start: 0, + Length: 8, + Scale: 1, + ReceiverNodes: []string{"SENSOR", "MOTOR"}, + ValueDescriptions: []*descriptor.ValueDescription{ + {Value: 0, Description: "None"}, + {Value: 1, Description: "Sync"}, + {Value: 2, Description: "Reboot"}, + }, + }, + }, + } + + exampleDatabase.Messages[101] = &descriptor.Message{ + ID: 101, + Name: "MotorCommand", + Length: 1, + SenderNode: "DRIVER", + SendType: descriptor.SendTypeCyclic, + CycleTime: 100 * time.Millisecond, + Signals: []*descriptor.Signal{ + { + Name: "Steer", + Start: 0, + Length: 4, + IsSigned: true, + Scale: 1, + Offset: -5, + Min: -5, + Max: 5, + ReceiverNodes: []string{"MOTOR"}, + }, + { + Name: "Drive", + Start: 4, + Length: 4, + Scale: 1, + Max: 9, + ReceiverNodes: []string{"MOTOR"}, + }, + }, + } + + exampleDatabase.Messages[200] = &descriptor.Message{ + ID: 200, + Name: "SensorSonars", + Length: 8, + SenderNode: "SENSOR", + SendType: descriptor.SendTypeCyclic, + CycleTime: 100 * time.Millisecond, + Signals: []*descriptor.Signal{ + { + Name: "Mux", + IsMultiplexer: true, + Start: 0, + Length: 4, + Scale: 1, + ReceiverNodes: []string{"DRIVER", "IO"}, + }, + { + Name: "ErrCount", + Start: 4, + Length: 12, + Scale: 1, + ReceiverNodes: []string{"DRIVER", "IO"}, + }, + { + Name: "Left", + IsMultiplexed: true, + MultiplexerValue: 0, + Start: 16, + Length: 12, + Scale: 0.1, + ReceiverNodes: []string{"DRIVER", "IO"}, + }, + { + Name: "NoFiltLeft", + IsMultiplexed: true, + MultiplexerValue: 1, + Start: 16, + Length: 12, + Scale: 0.1, + ReceiverNodes: []string{"DBG"}, + }, + { + Name: "Middle", + IsMultiplexed: true, + MultiplexerValue: 0, + Start: 28, + Length: 12, + Scale: 0.1, + ReceiverNodes: []string{"DRIVER", "IO"}, + }, + { + Name: "NoFiltMiddle", + IsMultiplexed: true, + MultiplexerValue: 1, + Start: 28, + Length: 12, + Scale: 0.1, + ReceiverNodes: []string{"DBG"}, + }, + { + Name: "Right", + IsMultiplexed: true, + MultiplexerValue: 0, + Start: 40, + Length: 12, + Scale: 0.1, + ReceiverNodes: []string{"DRIVER", "IO"}, + }, + { + Name: "NoFiltRight", + IsMultiplexed: true, + MultiplexerValue: 1, + Start: 40, + Length: 12, + Scale: 0.1, + ReceiverNodes: []string{"DBG"}, + }, + { + Name: "Rear", + IsMultiplexed: true, + MultiplexerValue: 0, + Start: 52, + Length: 12, + Scale: 0.1, + ReceiverNodes: []string{"DRIVER", "IO"}, + }, + { + Name: "NoFiltRear", + IsMultiplexed: true, + MultiplexerValue: 1, + Start: 52, + Length: 12, + Scale: 0.1, + ReceiverNodes: []string{"DBG"}, + }, + }, + } + + exampleDatabase.Messages[400] = &descriptor.Message{ + ID: 400, + Name: "MotorStatus", + Length: 3, + SenderNode: "MOTOR", + SendType: descriptor.SendTypeCyclic, + CycleTime: 100 * time.Millisecond, + Signals: []*descriptor.Signal{ + { + Name: "WheelError", + Start: 0, + Length: 1, + Scale: 1, + ReceiverNodes: []string{"DRIVER", "IO"}, + }, + { + Name: "SpeedKph", + Start: 8, + Length: 16, + Scale: 0.001, + Unit: "km/h", + ReceiverNodes: []string{"DRIVER", "IO"}, + }, + }, + } + + exampleDatabase.Messages[500] = &descriptor.Message{ + ID: 500, + Name: "IODebug", + Length: 6, + SenderNode: "IO", + SendType: descriptor.SendTypeEvent, + Signals: []*descriptor.Signal{ + { + Name: "TestUnsigned", + Start: 0, + Length: 8, + Scale: 1, + ReceiverNodes: []string{"DBG"}, + }, + { + Name: "TestEnum", + Start: 8, + Length: 6, + Scale: 1, + ReceiverNodes: []string{"DBG"}, + DefaultValue: int(examplecan.IODebug_TestEnum_Two), + ValueDescriptions: []*descriptor.ValueDescription{ + {Value: 1, Description: "One"}, + {Value: 2, Description: "Two"}, + }, + }, + { + Name: "TestSigned", + Start: 16, + Length: 8, + IsSigned: true, + Scale: 1, + ReceiverNodes: []string{"DBG"}, + }, + { + Name: "TestFloat", + Start: 24, + Length: 8, + Scale: 0.5, + ReceiverNodes: []string{"DBG"}, + }, + { + Name: "TestBoolEnum", + Start: 32, + Length: 1, + Scale: 1, + ReceiverNodes: []string{"DBG"}, + ValueDescriptions: []*descriptor.ValueDescription{ + {Value: 0, Description: "Zero"}, + {Value: 1, Description: "One"}, + }, + }, + { + Name: "TestScaledEnum", + Start: 40, + Length: 2, + Scale: 2, + Min: 0, + Max: 6, + ReceiverNodes: []string{"DBG"}, + ValueDescriptions: []*descriptor.ValueDescription{ + {Value: 0, Description: "Zero"}, + {Value: 1, Description: "Two"}, + {Value: 2, Description: "Four"}, + {Value: 3, Description: "Six"}, + }, + }, + }, + } + + input, err := ioutil.ReadFile(exampleDBCFile) + assert.NilError(t, err) + result, err := Compile(exampleDBCFile, input) + if err != nil { + t.Fatal(err) + } + if len(result.Warnings) > 0 { + t.Fatal(result.Warnings) + } + assert.DeepEqual(t, exampleDatabase, result.Database) +} diff --git a/pkg/can-go/internal/generate/example_test.go b/pkg/can-go/internal/generate/example_test.go new file mode 100644 index 0000000..dc8e5b1 --- /dev/null +++ b/pkg/can-go/internal/generate/example_test.go @@ -0,0 +1,338 @@ +package generate + +import ( + "context" + "fmt" + "net" + "reflect" + "testing" + "time" + + can "github.com/fiskerinc/cloud-services/pkg/can-go" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/generated" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/socketcan" + examplecan "github.com/fiskerinc/cloud-services/pkg/can-go/testdata/gen/go/example" + "golang.org/x/sync/errgroup" + "gotest.tools/v3/assert" +) + +func TestExampleDatabase_MarshalUnmarshal(t *testing.T) { + for _, tt := range []struct { + name string + m can.Message + f can.Frame + }{ + { + name: "IODebug", + m: examplecan.NewIODebug(). + SetTestUnsigned(5). + SetTestEnum(examplecan.IODebug_TestEnum_Two). + SetTestSigned(-42). + SetTestFloat(61.5). + SetTestBoolEnum(examplecan.IODebug_TestBoolEnum_One). + SetRawTestScaledEnum(examplecan.IODebug_TestScaledEnum_Four), + f: can.Frame{ + ID: 500, + Length: 6, + Data: can.Data{5, 2, 214, 123, 1, 2}, + }, + }, + + { + name: "MotorStatus1", + m: examplecan.NewMotorStatus(). + SetSpeedKph(0.423). + SetWheelError(true), + f: can.Frame{ + ID: 400, + Length: 3, + Data: can.Data{0x1, 0xa7, 0x1}, + }, + }, + + { + name: "MotorStatus2", + m: examplecan.NewMotorStatus(). + SetSpeedKph(12), + f: can.Frame{ + ID: 400, + Length: 3, + Data: can.Data{0x00, 0xe0, 0x2e}, + }, + }, + } { + tt := tt + t.Run(tt.name, func(t *testing.T) { + f, err := tt.m.MarshalFrame() + assert.NilError(t, err) + assert.Equal(t, tt.f, f) + // allocate new message of same type as tt.m + msg := reflect.New(reflect.ValueOf(tt.m).Elem().Type()).Interface().(generated.Message) + assert.NilError(t, msg.UnmarshalFrame(f)) + assert.Assert(t, reflect.DeepEqual(tt.m, msg)) + }) + } +} + +func TestExampleDatabase_UnmarshalFrame_Error(t *testing.T) { + for _, tt := range []struct { + name string + f can.Frame + m generated.Message + err string + }{ + { + name: "wrong ID", + f: can.Frame{ID: 11, Length: 8}, + m: examplecan.NewSensorSonars(), + err: "unmarshal SensorSonars: expects ID 200 (got 00B#0000000000000000 with ID 11)", + }, + { + name: "wrong length", + f: can.Frame{ID: 200, Length: 4}, + m: examplecan.NewSensorSonars(), + err: "unmarshal SensorSonars: expects length 8 (got 0C8#00000000 with length 4)", + }, + { + name: "remote frame", + f: can.Frame{ID: 200, Length: 8, IsRemote: true}, + m: examplecan.NewSensorSonars(), + err: "unmarshal SensorSonars: expects non-remote frame (got remote frame 0C8#R8)", + }, + { + name: "extended ID", + f: can.Frame{ID: 200, Length: 8, IsExtended: true}, + m: examplecan.NewSensorSonars(), + err: "unmarshal SensorSonars: expects standard ID (got 000000C8#0000000000000000 with extended ID)", + }, + } { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.err, tt.m.UnmarshalFrame(tt.f).Error()) + }) + } +} + +func TestExampleDatabase_TestEnum_String(t *testing.T) { + assert.Equal(t, "One", examplecan.IODebug_TestEnum_One.String()) + assert.Equal(t, "Two", examplecan.IODebug_TestEnum_Two.String()) + assert.Equal(t, "IODebug_TestEnum(3)", examplecan.IODebug_TestEnum(3).String()) +} + +func TestExampleDatabase_Message_String(t *testing.T) { + const expected = "{WheelError: true, SpeedKph: 42km/h}" + msg := examplecan.NewMotorStatus(). + SetSpeedKph(42). + SetWheelError(true) + assert.Equal(t, expected, msg.String()) + assert.Equal(t, expected, fmt.Sprintf("%v", msg)) +} + +func TestExampleDatabase_OutOfBoundsValue(t *testing.T) { + const expected = examplecan.IODebug_TestEnum(63) + actual := examplecan.NewIODebug().SetTestEnum(255).TestEnum() + assert.Equal(t, expected, actual) +} + +func TestExampleDatabase_MultiplexedSignals(t *testing.T) { + // Given a message with multiplexed signals + msg := examplecan.NewSensorSonars(). + SetErrCount(1). + SetMux(1). + SetLeft(20). + SetMiddle(30). + SetRight(40). + SetRear(50). + SetNoFiltLeft(60). + SetNoFiltMiddle(70). + SetNoFiltRight(80). + SetNoFiltRear(90) + for _, tt := range []struct { + expectedMux uint8 + expectedErrCount uint16 + expectedLeft float64 + expectedMiddle float64 + expectedRight float64 + expectedRear float64 + expectedNoFiltLeft float64 + expectedNoFiltMiddle float64 + expectedNoFiltRight float64 + expectedNoFiltRear float64 + }{ + { + expectedMux: 0, + expectedErrCount: 1, + expectedLeft: 20, + expectedMiddle: 30, + expectedRight: 40, + expectedRear: 50, + expectedNoFiltLeft: 0, + expectedNoFiltMiddle: 0, + expectedNoFiltRight: 0, + expectedNoFiltRear: 0, + }, + { + expectedMux: 1, + expectedErrCount: 1, + expectedLeft: 0, + expectedMiddle: 0, + expectedRight: 0, + expectedRear: 0, + expectedNoFiltLeft: 60, + expectedNoFiltMiddle: 70, + expectedNoFiltRight: 80, + expectedNoFiltRear: 90, + }, + } { + tt := tt + t.Run(fmt.Sprintf("mux=%v", tt.expectedMux), func(t *testing.T) { + unmarshal1 := examplecan.NewSensorSonars() + // When the multiplexer signal is 0 and we marshal the message + // to a CAN frame + msg.SetMux(tt.expectedMux) + f1, err := msg.MarshalFrame() + assert.NilError(t, err) + // When we unmarshal the CAN frame back to a message + assert.NilError(t, unmarshal1.UnmarshalFrame(f1)) + // Then only the multiplexed signals with multiplexer value 0 + // should be unmarshaled + assert.Equal(t, tt.expectedMux, unmarshal1.Mux(), "Mux") + assert.Equal(t, tt.expectedErrCount, unmarshal1.ErrCount(), "ErrCount") + assert.Equal(t, tt.expectedLeft, unmarshal1.Left(), "Left") + assert.Equal(t, tt.expectedMiddle, unmarshal1.Middle(), "Middle") + assert.Equal(t, tt.expectedRight, unmarshal1.Right(), "Right") + assert.Equal(t, tt.expectedRear, unmarshal1.Rear(), "Rear") + assert.Equal(t, tt.expectedNoFiltLeft, unmarshal1.NoFiltLeft(), "NoFiltLeft") + assert.Equal(t, tt.expectedNoFiltMiddle, unmarshal1.NoFiltMiddle(), "NoFiltMiddle") + assert.Equal(t, tt.expectedNoFiltRight, unmarshal1.NoFiltRight(), "NoFiltRight") + assert.Equal(t, tt.expectedNoFiltRear, unmarshal1.NoFiltRear(), "NoFiltRear") + }) + } +} + +func TestExampleDatabase_CopyFrom(t *testing.T) { + // Given: an original message + from := examplecan.NewIODebug(). + SetRawTestScaledEnum(examplecan.IODebug_TestScaledEnum_Four). + SetTestBoolEnum(true). + SetTestFloat(0.1). + SetTestSigned(-10). + SetTestUnsigned(10) + // When: another message copies from the original message + to := examplecan.NewIODebug().CopyFrom(from) + // Then: + // all fields should be equal... + assert.Equal(t, from.String(), to.String()) + assert.Equal(t, from.TestScaledEnum(), to.TestScaledEnum()) + assert.Equal(t, from.TestBoolEnum(), to.TestBoolEnum()) + assert.Equal(t, from.TestFloat(), to.TestFloat()) + assert.Equal(t, from.TestSigned(), to.TestSigned()) + assert.Equal(t, from.TestUnsigned(), to.TestUnsigned()) + // ...and changes to the original should not affect the new message + from.SetTestUnsigned(100) + assert.Equal(t, uint8(10), to.TestUnsigned()) +} + +func TestExample_Nodes(t *testing.T) { + const testTimeout = 2 * time.Second + requireVCAN0(t) + // given a DRIVER node and a MOTOR node + motor := examplecan.NewMOTOR("can", "vcan0") + driver := examplecan.NewDRIVER("can", "vcan0") + // when starting them + ctx, cancel := context.WithTimeout(context.Background(), testTimeout) + g, ctx := errgroup.WithContext(ctx) + g.Go(func() error { + return motor.Run(ctx) + }) + g.Go(func() error { + return driver.Run(ctx) + }) + // and the MOTOR node is configured to send a speed report + const expectedSpeedKph = 42 + motor.Lock() + motor.Tx().MotorStatus().SetSpeedKph(expectedSpeedKph) + motor.Tx().MotorStatus().SetCyclicTransmissionEnabled(true) + motor.Unlock() + // and the DRIVER node is configured to send a steering command + const expectedSteer = -4 + driver.Lock() + driver.Tx().MotorCommand().SetSteer(expectedSteer) + driver.Tx().MotorCommand().SetCyclicTransmissionEnabled(true) + driver.Unlock() + // and the MOTOR node is listening for the steering command + expectedSteerReceivedChan := make(chan struct{}) + motor.Lock() + motor.Rx().MotorCommand().SetAfterReceiveHook(func(context.Context) error { + motor.Lock() + if motor.Rx().MotorCommand().Steer() == expectedSteer { + close(expectedSteerReceivedChan) + motor.Rx().MotorCommand().SetAfterReceiveHook(func(context.Context) error { return nil }) + } + motor.Unlock() + return nil + }) + motor.Unlock() + // and the DRIVER node is listening for the speed report + expectedSpeedReceivedChan := make(chan struct{}) + driver.Lock() + driver.Rx().MotorStatus().SetAfterReceiveHook(func(context.Context) error { + driver.Lock() + if driver.Rx().MotorStatus().SpeedKph() == expectedSpeedKph { + close(expectedSpeedReceivedChan) + driver.Rx().MotorStatus().SetAfterReceiveHook(func(context.Context) error { return nil }) + } + driver.Unlock() + return nil + }) + driver.Unlock() + // then the steer command transmitted by DRIVER should be received by MOTOR + select { + case <-expectedSteerReceivedChan: + case <-ctx.Done(): + t.Fatalf("expected steer not received: %v", expectedSteer) + } + // and the speed report transmitted by MOTOR should be received by DRIVER + select { + case <-expectedSpeedReceivedChan: + case <-ctx.Done(): + t.Fatalf("expected speed not received: %v", expectedSpeedKph) + } + cancel() + assert.NilError(t, g.Wait()) +} + +func TestExample_Node_NoEmptyMessages(t *testing.T) { + const testTimeout = 2 * time.Second + requireVCAN0(t) + // given a DRIVER node and a MOTOR node + motor := examplecan.NewMOTOR("can", "vcan0") + // when starting them + ctx, cancel := context.WithTimeout(context.Background(), testTimeout) + handler := func(ctx context.Context) error { + motor.Lock() + motor.Tx().MotorStatus().SetSpeedKph(100).SetWheelError(true) + motor.Unlock() + return nil + } + motor.Tx().MotorStatus().SetBeforeTransmitHook(handler) + motor.Tx().MotorStatus().SetCyclicTransmissionEnabled(true) + c, err := socketcan.Dial("can", "vcan0") + r := socketcan.NewReceiver(c) + assert.NilError(t, err) + g := errgroup.Group{} + g.Go(func() error { + return motor.Run(ctx) + }) + assert.Assert(t, r.Receive()) + assert.Equal(t, examplecan.NewMotorStatus().SetSpeedKph(100).SetWheelError(true).Frame(), r.Frame()) + cancel() + assert.NilError(t, g.Wait()) +} + +func requireVCAN0(t *testing.T) { + t.Helper() + if _, err := net.InterfaceByName("vcan0"); err != nil { + t.Skip("interface vcan0 does not exist") + } +} diff --git a/pkg/can-go/internal/generate/file.go b/pkg/can-go/internal/generate/file.go new file mode 100644 index 0000000..4eedb46 --- /dev/null +++ b/pkg/can-go/internal/generate/file.go @@ -0,0 +1,976 @@ +package generate + +import ( + "bytes" + "fmt" + "go/format" + "go/types" + "path" + "strings" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" + "github.com/shurcooL/go-goon" +) + +type File struct { + buf bytes.Buffer + err error +} + +func NewFile() *File { + f := &File{} + f.buf.Grow(1e5) // 100K + return f +} + +func (f *File) Write(p []byte) (int, error) { + if f.err != nil { + return 0, f.err + } + n, err := f.buf.Write(p) + f.err = err + return n, err +} + +func (f *File) P(v ...interface{}) { + for _, x := range v { + _, _ = fmt.Fprint(f, x) + } + _, _ = fmt.Fprintln(f) +} + +func (f *File) Dump(v interface{}) { + _, _ = goon.Fdump(f, v) +} + +func (f *File) Content() ([]byte, error) { + if f.err != nil { + return nil, fmt.Errorf("file content: %w", f.err) + } + formatted, err := format.Source(f.buf.Bytes()) + if err != nil { + return nil, fmt.Errorf("file content: %s: %w", f.buf.String(), err) + } + return formatted, nil +} + +func Database(h string, d *descriptor.Database) ([]byte, error) { + f := NewFile() + Package(f, d) + Imports(f) + Version(f, h, d.Version) + ListECUs(f, d) + + for _, m := range d.Messages { + if m == nil { + continue + } + MessageType(f, m) + for _, s := range m.Signals { + if hasCustomType(s) { + SignalCustomType(f, m, s) + } + } + MarshalFrame(f, m) + UnmarshalFrame(f, m) + } + if hasSendType(d) { // only code-generate nodes for schemas with send types specified + for _, n := range d.Nodes { + Node(f, d, n) + } + } + Descriptors(f, d) + return f.Content() +} + +func Package(f *File, d *descriptor.Database) { + packageName := strings.TrimSuffix(path.Base(d.SourceFile), path.Ext(d.SourceFile)) + "can" + f.P("// Package ", packageName, " provides primitives for encoding and decoding ", d.Name(), " CAN messages.") + f.P("//") + f.P("// Source: ", d.SourceFile) + f.P("package ", packageName) + f.P() +} + +func Imports(f *File) { + f.P("import (") + f.P(`"context"`) + f.P(`"fmt"`) + f.P(`"net"`) + f.P(`"net/http"`) + f.P(`"sync"`) + f.P(`"time"`) + f.P() + f.P(`"github.com/fiskerinc/cloud-services/pkg/can-go"`) + f.P(`"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/socketcan"`) + f.P(`"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/candebug"`) + f.P(`"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/canrunner"`) + f.P(`"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor"`) + f.P(`"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/generated"`) + f.P(`"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/cantext"`) + f.P(")") + f.P() + // we could use goimports for this, but it significantly slows down code generation + f.P("// prevent unused imports") + f.P("var (") + f.P("_ = context.Background") + f.P("_ = fmt.Print") + f.P("_ = net.Dial") + f.P("_ = http.Error") + f.P("_ = sync.Mutex{}") + f.P("_ = time.Now") + f.P("_ = socketcan.Dial") + f.P("_ = candebug.ServeMessagesHTTP") + f.P("_ = canrunner.Run") + f.P(")") + f.P() + f.P("// Generated code. DO NOT EDIT.") +} + +func Version(f *File, h string, v string) { + f.P() + f.P("// Hash used as versioning control for DBC") + f.P(`const Hash string = "`, h, `"`) + f.P("// Version is the version listed in the DBC") + f.P(`const Version string = "`, v, `"`) + f.P() +} + +func ListECUs(f *File, d *descriptor.Database) { + f.P() + f.P("// ECUs parsed from DBC") + + ecuList := "var ECUs = []string{" + i := 0 + for ecu := range d.ECUs { + ecuList += fmt.Sprintf(`"%s"`, ecu) + i++ + if i != len(d.ECUs) { + ecuList += ", " + } + } + ecuList += "}" + f.P(ecuList) + f.P() +} + +func SignalCustomType(f *File, m *descriptor.Message, s *descriptor.Signal) { + f.P("// ", signalType(m, s), " models the ", s.Name, " signal of the ", m.Name, " message.") + f.P("type ", signalType(m, s), " ", signalPrimitiveType(s)) + f.P() + + // dtaylor@fiskerinc.com EDIT + + // f.P("// Value descriptions for the ", s.Name, " signal of the ", m.Name, " message.") + // f.P("const (") + // for _, vd := range s.ValueDescriptions { + // switch { + // case s.Length == 1 && vd.Value == 1: + // f.P(signalType(m, s), "_", vd.Description, " ", signalType(m, s), " = true") + // case s.Length == 1 && vd.Value == 0: + // f.P(signalType(m, s), "_", vd.Description, " ", signalType(m, s), " = false") + // default: + // f.P(signalType(m, s), "_", vd.Description, " ", signalType(m, s), " = ", vd.Value) + // } + // } + // f.P(")") + // f.P() + + f.P("func (v ", signalType(m, s), ") String() string {") + if s.Length == 1 { + f.P("switch bool(v) {") + for _, vd := range s.ValueDescriptions { + if vd.Value == 1 { + f.P("case true:") + } else { + f.P("case false:") + } + f.P(`return "`, vd.Description, `"`) + } + f.P("}") + f.P(`return fmt.Sprintf("`, signalType(m, s), `(%t)", v)`) + } else { + f.P("switch v {") + for _, vd := range s.ValueDescriptions { + f.P("case ", vd.Value, ":") + f.P(`return "`, vd.Description, `"`) + } + f.P("default:") + f.P(`return fmt.Sprintf("`, signalType(m, s), `(%d)", v)`) + f.P("}") + } + f.P("}") +} + +func MessageType(f *File, m *descriptor.Message) { + f.P("// ", messageReaderInterface(m), " provides read access to a ", m.Name, " message.") + f.P("type ", messageReaderInterface(m), " interface {") + for _, s := range m.Signals { + if hasPhysicalRepresentation(s) { + f.P("// ", s.Name, " returns the physical value of the ", s.Name, " signal.") + f.P(s.Name, "() float64") + if len(s.ValueDescriptions) > 0 { + f.P() + f.P("// ", s.Name, " returns the raw (encoded) value of the ", s.Name, " signal.") + f.P("Raw", s.Name, "() ", signalType(m, s)) + } + } else { + f.P("// ", s.Name, " returns the value of the ", s.Name, " signal.") + f.P(s.Name, "()", signalType(m, s)) + } + } + f.P("}") + f.P() + f.P("// ", messageWriterInterface(m), " provides write access to a ", m.Name, " message.") + f.P("type ", messageWriterInterface(m), " interface {") + f.P("// CopyFrom copies all values from ", messageReaderInterface(m), ".") + f.P("CopyFrom(", messageReaderInterface(m), ") *", messageStruct(m)) + for _, s := range m.Signals { + if hasPhysicalRepresentation(s) { + f.P("// Set", s.Name, " sets the physical value of the ", s.Name, " signal.") + f.P("Set", s.Name, "(float64) *", messageStruct(m)) + if len(s.ValueDescriptions) > 0 { + f.P() + f.P("// SetRaw", s.Name, " sets the raw (encoded) value of the ", s.Name, " signal.") + f.P("SetRaw", s.Name, "(", signalType(m, s), ") *", messageStruct(m)) + } + } else { + f.P("// Set", s.Name, " sets the value of the ", s.Name, " signal.") + f.P("Set", s.Name, "(", signalType(m, s), ") *", messageStruct(m)) + } + } + f.P("}") + f.P() + f.P("type ", messageStruct(m), " struct {") + for _, s := range m.Signals { + f.P(signalField(s), " ", signalType(m, s)) + } + f.P("}") + f.P() + f.P("func New", messageStruct(m), "() *", messageStruct(m), " {") + f.P("m := &", messageStruct(m), "{}") + f.P("m.Reset()") + f.P("return m") + f.P("}") + f.P() + f.P("func (m *", messageStruct(m), ") Reset() {") + for _, s := range m.Signals { + switch { + case s.Length == 1 && s.DefaultValue == 1: + f.P("m.", signalField(s), " = true") + case s.Length == 1: + f.P("m.", signalField(s), " = false") + default: + f.P("m.", signalField(s), " = ", s.DefaultValue) + } + } + f.P("}") + f.P() + f.P("func (m *", messageStruct(m), ") CopyFrom(o ", messageReaderInterface(m), ") *", messageStruct(m), "{") + for _, s := range m.Signals { + if hasPhysicalRepresentation(s) { + f.P("m.Set", s.Name, "(o.", s.Name, "())") + } else { + f.P("m.", signalField(s), " = o.", s.Name, "()") + } + } + f.P("return m") + f.P("}") + f.P() + f.P("// Descriptor returns the ", m.Name, " descriptor.") + f.P("func (m *", messageStruct(m), ") Descriptor() *descriptor.Message {") + f.P("return ", messageDescriptor(m), ".Message") + f.P("}") + f.P() + f.P("// String returns a compact string representation of the message.") + f.P("func(m *", messageStruct(m), ") String() string {") + f.P("return cantext.MessageString(m)") + f.P("}") + f.P() + for _, s := range m.Signals { + if !hasPhysicalRepresentation(s) { + f.P("func (m *", messageStruct(m), ") ", s.Name, "() ", signalType(m, s), " {") + f.P("return m.", signalField(s)) + f.P("}") + f.P() + f.P("func (m *", messageStruct(m), ") Set", s.Name, "(v ", signalType(m, s), ") *", messageStruct(m), " {") + if s.Length == 1 { + f.P("m.", signalField(s), " = v") + } else { + f.P( + "m.", signalField(s), " = ", signalType(m, s), "(", + signalDescriptor(m, s), ".SaturatedCast", signalSuperType(s), "(", + signalPrimitiveSuperType(s), "(v)))", + ) + } + f.P("return m") + f.P("}") + f.P() + continue + } + f.P("func (m *", messageStruct(m), ") ", s.Name, "() float64 {") + f.P("return ", signalDescriptor(m, s), ".ToPhysical(float64(m.", signalField(s), "))") + f.P("}") + f.P() + f.P("func (m *", messageStruct(m), ") Set", s.Name, "(v float64) *", messageStruct(m), " {") + f.P("m.", signalField(s), " = ", signalType(m, s), "(", signalDescriptor(m, s), ".FromPhysical(v))") + f.P("return m") + f.P("}") + f.P() + if len(s.ValueDescriptions) > 0 { + f.P("func (m *", messageStruct(m), ") Raw", s.Name, "() ", signalType(m, s), " {") + f.P("return m.", signalField(s)) + f.P("}") + f.P() + f.P("func (m *", messageStruct(m), ") SetRaw", s.Name, "(v ", signalType(m, s), ") *", messageStruct(m), "{") + f.P( + "m.", signalField(s), " = ", signalType(m, s), "(", + signalDescriptor(m, s), ".SaturatedCast", signalSuperType(s), "(", + signalPrimitiveSuperType(s), "(v)))", + ) + f.P("return m") + f.P("}") + f.P() + } + } +} + +func Descriptors(f *File, d *descriptor.Database) { + f.P("// Nodes returns the ", d.Name(), " node descriptors.") + f.P("func Nodes() *NodesDescriptor {") + f.P("return nd") + f.P("}") + f.P() + f.P("// NodesDescriptor contains all ", d.Name(), " node descriptors.") + f.P("type NodesDescriptor struct{") + for _, n := range d.Nodes { + f.P(n.Name, " *descriptor.Node") + } + f.P("}") + f.P() + f.P("// Messages returns the ", d.Name(), " message descriptors.") + f.P("func Messages() *MessagesDescriptor {") + f.P("return md") + f.P("}") + f.P() + f.P("// MessagesDescriptor contains all ", d.Name(), " message descriptors.") + f.P("type MessagesDescriptor struct{") + for _, m := range d.Messages { + if m == nil { + continue + } + f.P(m.Name, " *", m.Name, "Descriptor") + } + f.P("}") + f.P() + f.P("// UnmarshalFrame unmarshals the provided ", d.Name(), " CAN frame.") + f.P("func (md *MessagesDescriptor) UnmarshalFrame(f can.Frame) (generated.Message, error) {") + f.P("switch f.ID {") + for _, m := range d.Messages { + if m == nil { + continue + } + f.P("case md.", m.Name, ".ID:") + f.P("var msg ", messageStruct(m)) + f.P("if err := msg.UnmarshalFrame(f); err != nil {") + f.P(`return nil, fmt.Errorf("unmarshal `, d.Name(), ` frame: %w", err)`) + f.P("}") + f.P("return &msg, nil") + } + f.P("default:") + f.P(`return nil, fmt.Errorf("unmarshal `, d.Name(), ` frame: ID not in database: %d", f.ID)`) + f.P("}") + f.P("}") + f.P() + for _, m := range d.Messages { + if m == nil { + continue + } + f.P("type ", m.Name, "Descriptor struct{") + f.P("*descriptor.Message") + for _, s := range m.Signals { + f.P(s.Name, " *descriptor.Signal") + } + f.P("}") + f.P() + } + f.P("// Database returns the ", d.Name(), " database descriptor.") + f.P("func (md *MessagesDescriptor) Database() *descriptor.Database {") + f.P("return d") + f.P("}") + f.P() + f.P("var nd = &NodesDescriptor{") + for ni, n := range d.Nodes { + f.P(n.Name, ": d.Nodes[", ni, "],") + } + f.P("}") + f.P() + f.P("var md = &MessagesDescriptor{") + for mi, m := range d.Messages { + if m == nil { + continue + } + f.P(m.Name, ": &", m.Name, "Descriptor{") + f.P("Message: d.Messages[", mi, "],") + for si, s := range m.Signals { + f.P(s.Name, ": d.Messages[", mi, "].Signals[", si, "],") + } + f.P("},") + } + f.P("}") + f.P() + f.P("var d = ") + f.Dump(d) + f.P() +} + +func MarshalFrame(f *File, m *descriptor.Message) { + f.P("// Frame returns a CAN frame representing the message.") + f.P("func (m *", messageStruct(m), ") Frame() can.Frame {") + f.P("md := ", messageDescriptor(m)) + f.P("f := can.Frame{ID: md.ID, IsExtended: md.IsExtended, Length: md.Length}") + for _, s := range m.Signals { + if s.IsMultiplexed { + continue + } + f.P( + "md.", s.Name, ".Marshal", signalSuperType(s), + "(&f.Data, ", signalPrimitiveSuperType(s), "(m.", signalField(s), "))", + ) + } + if mux, ok := m.MultiplexerSignal(); ok { + for _, s := range m.Signals { + if !s.IsMultiplexed { + continue + } + f.P("if m.", signalField(mux), " == ", s.MultiplexerValue, " {") + f.P( + "md.", s.Name, ".Marshal", signalSuperType(s), "(&f.Data, ", signalPrimitiveSuperType(s), + "(m.", signalField(s), "))", + ) + f.P("}") + } + } + f.P("return f") + f.P("}") + f.P() + f.P("// MarshalFrame encodes the message as a CAN frame.") + f.P("func (m *", messageStruct(m), ") MarshalFrame() (can.Frame, error) {") + f.P("return m.Frame(), nil") + f.P("}") + f.P() +} + +func UnmarshalFrame(f *File, m *descriptor.Message) { + f.P("// UnmarshalFrame decodes the message from a CAN frame.") + f.P("func (m *", messageStruct(m), ") UnmarshalFrame(f can.Frame) error {") + f.P("md := ", messageDescriptor(m)) + // generate frame checks + id := func(isExtended bool) string { + if isExtended { + return "extended ID" + } + return "standard ID" + } + f.P("switch {") + f.P("case f.ID != md.ID:") + f.P(`return fmt.Errorf(`) + f.P(`"unmarshal `, m.Name, `: expects ID `, m.ID, ` (got %s with ID %d)", f.String(), f.ID,`) + f.P(`)`) + f.P("case f.Length != md.Length:") + f.P(`return fmt.Errorf(`) + f.P(`"unmarshal `, m.Name, `: expects length `, m.Length, ` (got %s with length %d)", f.String(), f.Length,`) + f.P(`)`) + f.P("case f.IsRemote:") + f.P(`return fmt.Errorf(`) + f.P(`"unmarshal `, m.Name, `: expects non-remote frame (got remote frame %s)", f.String(),`) + f.P(`)`) + f.P("case f.IsExtended != md.IsExtended:") + f.P(`return fmt.Errorf(`) + f.P(`"unmarshal `, m.Name, `: expects `, id(m.IsExtended), ` (got %s with `, id(!m.IsExtended), `)", f.String(),`) + f.P(`)`) + f.P("}") + if len(m.Signals) == 0 { + f.P("return nil") + f.P("}") + return + } + // generate non-multiplexed signal unmarshaling + for _, s := range m.Signals { + if s.IsMultiplexed { + continue + } + f.P("m.", signalField(s), " = ", signalType(m, s), "(md.", s.Name, ".Unmarshal", signalSuperType(s), "(f.Data))") + } + // generate multiplexed signal unmarshaling + if mux, ok := m.MultiplexerSignal(); ok { + for _, s := range m.Signals { + if !s.IsMultiplexed { + continue + } + f.P("if m.", signalField(mux), " == ", s.MultiplexerValue, " {") + f.P("m.", signalField(s), " = ", signalType(m, s), "(md.", s.Name, ".Unmarshal", signalSuperType(s), "(f.Data))") + f.P("}") + } + } + f.P("return nil") + f.P("}") + f.P() +} + +func Node(f *File, d *descriptor.Database, n *descriptor.Node) { + rxMessages := collectRxMessages(d, n) + txMessages := collectTxMessages(d, n) + f.P("type ", nodeInterface(n), " interface {") + f.P("sync.Locker") + f.P("Tx() ", txGroupInterface(n)) + f.P("Rx() ", rxGroupInterface(n)) + f.P("Run(ctx context.Context) error") + f.P("}") + f.P() + f.P("type ", rxGroupInterface(n), " interface {") + f.P("http.Handler // for debugging") + for _, m := range rxMessages { + f.P(m.Name, "() ", rxMessageInterface(n, m)) + } + f.P("}") + f.P() + f.P("type ", txGroupInterface(n), " interface {") + f.P("http.Handler // for debugging") + for _, m := range txMessages { + f.P(m.Name, "() ", txMessageInterface(n, m)) + } + f.P("}") + f.P() + for _, m := range rxMessages { + f.P("type ", rxMessageInterface(n, m), " interface {") + f.P(messageReaderInterface(m)) + f.P("ReceiveTime() time.Time") + f.P("SetAfterReceiveHook(h func(context.Context) error)") + f.P("}") + f.P() + } + for _, m := range txMessages { + f.P("type ", txMessageInterface(n, m), " interface {") + f.P(messageReaderInterface(m)) + f.P(messageWriterInterface(m)) + f.P("TransmitTime() time.Time") + f.P("Transmit(ctx context.Context) error") + f.P("SetBeforeTransmitHook(h func(context.Context) error)") + if m.SendType == descriptor.SendTypeCyclic { + f.P("// SetCyclicTransmissionEnabled enables/disables cyclic transmission.") + f.P("SetCyclicTransmissionEnabled(bool)") + f.P("// IsCyclicTransmissionEnabled returns whether cyclic transmission is enabled/disabled.") + f.P("IsCyclicTransmissionEnabled() bool") + } + f.P("}") + f.P() + } + f.P("type ", nodeStruct(n), " struct {") + f.P("sync.Mutex // protects all node state") + f.P("network string") + f.P("address string") + f.P("rx ", rxGroupStruct(n)) + f.P("tx ", txGroupStruct(n)) + f.P("}") + f.P() + f.P("var _ ", nodeInterface(n), " = &", nodeStruct(n), "{}") + f.P("var _ canrunner.Node = &", nodeStruct(n), "{}") + f.P() + f.P("func New", nodeInterface(n), "(network, address string) ", nodeInterface(n), " {") + f.P("n := &", nodeStruct(n), "{network: network, address: address}") + f.P("n.rx.parentMutex = &n.Mutex") + f.P("n.tx.parentMutex = &n.Mutex") + for _, m := range rxMessages { + f.P("n.rx.", messageField(m), ".init()") + f.P("n.rx.", messageField(m), ".Reset()") + } + for _, m := range txMessages { + f.P("n.tx.", messageField(m), ".init()") + f.P("n.tx.", messageField(m), ".Reset()") + } + f.P("return n") + f.P("}") + f.P() + f.P("func (n *", nodeStruct(n), ") Run(ctx context.Context) error {") + f.P("return canrunner.Run(ctx, n)") + f.P("}") + f.P() + f.P("func (n *", nodeStruct(n), ") Rx() ", rxGroupInterface(n), " {") + f.P("return &n.rx") + f.P("}") + f.P() + f.P("func (n *", nodeStruct(n), ") Tx() ", txGroupInterface(n), " {") + f.P("return &n.tx") + f.P("}") + f.P() + f.P("type ", rxGroupStruct(n), " struct {") + f.P("parentMutex *sync.Mutex") + for _, m := range rxMessages { + f.P(messageField(m), " ", rxMessageStruct(n, m)) + } + f.P("}") + f.P() + f.P("var _ ", rxGroupInterface(n), " = &", rxGroupStruct(n), "{}") + f.P() + f.P("func (rx *", rxGroupStruct(n), ") ServeHTTP(w http.ResponseWriter, r *http.Request) {") + f.P("rx.parentMutex.Lock()") + f.P("defer rx.parentMutex.Unlock()") + f.P("candebug.ServeMessagesHTTP(w, r, []generated.Message{") + for _, m := range rxMessages { + f.P("&rx.", messageField(m), ",") + } + f.P("})") + f.P("}") + f.P() + for _, m := range rxMessages { + f.P("func (rx *", rxGroupStruct(n), ") ", m.Name, "() ", rxMessageInterface(n, m), " {") + f.P("return &rx.", messageField(m)) + f.P("}") + f.P() + } + f.P() + f.P("type ", txGroupStruct(n), " struct {") + f.P("parentMutex *sync.Mutex") + for _, m := range txMessages { + f.P(messageField(m), " ", txMessageStruct(n, m)) + } + f.P("}") + f.P() + f.P("var _ ", txGroupInterface(n), " = &", txGroupStruct(n), "{}") + f.P() + f.P("func (tx *", txGroupStruct(n), ") ServeHTTP(w http.ResponseWriter, r *http.Request) {") + f.P("tx.parentMutex.Lock()") + f.P("defer tx.parentMutex.Unlock()") + f.P("candebug.ServeMessagesHTTP(w, r, []generated.Message{") + for _, m := range txMessages { + f.P("&tx.", messageField(m), ",") + } + f.P("})") + f.P("}") + f.P() + for _, m := range txMessages { + f.P("func (tx *", txGroupStruct(n), ") ", m.Name, "() ", txMessageInterface(n, m), " {") + f.P("return &tx.", messageField(m)) + f.P("}") + f.P() + } + f.P() + f.P("func (n *", nodeStruct(n), ") Descriptor() *descriptor.Node {") + f.P("return ", nodeDescriptor(n)) + f.P("}") + f.P() + f.P("func (n *", nodeStruct(n), ") Connect() (net.Conn, error) {") + f.P("return socketcan.Dial(n.network, n.address)") + f.P("}") + f.P() + f.P("func (n *", nodeStruct(n), ") ReceivedMessage(id uint32) (canrunner.ReceivedMessage, bool) {") + f.P("switch id {") + for _, m := range rxMessages { + f.P("case ", m.ID, ":") + f.P("return &n.rx.", messageField(m), ", true") + } + f.P("default:") + f.P("return nil, false") + f.P("}") + f.P("}") + f.P() + f.P("func (n *", nodeStruct(n), ") TransmittedMessages() []canrunner.TransmittedMessage {") + f.P("return []canrunner.TransmittedMessage{") + for _, m := range txMessages { + f.P("&n.tx.", messageField(m), ",") + } + f.P("}") + f.P("}") + f.P() + for _, m := range rxMessages { + f.P("type ", rxMessageStruct(n, m), " struct {") + f.P(messageStruct(m)) + f.P("receiveTime time.Time") + f.P("afterReceiveHook func(context.Context) error") + f.P("}") + f.P() + f.P("func (m *", rxMessageStruct(n, m), ") init() {") + f.P("m.afterReceiveHook = func(context.Context) error { return nil }") + f.P("}") + f.P() + f.P("func (m *", rxMessageStruct(n, m), ") SetAfterReceiveHook(h func(context.Context) error) {") + f.P("m.afterReceiveHook = h") + f.P("}") + f.P() + f.P("func (m *", rxMessageStruct(n, m), ") AfterReceiveHook() func(context.Context) error {") + f.P("return m.afterReceiveHook") + f.P("}") + f.P() + f.P("func (m *", rxMessageStruct(n, m), ") ReceiveTime() time.Time {") + f.P("return m.receiveTime") + f.P("}") + f.P() + f.P("func (m *", rxMessageStruct(n, m), ") SetReceiveTime(t time.Time) {") + f.P("m.receiveTime = t") + f.P("}") + f.P() + f.P("var _ canrunner.ReceivedMessage = &", rxMessageStruct(n, m), "{}") + f.P() + } + for _, m := range txMessages { + f.P("type ", txMessageStruct(n, m), " struct {") + f.P(messageStruct(m)) + f.P("transmitTime time.Time") + f.P("beforeTransmitHook func(context.Context) error") + f.P("isCyclicEnabled bool") + f.P("wakeUpChan chan struct{}") + f.P("transmitEventChan chan struct{}") + f.P("}") + f.P() + f.P("var _ ", txMessageInterface(n, m), " = &", txMessageStruct(n, m), "{}") + f.P("var _ canrunner.TransmittedMessage = &", txMessageStruct(n, m), "{}") + f.P() + f.P("func (m *", txMessageStruct(n, m), ") init() {") + f.P("m.beforeTransmitHook = func(context.Context) error { return nil }") + f.P("m.wakeUpChan = make(chan struct{}, 1)") + f.P("m.transmitEventChan = make(chan struct{})") + f.P("}") + f.P() + f.P("func (m *", txMessageStruct(n, m), ") SetBeforeTransmitHook(h func(context.Context) error) {") + f.P("m.beforeTransmitHook = h") + f.P("}") + f.P() + f.P("func (m *", txMessageStruct(n, m), ") BeforeTransmitHook() func(context.Context) error {") + f.P("return m.beforeTransmitHook") + f.P("}") + f.P() + f.P("func (m *", txMessageStruct(n, m), ") TransmitTime() time.Time {") + f.P("return m.transmitTime") + f.P("}") + f.P() + f.P("func (m *", txMessageStruct(n, m), ") SetTransmitTime(t time.Time) {") + f.P("m.transmitTime = t") + f.P("}") + f.P() + f.P("func (m *", txMessageStruct(n, m), ") IsCyclicTransmissionEnabled() bool {") + f.P("return m.isCyclicEnabled") + f.P("}") + f.P() + f.P("func (m *", txMessageStruct(n, m), ") SetCyclicTransmissionEnabled(b bool) {") + f.P("m.isCyclicEnabled = b") + f.P("select {") + f.P("case m.wakeUpChan <-struct{}{}:") + f.P("default:") + f.P("}") + f.P("}") + f.P() + f.P("func (m *", txMessageStruct(n, m), ") WakeUpChan() <-chan struct{} {") + f.P("return m.wakeUpChan") + f.P("}") + f.P() + f.P("func (m *", txMessageStruct(n, m), ") Transmit(ctx context.Context) error {") + f.P("select {") + f.P("case m.transmitEventChan <- struct{}{}:") + f.P("return nil") + f.P("case <-ctx.Done():") + f.P(`return fmt.Errorf("event-triggered transmit of `, m.Name, `: %w", ctx.Err())`) + f.P("}") + f.P("}") + f.P() + f.P("func (m *", txMessageStruct(n, m), ") TransmitEventChan() <-chan struct{} {") + f.P("return m.transmitEventChan") + f.P("}") + f.P() + f.P("var _ canrunner.TransmittedMessage = &", txMessageStruct(n, m), "{}") + f.P() + } +} + +func txGroupInterface(n *descriptor.Node) string { + return n.Name + "_Tx" +} + +func txGroupStruct(n *descriptor.Node) string { + return "xxx_" + n.Name + "_Tx" +} + +func rxGroupInterface(n *descriptor.Node) string { + return n.Name + "_Rx" +} + +func rxGroupStruct(n *descriptor.Node) string { + return "xxx_" + n.Name + "_Rx" +} + +func rxMessageInterface(n *descriptor.Node, m *descriptor.Message) string { + return n.Name + "_Rx_" + m.Name +} + +func rxMessageStruct(n *descriptor.Node, m *descriptor.Message) string { + return "xxx_" + n.Name + "_Rx_" + m.Name +} + +func txMessageInterface(n *descriptor.Node, m *descriptor.Message) string { + return n.Name + "_Tx_" + m.Name +} + +func txMessageStruct(n *descriptor.Node, m *descriptor.Message) string { + return "xxx_" + n.Name + "_Tx_" + m.Name +} + +func collectTxMessages(d *descriptor.Database, n *descriptor.Node) []*descriptor.Message { + tx := make([]*descriptor.Message, 0, len(d.Messages)) + for _, m := range d.Messages { + if m == nil { + continue + } + + if m.SenderNode == n.Name && m.SendType != descriptor.SendTypeNone { + tx = append(tx, m) + } + } + return tx +} + +func collectRxMessages(d *descriptor.Database, n *descriptor.Node) []*descriptor.Message { + rx := make([]*descriptor.Message, 0, len(d.Messages)) +Loop: + for _, m := range d.Messages { + if m == nil { + continue + } + + for _, s := range m.Signals { + for _, node := range s.ReceiverNodes { + if node != n.Name { + continue + } + rx = append(rx, m) + continue Loop + } + } + } + return rx +} + +func hasPhysicalRepresentation(s *descriptor.Signal) bool { + hasScale := s.Scale != 0 && s.Scale != 1 + hasOffset := s.Offset != 0 + hasRange := s.Min != 0 || s.Max != 0 + var hasConstrainedRange bool + if s.IsSigned { + hasConstrainedRange = s.Min > float64(s.MinSigned()) || s.Max < float64(s.MaxSigned()) + } else { + hasConstrainedRange = s.Min > 0 || s.Max < float64(s.MaxUnsigned()) + } + return hasScale || hasOffset || hasRange && hasConstrainedRange +} + +func hasCustomType(s *descriptor.Signal) bool { + return len(s.ValueDescriptions) > 0 +} + +func hasSendType(d *descriptor.Database) bool { + for _, m := range d.Messages { + if m == nil { + continue + } + + if m.SendType != descriptor.SendTypeNone { + return true + } + } + return false +} + +func signalType(m *descriptor.Message, s *descriptor.Signal) string { + if hasCustomType(s) { + return m.Name + "_" + s.Name + } + return signalPrimitiveType(s).String() +} + +func signalPrimitiveType(s *descriptor.Signal) types.Type { + var t types.BasicKind + switch { + case s.Length == 1: + t = types.Bool + case s.Length <= 8 && s.IsSigned: + t = types.Int8 + case s.Length <= 8: + t = types.Uint8 + case s.Length <= 16 && s.IsSigned: + t = types.Int16 + case s.Length <= 16: + t = types.Uint16 + case s.Length <= 32 && s.IsSigned: + t = types.Int32 + case s.Length <= 32: + t = types.Uint32 + case s.Length <= 64 && s.IsSigned: + t = types.Int64 + default: + t = types.Uint64 + } + return types.Typ[t] +} + +func signalPrimitiveSuperType(s *descriptor.Signal) types.Type { + var t types.BasicKind + switch { + case s.Length == 1: + t = types.Bool + case s.IsSigned: + t = types.Int64 + default: + t = types.Uint64 + } + return types.Typ[t] +} + +func signalSuperType(s *descriptor.Signal) string { + switch { + case s.Length == 1: + return "Bool" + case s.IsSigned: + return "Signed" + default: + return "Unsigned" + } +} + +func nodeInterface(n *descriptor.Node) string { + return n.Name +} + +func nodeStruct(n *descriptor.Node) string { + return "xxx_" + n.Name +} + +func messageStruct(m *descriptor.Message) string { + return m.Name +} + +func messageReaderInterface(m *descriptor.Message) string { + return m.Name + "Reader" +} + +func messageWriterInterface(m *descriptor.Message) string { + return m.Name + "Writer" +} + +func messageField(m *descriptor.Message) string { + return "xxx_" + m.Name +} + +func signalField(s *descriptor.Signal) string { + return "xxx_" + s.Name +} + +func nodeDescriptor(n *descriptor.Node) string { + return "Nodes()." + n.Name +} + +func messageDescriptor(m *descriptor.Message) string { + return "Messages()." + m.Name +} + +func signalDescriptor(m *descriptor.Message, s *descriptor.Signal) string { + return messageDescriptor(m) + "." + s.Name +} diff --git a/pkg/can-go/internal/generate/file_test.go b/pkg/can-go/internal/generate/file_test.go new file mode 100644 index 0000000..b800aae --- /dev/null +++ b/pkg/can-go/internal/generate/file_test.go @@ -0,0 +1,18 @@ +package generate + +import ( + "os" + "testing" + + "gotest.tools/v3/assert" +) + +func runTestInDir(t *testing.T, dir string) func() { + // change working directory to project root + wd, err := os.Getwd() + assert.NilError(t, err) + assert.NilError(t, os.Chdir(dir)) + return func() { + assert.NilError(t, os.Chdir(wd)) + } +} diff --git a/pkg/can-go/internal/identifiers/case.go b/pkg/can-go/internal/identifiers/case.go new file mode 100644 index 0000000..aaa208d --- /dev/null +++ b/pkg/can-go/internal/identifiers/case.go @@ -0,0 +1,17 @@ +package identifiers + +import "unicode" + +func IsCamelCase(s string) bool { + i := 0 + for _, r := range s { + if unicode.IsDigit(r) { + continue + } + if i == 0 && !unicode.IsUpper(r) || !IsAlphaChar(r) && !IsNumChar(r) { + return false + } + i++ + } + return true +} diff --git a/pkg/can-go/internal/identifiers/case_test.go b/pkg/can-go/internal/identifiers/case_test.go new file mode 100644 index 0000000..6b7bd5c --- /dev/null +++ b/pkg/can-go/internal/identifiers/case_test.go @@ -0,0 +1,18 @@ +package identifiers + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestIsCamelCase(t *testing.T) { + assert.Assert(t, IsCamelCase("SOC")) + assert.Assert(t, IsCamelCase("Camel")) + assert.Assert(t, IsCamelCase("CamelCase")) + assert.Assert(t, IsCamelCase("111CamelCaseNr")) + assert.Assert(t, !IsCamelCase("camelCase")) + assert.Assert(t, !IsCamelCase("snake_case")) + assert.Assert(t, !IsCamelCase("kebab-case")) + assert.Assert(t, !IsCamelCase("111camelCaseNr")) +} diff --git a/pkg/can-go/internal/identifiers/char.go b/pkg/can-go/internal/identifiers/char.go new file mode 100644 index 0000000..f99d800 --- /dev/null +++ b/pkg/can-go/internal/identifiers/char.go @@ -0,0 +1,9 @@ +package identifiers + +func IsAlphaChar(r rune) bool { + return ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') +} + +func IsNumChar(r rune) bool { + return '0' <= r && r <= '9' +} diff --git a/pkg/can-go/internal/identifiers/char_test.go b/pkg/can-go/internal/identifiers/char_test.go new file mode 100644 index 0000000..4def81a --- /dev/null +++ b/pkg/can-go/internal/identifiers/char_test.go @@ -0,0 +1,23 @@ +package identifiers + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestIsAlphaChar(t *testing.T) { + assert.Assert(t, IsAlphaChar('b')) + assert.Assert(t, IsAlphaChar('C')) + assert.Assert(t, !IsAlphaChar('Ö')) + assert.Assert(t, !IsAlphaChar('_')) +} + +func TestIsNumChar(t *testing.T) { + assert.Assert(t, IsNumChar('0')) + assert.Assert(t, IsNumChar('1')) + assert.Assert(t, IsNumChar('2')) + assert.Assert(t, IsNumChar('9')) + assert.Assert(t, !IsNumChar('/')) + assert.Assert(t, !IsNumChar('a')) +} diff --git a/pkg/can-go/internal/reinterpret/reinterpret.go b/pkg/can-go/internal/reinterpret/reinterpret.go new file mode 100644 index 0000000..2a4e46a --- /dev/null +++ b/pkg/can-go/internal/reinterpret/reinterpret.go @@ -0,0 +1,50 @@ +// Package reinterpret provides primitives for reinterpreting arbitrary-length values as signed or unsigned. +package reinterpret + +// AsSigned reinterprets the provided unsigned value as a signed value. +func AsSigned(unsigned uint64, bits uint8) int64 { + switch bits { + case 8: + return int64(int8(uint8(unsigned))) + case 16: + return int64(int16(uint16(unsigned))) + case 32: + return int64(int32(uint32(unsigned))) + case 64: + return int64(unsigned) + default: + // calculate bit mask for sign bit + signBitMask := uint64(1 << (bits - 1)) + // check if sign bit is set + isNegative := unsigned&signBitMask > 0 + if !isNegative { + // sign bit not set means we can reinterpret the value as-is + return int64(unsigned) + } + // calculate bit mask for extracting value bits (all bits except the sign bit) + valueBitMask := signBitMask - 1 + // calculate two's complement of the value bits + value := ((^unsigned) & valueBitMask) + 1 + // result is the negative value of the two's complement + return -1 * int64(value) + } +} + +// AsUnsigned reinterprets the provided signed value as an unsigned value. +func AsUnsigned(signed int64, bits uint8) uint64 { + switch bits { + case 8: + return uint64(uint8(int8(signed))) + case 16: + return uint64(uint16(int16(signed))) + case 32: + return uint64(uint32(int32(signed))) + case 64: + return uint64(signed) + default: + // calculate bit mask for extracting relevant bits + valueBitMask := uint64(1< 8*len(p.Data)-1 { + return false + } + // calculate which byte the bit belongs to + byteIndex := i / 8 + // calculate bit mask for extracting the bit + bitMask := uint8(1 << (i % 8)) + // mocks the bit + bit := p.Data[byteIndex]&bitMask > 0 + // done + return bit +} + +// SetBit sets the value of the i:th bit in the data. +func (p *Payload) SetBit(i uint16, value bool) { + if int(i) > 8*len(p.Data)-1 { + return + } + byteIndex := i / 8 + bitIndex := i % 8 + if value { + p.Data[byteIndex] |= uint8(1 << bitIndex) + } else { + p.Data[byteIndex] &= ^uint8(1 << bitIndex) + } +} + +// PackLittleEndian packs the byte array into a continuous little endian big.Int. +func (p *Payload) PackLittleEndian() *big.Int { + if p.PackedLittleEndian == nil { + packed := new(big.Int).SetBytes(reverse(p.Data)) + p.PackedLittleEndian = packed + } + return new(big.Int).Set(p.PackedLittleEndian) +} + +// Reverse byte array for little endian signals. +func reverse(data []byte) []byte { + reversedArray := make([]byte, len(data)) + for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 { + reversedArray[i], reversedArray[j] = data[j], data[i] + } + return reversedArray +} + +// PackBigEndian packs the byte array into a continuous big endian big.Int. +func (p *Payload) PackBigEndian() *big.Int { + if p.PackedBigEndian == nil { + packed := new(big.Int).SetBytes(p.Data) + p.PackedBigEndian = packed + } + return new(big.Int).Set(p.PackedBigEndian) +} + +// TODO: Implement UnpackLittleEndian for Payload. +// UnpackLittleEndian sets the value of d.Bytes by unpacking the provided value as sequential little-endian bits. +// func (d *Data) UnpackLittleEndian(packed uint64) { +// d[0] = uint8(packed >> (0 * 8)) +// d[1] = uint8(packed >> (1 * 8)) +// d[2] = uint8(packed >> (2 * 8)) +// d[3] = uint8(packed >> (3 * 8)) +// d[4] = uint8(packed >> (4 * 8)) +// d[5] = uint8(packed >> (5 * 8)) +// d[6] = uint8(packed >> (6 * 8)) +// d[7] = uint8(packed >> (7 * 8)) +// } + +// TODO: Implement UnpackBigEndian for Payload. +// UnpackBigEndian sets the value of d.Bytes by unpacking the provided value as sequential big-endian bits. +// func (d *Data) UnpackBigEndian(packed uint64) { +// d[0] = uint8(packed >> (7 * 8)) +// d[1] = uint8(packed >> (6 * 8)) +// d[2] = uint8(packed >> (5 * 8)) +// d[3] = uint8(packed >> (4 * 8)) +// d[4] = uint8(packed >> (3 * 8)) +// d[5] = uint8(packed >> (2 * 8)) +// d[6] = uint8(packed >> (1 * 8)) +// d[7] = uint8(packed >> (0 * 8)) +// } + +// invertEndian converts from big-endian to little-endian bit indexing and vice versa. +func (p *Payload) invertEndian(i uint16) uint16 { + row := i / 8 + col := i % 8 + oppositeRow := uint16(len(p.Data)) - row - 1 + bitIndex := (oppositeRow * 8) + col + return bitIndex +} + +// AsSigned reinterprets the provided unsigned value as a signed value. +func AsSigned(unsigned uint64, bits uint16) int64 { + switch bits { + case 8: + return int64(int8(uint8(unsigned))) + case 16: + return int64(int16(uint16(unsigned))) + case 32: + return int64(int32(uint32(unsigned))) + case 64: + return int64(unsigned) + default: + // calculate bit mask for sign bit + signBitMask := uint64(1 << (bits - 1)) + // check if sign bit is set + isNegative := unsigned&signBitMask > 0 + if !isNegative { + // sign bit not set means we can reinterpret the value as-is + return int64(unsigned) + } + // calculate bit mask for extracting value bits (all bits except the sign bit) + valueBitMask := signBitMask - 1 + // calculate two's complement of the value bits + value := ((^unsigned) & valueBitMask) + 1 + // result is the negative value of the two's complement + return -1 * int64(value) + } +} diff --git a/pkg/can-go/payload_test.go b/pkg/can-go/payload_test.go new file mode 100644 index 0000000..1ed28bd --- /dev/null +++ b/pkg/can-go/payload_test.go @@ -0,0 +1,99 @@ +package can + +import ( + "fmt" + "testing" +) + +type signals struct { + start uint16 + length uint16 + unsigned uint64 + signed int64 +} + +func TestPackLittleEndian(t *testing.T) { + // 302064448 + // 10010000000010010001101000000 + data := []byte{0x40, 0x23, 0x01, 0x12} + payload := Payload{Data: data} + + dataLittleEndian := payload.PackLittleEndian() + fmt.Println(dataLittleEndian) + fmt.Printf("%b\n", dataLittleEndian) +} + +func TestPackBigEndian(t *testing.T) { + // 4621538819433299968 + // 100000000100011000000010001001000000000000000000000000000000000 + data := []byte{0x40, 0x23, 0x01, 0x12} + payload := Payload{Data: data} + + dataBigEndian := payload.PackBigEndian() + fmt.Println(dataBigEndian) + fmt.Printf("%b\n", dataBigEndian) +} + +func TestUnsignedLittleEndian(t *testing.T) { + // 18 + data := []byte{0x40, 0x23, 0x01, 0x12} + payload := Payload{Data: data} + signal := signals{start: 24, length: 8, unsigned: 0x12, signed: 18} + fmt.Println(payload.UnsignedBitsLittleEndian(signal.start, signal.length)) +} + +func TestUnsignedBigEndian(t *testing.T) { + // 3219 + data := []byte{0x3f, 0xf7, 0x0d, 0xc4, 0x0c, 0x93, 0xff, 0xff} + payload := Payload{Data: data} + signal := signals{start: 39, length: 16, unsigned: 0xc93, signed: 3219} + fmt.Println(payload.UnsignedBitsBigEndian(signal.start, signal.length)) +} + +func TestSignedLittleEndian(t *testing.T) { + // -1 + data := []byte{0x80, 0x01} + payload := Payload{Data: data} + signal := signals{start: 7, length: 2, unsigned: 0x3, signed: -1} + fmt.Println(payload.SignedBitsLittleEndian(signal.start, signal.length)) +} + +func TestSignedBigEndian(t *testing.T) { + // -9 + data := []byte{0x3f, 0xf7, 0x0d, 0xc4, 0x0c, 0x93, 0xff, 0xff} + payload := Payload{Data: data} + signal := signals{start: 3, length: 12, unsigned: 0xff7, signed: -9} + fmt.Println(payload.SignedBitsBigEndian(signal.start, signal.length)) +} + +func Benchmark4BytesPayload_PackLittleEndian(b *testing.B) { + data := []byte{0x40, 0x23, 0x01, 0x12} + payload := Payload{Data: data} + for i := 0; i < b.N; i++ { + _ = payload.PackLittleEndian() + } +} + +func Benchmark4BytesPayload_PackBigEndian(b *testing.B) { + data := []byte{0x40, 0x23, 0x01, 0x12} + payload := Payload{Data: data} + for i := 0; i < b.N; i++ { + _ = payload.PackBigEndian() + } +} + +func Benchmark4BytesPayload_UnsignedBitsLittleEndian(b *testing.B) { + data := []byte{0x40, 0x23, 0x01, 0x12} + payload := Payload{Data: data} + for i := 0; i < b.N; i++ { + _ = payload.UnsignedBitsLittleEndian(0, 16) + } +} + +func Benchmark4BytesPayload_UnsignedBitsBigEndian(b *testing.B) { + data := []byte{0x40, 0x23, 0x01, 0x12} + payload := Payload{Data: data} + for i := 0; i < b.N; i++ { + _ = payload.UnsignedBitsBigEndian(0, 16) + } +} diff --git a/pkg/can-go/pkg/candebug/http.go b/pkg/can-go/pkg/candebug/http.go new file mode 100644 index 0000000..a17dfd4 --- /dev/null +++ b/pkg/can-go/pkg/candebug/http.go @@ -0,0 +1,98 @@ +package candebug + +import ( + "bytes" + "net/http" + "path" + "strconv" + "time" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/cantext" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/generated" +) + +func ServeMessagesHTTP(w http.ResponseWriter, r *http.Request, msgs []generated.Message) { + base := path.Base(r.URL.Path) + // if path ends with a message name, serve only that message + for _, m := range msgs { + if m.Descriptor().Name == base { + serveMessagesHTTP(w, r, []generated.Message{m}) + return + } + } + serveMessagesHTTP(w, r, msgs) +} + +func serveMessagesHTTP(w http.ResponseWriter, _ *http.Request, msgs []generated.Message) { + var buf []byte + for i, m := range msgs { + buf = appendMessage(buf, m) + if i != len(msgs)-1 { + buf = append(buf, "\n\n\n"...) + } + } + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.WriteHeader(http.StatusOK) + _, _ = w.Write(buf) +} + +func appendMessage(buf []byte, m generated.Message) []byte { + name := m.Descriptor().Name + sep := append(bytes.Repeat([]byte{'='}, len(name)), '\n') + buf = append(buf, name...) + buf = append(buf, '\n') + buf = append(buf, sep...) + buf = cantext.AppendID(buf, m.Descriptor()) + buf = append(buf, '\n') + buf = cantext.AppendSender(buf, m.Descriptor()) + buf = append(buf, '\n') + buf = cantext.AppendSendType(buf, m.Descriptor()) + buf = append(buf, '\n') + if m.Descriptor().SendType == descriptor.SendTypeCyclic { + if enabler, ok := m.(interface{ IsCyclicTransmissionEnabled() bool }); ok { + buf = append(buf, "Enabled: "...) + buf = strconv.AppendBool(buf, enabler.IsCyclicTransmissionEnabled()) + buf = append(buf, '\n') + } + buf = cantext.AppendCycleTime(buf, m.Descriptor()) + buf = append(buf, '\n') + } + if m.Descriptor().DelayTime != 0 { + buf = cantext.AppendDelayTime(buf, m.Descriptor()) + buf = append(buf, '\n') + } + buf = append(buf, sep...) + if timer, ok := m.(interface{ ReceiveTime() time.Time }); ok { + buf = append(buf, "Received: "...) + buf = appendTime(buf, timer.ReceiveTime()) + buf = append(buf, '\n') + buf = append(buf, sep...) + } + if timer, ok := m.(interface{ TransmitTime() time.Time }); ok { + buf = append(buf, "Transmitted: "...) + buf = appendTime(buf, timer.TransmitTime()) + buf = append(buf, '\n') + buf = append(buf, sep...) + } + f := m.Frame() + for i, s := range m.Descriptor().Signals { + buf = cantext.AppendSignal(buf, s, f.Data) + if i < len(m.Descriptor().Signals)-1 { + buf = append(buf, '\n') + } + } + return buf +} + +func appendTime(buf []byte, t time.Time) []byte { + if t.IsZero() { + buf = append(buf, "never"...) + return buf + } + buf = append(buf, time.Since(t).String()...) + buf = append(buf, " ago ("...) + buf = t.AppendFormat(buf, "15:04:05.000000000") + buf = append(buf, ")"...) + return buf +} diff --git a/pkg/can-go/pkg/candebug/http_test.go b/pkg/can-go/pkg/candebug/http_test.go new file mode 100644 index 0000000..e0aca8e --- /dev/null +++ b/pkg/can-go/pkg/candebug/http_test.go @@ -0,0 +1,142 @@ +package candebug + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + can "github.com/fiskerinc/cloud-services/pkg/can-go" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/generated" + "gotest.tools/v3/assert" +) + +func TestServeMessagesHTTP_Single(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ServeMessagesHTTP(w, r, []generated.Message{ + &testMessage{ + frame: can.Frame{ID: 100, Length: 1}, + descriptor: newDriverHeartbeatDescriptor(), + }, + }) + })) + res, err := http.Get(ts.URL) + assert.NilError(t, err) + response, err := ioutil.ReadAll(res.Body) + assert.NilError(t, err) + assert.NilError(t, res.Body.Close()) + const expected = ` +DriverHeartbeat +=============== +ID: 100 (0x64) +Sender: DRIVER +SendType: Cyclic +CycleTime: 100ms +DelayTime: 2s +=============== +Command: 0 (0x0) None +` + assert.Equal(t, strings.TrimSpace(expected), string(response)) +} + +func TestServeMessagesHTTP_Multi(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ServeMessagesHTTP(w, r, []generated.Message{ + &testMessage{ + frame: can.Frame{ID: 100, Length: 1}, + descriptor: newDriverHeartbeatDescriptor(), + }, + &testMessage{ + frame: can.Frame{ID: 100, Length: 1, Data: can.Data{0x01}}, + descriptor: newDriverHeartbeatDescriptor(), + }, + }) + })) + res, err := http.Get(ts.URL) + assert.NilError(t, err) + response, err := ioutil.ReadAll(res.Body) + assert.NilError(t, err) + assert.NilError(t, res.Body.Close()) + const expected = ` +DriverHeartbeat +=============== +ID: 100 (0x64) +Sender: DRIVER +SendType: Cyclic +CycleTime: 100ms +DelayTime: 2s +=============== +Command: 0 (0x0) None + + +DriverHeartbeat +=============== +ID: 100 (0x64) +Sender: DRIVER +SendType: Cyclic +CycleTime: 100ms +DelayTime: 2s +=============== +Command: 1 (0x1) Sync +` + assert.Equal(t, strings.TrimSpace(expected), string(response)) +} + +type testMessage struct { + frame can.Frame + descriptor *descriptor.Message +} + +func (m *testMessage) Frame() can.Frame { + return m.frame +} + +func (m *testMessage) Descriptor() *descriptor.Message { + return m.descriptor +} + +func (m *testMessage) MarshalFrame() (can.Frame, error) { + panic("should not be called") +} + +func (testMessage) Reset() { + panic("should not be called") +} + +func (testMessage) String() string { + panic("should not be called") +} + +func (testMessage) UnmarshalFrame(can.Frame) error { + panic("should not be called") +} + +func newDriverHeartbeatDescriptor() *descriptor.Message { + return &descriptor.Message{ + Name: "DriverHeartbeat", + SenderNode: "DRIVER", + ID: 100, + Length: 1, + Description: "Sync message used to synchronize the controllers", + SendType: descriptor.SendTypeCyclic, + CycleTime: 100 * time.Millisecond, + DelayTime: 2 * time.Second, + Signals: []*descriptor.Signal{ + { + Name: "Command", + Start: 0, + Length: 8, + Scale: 1, + ValueDescriptions: []*descriptor.ValueDescription{ + {Value: 0, Description: "None"}, + {Value: 1, Description: "Sync"}, + {Value: 2, Description: "Reboot"}, + }, + ReceiverNodes: []string{"SENSOR", "MOTOR"}, + }, + }, + } +} diff --git a/pkg/can-go/pkg/canjson/encode.go b/pkg/can-go/pkg/canjson/encode.go new file mode 100644 index 0000000..6f6d019 --- /dev/null +++ b/pkg/can-go/pkg/canjson/encode.go @@ -0,0 +1,105 @@ +package canjson + +import ( + "encoding/json" + "fmt" + "strconv" + + can "github.com/fiskerinc/cloud-services/pkg/can-go" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/generated" +) + +// preAllocatedBytesPerSignal is an estimate of how many bytes each signal needs. +const preAllocatedBytesPerSignal = 40 + +// Marshal a CAN message to JSON. +func Marshal(m generated.Message) ([]byte, error) { + f := m.Frame() + bytes := make([]byte, 0, len(m.Descriptor().Signals)*preAllocatedBytesPerSignal) + bytes = append(bytes, '{') + for i, s := range m.Descriptor().Signals { + s := s + bytes = append(bytes, '"') + bytes = append(bytes, s.Name...) + bytes = append(bytes, `":`...) + sig := &signal{} + sig.set(s, f) + jsonSig, err := json.Marshal(sig) + if err != nil { + return nil, fmt.Errorf("marshal json: %w", err) + } + bytes = append(bytes, jsonSig...) + if i < len(m.Descriptor().Signals)-1 { + bytes = append(bytes, ',') + } + } + bytes = append(bytes, '}') + return bytes, nil +} + +type signal struct { + Raw json.Number + Physical json.Number + Unit string `json:",omitempty"` + Description string `json:",omitempty"` +} + +func (s *signal) set(desc *descriptor.Signal, f can.Frame) { + switch { + case desc.Length == 1: // bool + s.setBoolValue(desc.UnmarshalBool(f.Data), desc) + case desc.IsSigned: // signed + s.setSignedValue(desc.UnmarshalSigned(f.Data), desc) + default: // unsigned + s.setUnsignedValue(desc.UnmarshalUnsigned(f.Data), desc) + } +} + +func (s *signal) setUnsignedValue(value uint64, desc *descriptor.Signal) { + s.Raw = uintToJSON(value) + s.Physical = floatToJSON(desc.ToPhysical(float64(value))) + s.Unit = desc.Unit + if value, ok := desc.ValueDescription(int(value)); ok { + s.Description = value + } +} + +func (s *signal) setSignedValue(value int64, desc *descriptor.Signal) { + s.Raw = intToJSON(value) + s.Physical = floatToJSON(desc.ToPhysical(float64(value))) + s.Unit = desc.Unit + if value, ok := desc.ValueDescription(int(value)); ok { + s.Description = value + } +} + +func (s *signal) setBoolValue(value bool, desc *descriptor.Signal) { + if value { + s.Raw = "1" + s.Physical = floatToJSON(desc.ToPhysical(1)) + } else { + s.Raw = "0" + s.Physical = floatToJSON(desc.ToPhysical(0)) + } + s.Unit = desc.Unit + intValue := 0 + if value { + intValue = 1 + } + if value, ok := desc.ValueDescription(intValue); ok { + s.Description = value + } +} + +func floatToJSON(f float64) json.Number { + return json.Number(strconv.FormatFloat(f, 'f', -1, 64)) +} + +func intToJSON(i int64) json.Number { + return json.Number(strconv.Itoa(int(i))) +} + +func uintToJSON(i uint64) json.Number { + return json.Number(strconv.Itoa(int(i))) +} diff --git a/pkg/can-go/pkg/canjson/encode_test.go b/pkg/can-go/pkg/canjson/encode_test.go new file mode 100644 index 0000000..87527a8 --- /dev/null +++ b/pkg/can-go/pkg/canjson/encode_test.go @@ -0,0 +1,19 @@ +package canjson + +import ( + "strings" + "testing" + + examplecan "github.com/fiskerinc/cloud-services/pkg/can-go/testdata/gen/go/example" + "gotest.tools/v3/assert" +) + +func TestMarshal(t *testing.T) { + driverHeartbeat := examplecan.NewDriverHeartbeat().SetCommand(examplecan.DriverHeartbeat_Command_Reboot) + js, err := Marshal(driverHeartbeat) + assert.NilError(t, err) + expected := strings.TrimSpace(` + {"Command":{"Raw":2,"Physical":2,"Description":"Reboot"}} + `) + assert.Equal(t, expected, string(js)) +} diff --git a/pkg/can-go/pkg/canrunner/run.go b/pkg/can-go/pkg/canrunner/run.go new file mode 100644 index 0000000..f4f8f89 --- /dev/null +++ b/pkg/can-go/pkg/canrunner/run.go @@ -0,0 +1,204 @@ +package canrunner + +import ( + "context" + "fmt" + "net" + "strings" + "sync" + "time" + + can "github.com/fiskerinc/cloud-services/pkg/can-go" + "github.com/fiskerinc/cloud-services/pkg/can-go/internal/clock" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/generated" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/socketcan" + "golang.org/x/sync/errgroup" +) + +// defaultSendTimeout is the send timeout used for messages without a cycle time. +const defaultSendTimeout = time.Second + +// Node is an interface for a CAN node to be run by the runner. +type Node interface { + sync.Locker + Connect() (net.Conn, error) + Descriptor() *descriptor.Node + TransmittedMessages() []TransmittedMessage + ReceivedMessage(id uint32) (ReceivedMessage, bool) +} + +// TransmittedMessage is an interface for a message to be transmitted by the runner. +type TransmittedMessage interface { + generated.Message + // SetTransmitTime sets the time the message was last transmitted. + SetTransmitTime(time.Time) + // IsCyclicTransmissionEnabled returns true when cyclic transmission is enabled. + IsCyclicTransmissionEnabled() bool + // WakeUpChan returns a channel for waking up and checking if cyclic transmission is enabled. + WakeUpChan() <-chan struct{} + // TransmitEventChan returns channel for event-based transmission of the message. + TransmitEventChan() <-chan struct{} + // BeforeTransmitHook returns a function to be called before the message is transmitted. + // + // If the hook returns an error, the transmitter halt. + BeforeTransmitHook() func(context.Context) error +} + +// ReceivedMessage is an interface for a message to be received by the runner. +type ReceivedMessage interface { + generated.Message + // SetReceiveTime sets the time the message was last received. + SetReceiveTime(time.Time) + // AfterReceiveHook returns a function to be called after the message has been received. + // + // If the hook returns an error, the receiver will halt. + AfterReceiveHook() func(context.Context) error +} + +// FrameTransmitter is an interface for the the CAN frame transmitter used by the runner. +type FrameTransmitter interface { + TransmitFrame(context.Context, can.Frame) error +} + +// FrameReceiver is an interface for the CAN frame receiver used by the runner. +type FrameReceiver interface { + Receive() bool + Frame() can.Frame + Err() error +} + +func Run(ctx context.Context, n Node) error { + conn, err := n.Connect() + if err != nil { + return fmt.Errorf("run %s node: %w", n.Descriptor().Name, err) + } + g, ctx := errgroup.WithContext(ctx) + g.Go(func() error { + <-ctx.Done() + return conn.Close() + }) + g.Go(func() error { + rx := socketcan.NewReceiver(conn) + return RunMessageReceiver(ctx, rx, n, clock.System()) + }) + for _, m := range n.TransmittedMessages() { + m := m + g.Go(func() error { + tx := socketcan.NewTransmitter(conn) + return RunMessageTransmitter(ctx, tx, n, m, clock.System()) + }) + } + if err := g.Wait(); err != nil { + if strings.Contains(err.Error(), "closed") { + return nil + } + return fmt.Errorf("run %s node: %w", n.Descriptor().Name, err) + } + return nil +} + +func RunMessageReceiver(ctx context.Context, rx FrameReceiver, n Node, c clock.Clock) error { + for rx.Receive() { + f := rx.Frame() + m, ok := n.ReceivedMessage(f.ID) + if !ok { + continue + } + n.Lock() + hook := m.AfterReceiveHook() + m.SetReceiveTime(c.Now()) + err := m.UnmarshalFrame(f) + n.Unlock() + if err != nil { + return fmt.Errorf("receiver: %w", err) + } + if err := hook(ctx); err != nil { + return fmt.Errorf("receiver: %w", err) + } + } + if err := rx.Err(); err != nil { + return fmt.Errorf("receiver: %w", err) + } + return nil +} + +func RunMessageTransmitter( + ctx context.Context, + tx FrameTransmitter, + l sync.Locker, + m TransmittedMessage, + c clock.Clock, +) error { + sendTimeout := m.Descriptor().CycleTime + if sendTimeout == 0 { + sendTimeout = defaultSendTimeout + } + var cyclicTransmissionTicker *time.Ticker + var cyclicTransmissionTickChan <-chan time.Time + enableCyclicTransmission := func() { + isCyclic := m.Descriptor().SendType == descriptor.SendTypeCyclic + hasCycleTime := m.Descriptor().CycleTime > 0 + if !isCyclic || !hasCycleTime || cyclicTransmissionTicker != nil { + return + } + cyclicTransmissionTicker = time.NewTicker(m.Descriptor().CycleTime) + cyclicTransmissionTickChan = cyclicTransmissionTicker.C + } + disableCyclicTransmission := func() { + if cyclicTransmissionTicker == nil { + return + } + cyclicTransmissionTicker.Stop() + cyclicTransmissionTicker = nil + } + setCyclicTransmission := func() { + l.Lock() + isCyclicTransmissionEnabled := m.IsCyclicTransmissionEnabled() + l.Unlock() + if isCyclicTransmissionEnabled { + enableCyclicTransmission() + } else { + disableCyclicTransmission() + } + } + transmit := func() error { + l.Lock() + hook := m.BeforeTransmitHook() + m.SetTransmitTime(c.Now()) + l.Unlock() + if err := hook(ctx); err != nil { + return fmt.Errorf("%s transmitter: %w", m.Descriptor().Name, err) + } + l.Lock() + f := m.Frame() + l.Unlock() + ctx, cancel := context.WithTimeout(ctx, sendTimeout) + err := tx.TransmitFrame(ctx, f) + cancel() + if err != nil { + return fmt.Errorf("%s transmitter: %w", m.Descriptor().Name, err) + } + return nil + } + ctxDone := ctx.Done() + transmitEventChan := m.TransmitEventChan() + setCyclicTransmission() + wakeUpChan := m.WakeUpChan() + for { + select { + case <-ctxDone: + return nil + case <-wakeUpChan: + setCyclicTransmission() + case <-transmitEventChan: + if err := transmit(); err != nil { + return err + } + case <-cyclicTransmissionTickChan: + if err := transmit(); err != nil { + return err + } + } + } +} diff --git a/pkg/can-go/pkg/canrunner/run_test.go b/pkg/can-go/pkg/canrunner/run_test.go new file mode 100644 index 0000000..fb12a03 --- /dev/null +++ b/pkg/can-go/pkg/canrunner/run_test.go @@ -0,0 +1,125 @@ +package canrunner_test + +import ( + "context" + "errors" + "os" + "testing" + "time" + + can "github.com/fiskerinc/cloud-services/pkg/can-go" + "github.com/fiskerinc/cloud-services/pkg/can-go/internal/gen/mock/mockcanrunner" + "github.com/fiskerinc/cloud-services/pkg/can-go/internal/gen/mock/mockclock" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/canrunner" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" + "github.com/golang/mock/gomock" + "golang.org/x/sync/errgroup" + "gotest.tools/v3/assert" +) + +func TestRunMessageReceiver_NoMessages(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + rx := mockcanrunner.NewMockFrameReceiver(ctrl) + node := mockcanrunner.NewMockNode(ctrl) + clock := mockclock.NewMockClock(ctrl) + ctx := context.Background() + // when the first receive fails + rx.EXPECT().Receive().Return(false) + rx.EXPECT().Err().Return(os.ErrClosed) + // then an error is returned + assert.Assert(t, errors.Is(canrunner.RunMessageReceiver(ctx, rx, node, clock), os.ErrClosed)) +} + +func TestRunMessageReceiver_ReceiveMessage(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + rx := mockcanrunner.NewMockFrameReceiver(ctrl) + node := mockcanrunner.NewMockNode(ctrl) + clock := mockclock.NewMockClock(ctrl) + msg := mockcanrunner.NewMockReceivedMessage(ctrl) + ctx := context.Background() + // when the first receive succeeds + frame := can.Frame{ID: 42} + rx.EXPECT().Receive().Return(true) + rx.EXPECT().Frame().Return(frame) + // then the receiver should do a message lookup + node.EXPECT().ReceivedMessage(frame.ID).Return(msg, true) + // and the node should be locked + node.EXPECT().Lock() + // and the message should be queried for a hook with the same context + afterReceiveHook := func(c context.Context) error { + assert.DeepEqual(t, ctx, c) + return nil + } + msg.EXPECT().AfterReceiveHook().Return(afterReceiveHook) + // and the receive time should be set + now := time.Unix(0, 1) + clock.EXPECT().Now().Return(now) + msg.EXPECT().SetReceiveTime(now) + // and the message should be called to unmarshal the frame + msg.EXPECT().UnmarshalFrame(frame) + // and the node should be unlocked + node.EXPECT().Unlock() + // when the next receive fails + rx.EXPECT().Receive().Return(false) + rx.EXPECT().Err().Return(nil) + // then the receiver should return + assert.NilError(t, canrunner.RunMessageReceiver(ctx, rx, node, clock)) +} + +func TestRunMessageTransmitter_TransmitEventMessage(t *testing.T) { + t.Skip() // TODO: fix deadlock flakynes. + ctrl := gomock.NewController(t) + defer ctrl.Finish() + tx := mockcanrunner.NewMockFrameTransmitter(ctrl) + node := mockcanrunner.NewMockNode(ctrl) + msg := mockcanrunner.NewMockTransmittedMessage(ctrl) + clock := mockclock.NewMockClock(ctrl) + desc := &descriptor.Message{ + Name: "TestMessage", + SendType: descriptor.SendTypeEvent, + } + transmitEventChan := make(chan struct{}) + wakeUpChan := make(chan struct{}) + ctx := context.Background() + msg.EXPECT().Descriptor().AnyTimes().Return(desc) + msg.EXPECT().TransmitEventChan().Return(transmitEventChan) + msg.EXPECT().WakeUpChan().Return(wakeUpChan) + // given a running transmitter + ctx, cancel := context.WithCancel(context.Background()) + var g errgroup.Group + g.Go(func() error { + return canrunner.RunMessageTransmitter(ctx, tx, node, msg, clock) + }) + // then message should be queried for if it has cyclic transmission enabled + node.EXPECT().Lock() + msg.EXPECT().IsCyclicTransmissionEnabled() + node.EXPECT().Unlock() + // then the node should be locked + node.EXPECT().Lock() + // and the time should be queried + now := time.Unix(0, 1) + clock.EXPECT().Now().Return(now) + // and the transmit hook should be queried with the same context + hook := func(c context.Context) error { + assert.Equal(t, ctx, c) + return nil + } + msg.EXPECT().BeforeTransmitHook().Return(hook) + // and the message should be marshaled to a CAN frame + frame := can.Frame{ID: 42} + // and the transmit time should be set + msg.EXPECT().SetTransmitTime(now) + // and the node should be unlocked + node.EXPECT().Unlock() + node.EXPECT().Lock() + msg.EXPECT().Frame().Return(frame) + node.EXPECT().Unlock() + // and the CAN frame should be transmitted + tx.EXPECT().TransmitFrame(gomock.Any(), frame) + // when the transmitter receives a transmit event + transmitEventChan <- struct{}{} + cancel() + assert.NilError(t, g.Wait()) +} diff --git a/pkg/can-go/pkg/cantext/encode.go b/pkg/can-go/pkg/cantext/encode.go new file mode 100644 index 0000000..fa29b57 --- /dev/null +++ b/pkg/can-go/pkg/cantext/encode.go @@ -0,0 +1,127 @@ +package cantext + +import ( + "strconv" + + can "github.com/fiskerinc/cloud-services/pkg/can-go" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/generated" +) + +// preAllocatedBytesPerSignal is an estimate of how many bytes each signal needs. +const preAllocatedBytesPerSignal = 40 + +func MessageString(m generated.Message) string { + return string(MarshalCompact(m)) +} + +func MarshalCompact(m generated.Message) []byte { + f := m.Frame() + buf := make([]byte, 0, len(m.Descriptor().Signals)*preAllocatedBytesPerSignal) + buf = append(buf, "{"...) + for i, s := range m.Descriptor().Signals { + buf = AppendSignalCompact(buf, s, f.Data) + if i != len(m.Descriptor().Signals)-1 { + buf = append(buf, ", "...) + } + } + buf = append(buf, "}"...) + return buf +} + +func Marshal(m generated.Message) []byte { + f := m.Frame() + // allocate space for one "extra" signal to account for message header + buf := make([]byte, 0, (len(m.Descriptor().Signals)+1)*preAllocatedBytesPerSignal) + buf = append(buf, m.Descriptor().Name...) + for _, s := range m.Descriptor().Signals { + buf = append(buf, "\n\t"...) + buf = AppendSignal(buf, s, f.Data) + } + return buf +} + +func AppendSignal(buf []byte, s *descriptor.Signal, d can.Data) []byte { + buf = append(buf, s.Name...) + buf = append(buf, ": "...) + switch { + case s.Length == 1: // bool + val := s.UnmarshalBool(d) + buf = strconv.AppendBool(buf, val) + case s.IsSigned: // signed + buf = strconv.AppendFloat(buf, s.UnmarshalPhysical(d), 'g', -1, 64) + buf = append(buf, s.Unit...) + buf = append(buf, " ("...) + buf = append(buf, "0x"...) + buf = strconv.AppendUint(buf, uint64(s.UnmarshalSigned(d)), 16) + buf = append(buf, ')') + default: // unsigned + buf = strconv.AppendFloat(buf, s.UnmarshalPhysical(d), 'g', -1, 64) + buf = append(buf, s.Unit...) + buf = append(buf, " ("...) + buf = append(buf, "0x"...) + buf = strconv.AppendUint(buf, s.UnmarshalUnsigned(d), 16) + buf = append(buf, ")"...) + } + if vd, ok := s.UnmarshalValueDescription(d); ok { + buf = append(buf, ' ') + buf = append(buf, vd...) + } + return buf +} + +func AppendSignalCompact(buf []byte, s *descriptor.Signal, d can.Data) []byte { + buf = append(buf, s.Name...) + buf = append(buf, ": "...) + valueDescription, hasValueDescription := s.UnmarshalValueDescription(d) + switch { + case hasValueDescription: + buf = append(buf, valueDescription...) + case s.Length == 1: // bool + val := s.UnmarshalBool(d) + buf = strconv.AppendBool(buf, val) + case s.IsSigned: // signed + buf = strconv.AppendFloat(buf, s.UnmarshalPhysical(d), 'g', -1, 64) + buf = append(buf, s.Unit...) + default: // unsigned + buf = strconv.AppendFloat(buf, s.UnmarshalPhysical(d), 'g', -1, 64) + buf = append(buf, s.Unit...) + } + return buf +} + +func AppendID(buf []byte, m *descriptor.Message) []byte { + buf = append(buf, "ID: "...) + buf = strconv.AppendUint(buf, uint64(m.ID), 10) + buf = append(buf, " (0x"...) + buf = strconv.AppendUint(buf, uint64(m.ID), 16) + buf = append(buf, ")"...) + return buf +} + +func AppendSender(buf []byte, m *descriptor.Message) []byte { + return appendAttributeString(buf, "Sender", m.SenderNode) +} + +func AppendSendType(buf []byte, m *descriptor.Message) []byte { + return appendAttributeString(buf, "SendType", m.SendType.String()) +} + +func AppendCycleTime(buf []byte, m *descriptor.Message) []byte { + return appendAttributeString(buf, "CycleTime", m.CycleTime.String()) +} + +func AppendDelayTime(buf []byte, m *descriptor.Message) []byte { + return appendAttributeString(buf, "DelayTime", m.DelayTime.String()) +} + +func AppendFrame(buf []byte, f can.Frame) []byte { + return appendAttributeString(buf, "Frame", f.String()) +} + +func appendAttributeString(buf []byte, name string, s string) []byte { + buf = append(buf, name...) + buf = append(buf, ": "...) + buf = append(buf, s...) + return buf +} diff --git a/pkg/can-go/pkg/cantext/encode_test.go b/pkg/can-go/pkg/cantext/encode_test.go new file mode 100644 index 0000000..c1934ab --- /dev/null +++ b/pkg/can-go/pkg/cantext/encode_test.go @@ -0,0 +1,234 @@ +package cantext + +import ( + "strings" + "testing" + "time" + + can "github.com/fiskerinc/cloud-services/pkg/can-go" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/generated" + "gotest.tools/v3/assert" +) + +func TestMarshal(t *testing.T) { + for _, tt := range []struct { + name string + msg generated.Message + expected string + expectedCompact string + }{ + { + name: "with enum", + msg: &testMessage{ + frame: can.Frame{ID: 100, Length: 1, Data: can.Data{2}}, + descriptor: newDriverHeartbeatDescriptor(), + }, + expected: ` +DriverHeartbeat + Command: 2 (0x2) Reboot +`, + expectedCompact: `{Command: Reboot}`, + }, + { + name: "with unit", + msg: &testMessage{ + frame: can.Frame{ID: 100, Length: 3, Data: can.Data{1, 0x7b}}, + descriptor: newMotorStatusDescriptor(), + }, + expected: ` +MotorStatus + WheelError: true + SpeedKph: 0.123km/h (0x7b) +`, + expectedCompact: `{WheelError: true, SpeedKph: 0.123km/h}`, + }, + } { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Run("standard", func(t *testing.T) { + txt := Marshal(tt.msg) + assert.Equal(t, strings.TrimSpace(tt.expected), string(txt)) + }) + t.Run("compact", func(t *testing.T) { + txt := MarshalCompact(tt.msg) + assert.Equal(t, strings.TrimSpace(tt.expectedCompact), string(txt)) + }) + }) + } +} + +func TestAppendID(t *testing.T) { + const expected = "ID: 100 (0x64)" + actual := string(AppendID([]byte{}, newDriverHeartbeatDescriptor())) + assert.Equal(t, expected, actual) +} + +func TestAppendSender(t *testing.T) { + const expected = "Sender: DRIVER" + actual := string(AppendSender([]byte{}, newDriverHeartbeatDescriptor())) + assert.Equal(t, expected, actual) +} + +func TestAppendSendType(t *testing.T) { + const expected = "SendType: Cyclic" + actual := string(AppendSendType([]byte{}, newDriverHeartbeatDescriptor())) + assert.Equal(t, expected, actual) +} + +func TestAppendCycleTime(t *testing.T) { + const expected = "CycleTime: 100ms" + actual := string(AppendCycleTime([]byte{}, newDriverHeartbeatDescriptor())) + assert.Equal(t, expected, actual) +} + +func TestAppendDelayTime(t *testing.T) { + const expected = "DelayTime: 2s" + actual := string(AppendDelayTime([]byte{}, newDriverHeartbeatDescriptor())) + assert.Equal(t, expected, actual) +} + +func TestAppendFrame(t *testing.T) { + const expected = "Frame: 042#123456" + actual := string(AppendFrame([]byte{}, can.Frame{ID: 0x42, Length: 3, Data: can.Data{0x12, 0x34, 0x56}})) + assert.Equal(t, expected, actual) +} + +func newDriverHeartbeatDescriptor() *descriptor.Message { + return &descriptor.Message{ + Name: (string)("DriverHeartbeat"), + ID: (uint32)(100), + IsExtended: (bool)(false), + Length: (uint16)(1), + Description: (string)("Sync message used to synchronize the controllers"), + SendType: descriptor.SendTypeCyclic, + CycleTime: 100 * time.Millisecond, + DelayTime: 2 * time.Second, + Signals: []*descriptor.Signal{ + { + Name: (string)("Command"), + Start: (uint16)(0), + Length: (uint16)(8), + IsBigEndian: (bool)(false), + IsSigned: (bool)(false), + IsMultiplexer: (bool)(false), + IsMultiplexed: (bool)(false), + MultiplexerValue: (uint)(0), + Offset: (float64)(0), + Scale: (float64)(1), + Min: (float64)(0), + Max: (float64)(0), + Unit: (string)(""), + Description: (string)(""), + ValueDescriptions: []*descriptor.ValueDescription{ + { + Value: (int)(0), + Description: (string)("None"), + }, + { + Value: (int)(1), + Description: (string)("Sync"), + }, + { + Value: (int)(2), + Description: (string)("Reboot"), + }, + }, + ReceiverNodes: []string{ + (string)("SENSOR"), + (string)("MOTOR"), + }, + DefaultValue: (int)(0), + }, + }, + SenderNode: (string)("DRIVER"), + } +} + +func newMotorStatusDescriptor() *descriptor.Message { + return &descriptor.Message{ + Name: (string)("MotorStatus"), + ID: (uint32)(400), + IsExtended: (bool)(false), + Length: (uint16)(3), + Description: (string)(""), + Signals: []*descriptor.Signal{ + { + Name: (string)("WheelError"), + Start: (uint16)(0), + Length: (uint16)(1), + IsBigEndian: (bool)(false), + IsSigned: (bool)(false), + IsMultiplexer: (bool)(false), + IsMultiplexed: (bool)(false), + MultiplexerValue: (uint)(0), + Offset: (float64)(0), + Scale: (float64)(1), + Min: (float64)(0), + Max: (float64)(0), + Unit: (string)(""), + Description: (string)(""), + ValueDescriptions: ([]*descriptor.ValueDescription)(nil), + ReceiverNodes: []string{ + (string)("DRIVER"), + (string)("IO"), + }, + DefaultValue: (int)(0), + }, + { + Name: (string)("SpeedKph"), + Start: (uint16)(8), + Length: (uint16)(16), + IsBigEndian: (bool)(false), + IsSigned: (bool)(false), + IsMultiplexer: (bool)(false), + IsMultiplexed: (bool)(false), + MultiplexerValue: (uint)(0), + Offset: (float64)(0), + Scale: (float64)(0.001), + Min: (float64)(0), + Max: (float64)(0), + Unit: (string)("km/h"), + Description: (string)(""), + ValueDescriptions: ([]*descriptor.ValueDescription)(nil), + ReceiverNodes: []string{ + (string)("DRIVER"), + (string)("IO"), + }, + DefaultValue: (int)(0), + }, + }, + SenderNode: (string)("MOTOR"), + CycleTime: (time.Duration)(100000000), + DelayTime: (time.Duration)(0), + } +} + +type testMessage struct { + frame can.Frame + descriptor *descriptor.Message +} + +func (m *testMessage) Frame() can.Frame { + return m.frame +} + +func (m *testMessage) Descriptor() *descriptor.Message { + return m.descriptor +} + +func (m *testMessage) MarshalFrame() (can.Frame, error) { + panic("should not be called") +} + +func (testMessage) Reset() { + panic("should not be called") +} + +func (testMessage) String() string { + panic("should not be called") +} + +func (testMessage) UnmarshalFrame(can.Frame) error { + panic("should not be called") +} diff --git a/pkg/can-go/pkg/dbc/accesstype.go b/pkg/can-go/pkg/dbc/accesstype.go new file mode 100644 index 0000000..b499d55 --- /dev/null +++ b/pkg/can-go/pkg/dbc/accesstype.go @@ -0,0 +1,26 @@ +package dbc + +import "fmt" + +// AccessType represents the access type of an environment variable. +type AccessType string + +const ( + AccessTypeUnrestricted AccessType = "DUMMY_NODE_VECTOR0" + AccessTypeRead AccessType = "DUMMY_NODE_VECTOR1" + AccessTypeWrite AccessType = "DUMMY_NODE_VECTOR2" + AccessTypeReadWrite AccessType = "DUMMY_NODE_VECTOR3" +) + +// Validate returns an error for invalid access types. +func (a AccessType) Validate() error { + switch a { + case AccessTypeUnrestricted: + case AccessTypeRead: + case AccessTypeWrite: + case AccessTypeReadWrite: + default: + return fmt.Errorf("invalid access type: %v", a) + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/accesstype_test.go b/pkg/can-go/pkg/dbc/accesstype_test.go new file mode 100644 index 0000000..3cbae45 --- /dev/null +++ b/pkg/can-go/pkg/dbc/accesstype_test.go @@ -0,0 +1,22 @@ +package dbc + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestAccessType_Validate(t *testing.T) { + for _, tt := range []AccessType{ + AccessTypeUnrestricted, + AccessTypeRead, + AccessTypeWrite, + AccessTypeReadWrite, + } { + assert.NilError(t, tt.Validate()) + } +} + +func TestAccessType_Validate_Error(t *testing.T) { + assert.ErrorContains(t, AccessType("foo").Validate(), "invalid access type") +} diff --git a/pkg/can-go/pkg/dbc/analysis/analysis.go b/pkg/can-go/pkg/dbc/analysis/analysis.go new file mode 100644 index 0000000..4277cbf --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/analysis.go @@ -0,0 +1,53 @@ +package analysis + +import ( + "fmt" + "strings" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" +) + +// An Analyzer describes an analysis function and its options. +type Analyzer struct { + // Name of the analyzer. + Name string + + // Doc is the documentation for the analyzer. + Doc string + + // Run the analyzer. + Run func(*Pass) error +} + +// Title is the part before the first "\n\n" of the documentation. +func (a *Analyzer) Title() string { + return strings.SplitN(a.Doc, "\n\n", 2)[0] +} + +// Validate the analyzer metadata. +func (a *Analyzer) Validate() error { + if a.Doc == "" { + return fmt.Errorf("missing doc") + } + return nil +} + +// A Diagnostic is a message associated with a source location. +type Diagnostic struct { + Pos scanner.Position + Message string +} + +// Pass is the interface to the run function that analyzes DBC definitions. +type Pass struct { + Analyzer *Analyzer + File *dbc.File + Diagnostics []*Diagnostic +} + +// Reportf reports a diagnostic by building a message from the provided format and args. +func (pass *Pass) Reportf(pos scanner.Position, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + pass.Diagnostics = append(pass.Diagnostics, &Diagnostic{Pos: pos, Message: msg}) +} diff --git a/pkg/can-go/pkg/dbc/analysis/analysistest/analysistest.go b/pkg/can-go/pkg/dbc/analysis/analysistest/analysistest.go new file mode 100644 index 0000000..5c55e45 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/analysistest/analysistest.go @@ -0,0 +1,38 @@ +package analysistest + +import ( + "strings" + "testing" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "gotest.tools/v3/assert" +) + +type Case struct { + Name string + Data string + Diagnostics []*analysis.Diagnostic +} + +func Run(t *testing.T, a *analysis.Analyzer, cs []*Case) { + t.Helper() + for _, c := range cs { + p := dbc.NewParser(c.Name, []byte(strings.TrimLeft(c.Data, "\n"))) + assert.NilError(t, p.Parse()) + pass := &analysis.Pass{ + Analyzer: a, + File: p.File(), + } + assert.NilError(t, a.Run(pass)) + // allow omitting byte offsets and file names + for _, d := range c.Diagnostics { + d.Pos.Offset = 0 + d.Pos.Filename = c.Name + } + for _, d := range pass.Diagnostics { + d.Pos.Offset = 0 + } + assert.DeepEqual(t, c.Diagnostics, pass.Diagnostics) + } +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/boolprefix/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/boolprefix/analyzer.go new file mode 100644 index 0000000..985350c --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/boolprefix/analyzer.go @@ -0,0 +1,60 @@ +package boolprefix + +import ( + "strings" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "boolprefix", + Doc: "check that bools (1-bit signals) have a correct prefix", + Run: run, + } +} + +func allowedPrefixes() []string { + return []string{ + "Is", + "Has", + } +} + +func run(pass *analysis.Pass) error { + for _, d := range pass.File.Defs { + messageDef, ok := d.(*dbc.MessageDef) + if !ok { + continue + } + SignalLoop: + for _, signalDef := range messageDef.Signals { + if signalDef.Size != 1 { + continue // skip all non-bool signals + } + for _, allowedPrefix := range allowedPrefixes() { + if strings.HasPrefix(string(signalDef.Name), allowedPrefix) { + continue SignalLoop // has allowed prefix + } + } + // edge-case: allow non-prefixed 1-bit signals with value descriptions + for _, d := range pass.File.Defs { + valueDescriptionsDef, ok := d.(*dbc.ValueDescriptionsDef) + if !ok { + continue // not value descriptions + } + if valueDescriptionsDef.MessageID == messageDef.MessageID && + valueDescriptionsDef.SignalName == signalDef.Name { + continue SignalLoop // has value descriptions + } + } + pass.Reportf( + signalDef.Pos, + "bool signals (1-bit) must have prefix %s", + strings.Join(allowedPrefixes(), " or "), + ) + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/boolprefix/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/boolprefix/analyzer_test.go new file mode 100644 index 0000000..7c5b3f5 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/boolprefix/analyzer_test.go @@ -0,0 +1,53 @@ +package boolprefix + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "prefix has", + Data: ` +BO_ 400 MOTOR_STATUS: 3 MOTOR + SG_ HasWheelError : 0|1@1+ (1,0) [0|0] "" DRIVER,IO +`, + }, + + { + Name: "prefix is", + Data: ` +BO_ 400 MOTOR_STATUS: 3 MOTOR + SG_ IsOverheated : 0|1@1+ (1,0) [0|0] "" DRIVER,IO +`, + }, + + { + Name: "missing prefix", + Data: ` +BO_ 400 MOTOR_STATUS: 3 MOTOR + SG_ WheelError : 0|1@1+ (1,0) [0|0] "" DRIVER,IO +`, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 2, Column: 2}, + Message: "bool signals (1-bit) must have prefix Is or Has", + }, + }, + }, + + { + Name: "missing prefix with value descriptions", + Data: ` +BO_ 400 MOTOR_STATUS: 3 MOTOR + SG_ Status : 0|1@1+ (1,0) [0|0] "" DRIVER,IO + +VAL_ 400 Status 1 "ValidDataPresent" 0 "NoData" ; +`, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/definitiontypeorder/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/definitiontypeorder/analyzer.go new file mode 100644 index 0000000..4408e6d --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/definitiontypeorder/analyzer.go @@ -0,0 +1,56 @@ +package definitiontypeorder + +import ( + "math" + "reflect" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "definitiontypeorder", + Doc: "check that definitions are in the correct order", + Run: run, + } +} + +func orderOf(def dbc.Def) uint64 { + for i, orderDef := range []dbc.Def{ + &dbc.VersionDef{}, + &dbc.NewSymbolsDef{}, + &dbc.BitTimingDef{}, + &dbc.NodesDef{}, + &dbc.ValueTableDef{}, + &dbc.MessageDef{}, + &dbc.MessageTransmittersDef{}, + &dbc.EnvironmentVariableDef{}, + &dbc.EnvironmentVariableDataDef{}, + &dbc.CommentDef{}, + &dbc.AttributeDef{}, + &dbc.AttributeDefaultValueDef{}, + &dbc.AttributeValueForObjectDef{}, + &dbc.ValueDescriptionsDef{}, + } { + if reflect.TypeOf(def) == reflect.TypeOf(orderDef) { + return uint64(i) + } + } + return math.MaxUint64 +} + +func run(pass *analysis.Pass) error { + minOrder := uint64(math.MaxUint64) + for i := range pass.File.Defs { + // diagnostics make more sense when going backwards + def := pass.File.Defs[len(pass.File.Defs)-i-1] + currOrder := orderOf(def) + if currOrder > minOrder { + pass.Reportf(def.Position(), "definition out of order") + } else { + minOrder = currOrder + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/definitiontypeorder/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/definitiontypeorder/analyzer_test.go new file mode 100644 index 0000000..34c3b44 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/definitiontypeorder/analyzer_test.go @@ -0,0 +1,56 @@ +package definitiontypeorder + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "correct order", + Data: ` +VERSION "foo" +NS_ : +BS_: +BU_: + `, + }, + + { + Name: "incorrect order", + Data: ` +VERSION "foo" +NS_ : +BU_: +BS_: + `, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 3, Column: 1}, + Message: "definition out of order", + }, + }, + }, + + { + Name: "unknown defs last", + Data: ` +VERSION "foo" +NS_ : +BS_: +FOO "bar" +BU_: + `, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 4, Column: 1}, + Message: "definition out of order", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/intervals/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/intervals/analyzer.go new file mode 100644 index 0000000..93397d4 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/intervals/analyzer.go @@ -0,0 +1,40 @@ +package intervals + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "intervals", + Doc: "check that all intervals are valid (min <= max)", + Run: run, + } +} + +func run(pass *analysis.Pass) error { + for _, def := range pass.File.Defs { + switch def := def.(type) { + case *dbc.EnvironmentVariableDef: + if def.Minimum > def.Maximum { + pass.Reportf(def.Pos, "invalid interval: [%f, %f]", def.Minimum, def.Maximum) + } + case *dbc.MessageDef: + for i := range def.Signals { + signal := &def.Signals[i] + if signal.Minimum > signal.Maximum { + pass.Reportf(def.Pos, "invalid interval: [%f, %f]", signal.Minimum, signal.Maximum) + } + } + case *dbc.AttributeDef: + if def.MinimumInt > def.MaximumInt || def.MinimumFloat > def.MaximumFloat { + pass.Reportf(def.Pos, "invalid interval: [%d, %d]", def.MinimumInt, def.MaximumInt) + } + if def.MinimumFloat > def.MaximumFloat { + pass.Reportf(def.Pos, "invalid interval: [%f, %f]", def.MinimumFloat, def.MaximumFloat) + } + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/intervals/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/intervals/analyzer_test.go new file mode 100644 index 0000000..1df9454 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/intervals/analyzer_test.go @@ -0,0 +1,29 @@ +package intervals + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "attribute interval ok", + Data: `BA_DEF_ "AttributeName" INT 0 10;`, + }, + + { + Name: "attribute interval bad", + Data: `BA_DEF_ "AttributeName" INT 10 0;`, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 1, Column: 1}, + Message: "invalid interval: [10, 0]", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/lineendings/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/lineendings/analyzer.go new file mode 100644 index 0000000..8a6802a --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/lineendings/analyzer.go @@ -0,0 +1,26 @@ +package lineendings + +import ( + "bytes" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "lineendings", + Doc: `check that the file does not contain Windows line-endings (\r\n)`, + Run: run, + } +} + +func run(pass *analysis.Pass) error { + if bytes.Contains(pass.File.Data, []byte{'\r', '\n'}) { + pass.Reportf( + scanner.Position{Filename: pass.File.Name, Line: 1, Column: 1}, + `file must not contain Windows line-endings (\r\n)`, + ) + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/lineendings/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/lineendings/analyzer_test.go new file mode 100644 index 0000000..7ba1869 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/lineendings/analyzer_test.go @@ -0,0 +1,29 @@ +package lineendings + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "ok", + Data: `NS_ :`, + }, + + { + Name: "not ok", + Data: "NS_ :\r\n", + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 1, Column: 1}, + Message: `file must not contain Windows line-endings (\r\n)`, + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/messagenames/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/messagenames/analyzer.go new file mode 100644 index 0000000..39bda32 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/messagenames/analyzer.go @@ -0,0 +1,28 @@ +package messagenames + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/internal/identifiers" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "messagenames", + Doc: "check that message names are valid CamelCase identifiers", + Run: run, + } +} + +func run(pass *analysis.Pass) error { + for _, def := range pass.File.Defs { + messageDef, ok := def.(*dbc.MessageDef) + if !ok { + continue // not a message + } + if !identifiers.IsCamelCase(string(messageDef.Name)) { + pass.Reportf(messageDef.Pos, "message names must be CamelCase") + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/messagenames/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/messagenames/analyzer_test.go new file mode 100644 index 0000000..5cc1a52 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/messagenames/analyzer_test.go @@ -0,0 +1,29 @@ +package messagenames + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "ok", + Data: `BO_ 100 DriverHeartbeat: 1 DRIVER`, + }, + + { + Name: "not ok", + Data: `BO_ 100 DRIVER_HEARTBEAT: 1 DRIVER`, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 1, Column: 1}, + Message: "message names must be CamelCase", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/multiplexedsignals/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/multiplexedsignals/analyzer.go new file mode 100644 index 0000000..ae75867 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/multiplexedsignals/analyzer.go @@ -0,0 +1,59 @@ +package multiplexedsignals + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "multiplexedsignals", + Doc: "check that multiplexed signals are valid", + Run: run, + } +} + +func run(pass *analysis.Pass) error { + for _, def := range pass.File.Defs { + message, ok := def.(*dbc.MessageDef) + if !ok { + continue + } + // locate multiplexer switch + var multiplexerSwitch *dbc.SignalDef + for i := range message.Signals { + if !message.Signals[i].IsMultiplexerSwitch { + continue + } + if multiplexerSwitch != nil { + pass.Reportf(message.Signals[i].Pos, "more than one multiplexer switch") + continue + } + multiplexerSwitch = &message.Signals[i] + if multiplexerSwitch.IsSigned { + pass.Reportf(message.Signals[i].Pos, "signed multiplexer switch") + continue + } + if multiplexerSwitch.IsMultiplexed { + pass.Reportf(message.Signals[i].Pos, "can't be multiplexer and multiplexed") + continue + } + } + for i := range message.Signals { + signal := &message.Signals[i] + if !signal.IsMultiplexed { + continue + } + if multiplexerSwitch == nil { + pass.Reportf(message.Signals[i].Pos, "no multiplexer switch for multiplexed signal") + continue + } + multiplexerSwitchMaxValue := uint64((1 << multiplexerSwitch.Size) - 1) + if signal.MultiplexerSwitch > multiplexerSwitchMaxValue { + pass.Reportf(signal.Pos, "multiplexer switch exceeds max value: %v", multiplexerSwitchMaxValue) + continue + } + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/multiplexedsignals/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/multiplexedsignals/analyzer_test.go new file mode 100644 index 0000000..4c01415 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/multiplexedsignals/analyzer_test.go @@ -0,0 +1,114 @@ +package multiplexedsignals + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "valid", + Data: ` +BO_ 200 SENSOR_SONARS: 8 SENSOR + SG_ SENSOR_SONARS_mux M : 0|4@1+ (1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_err_count : 4|12@1+ (1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_left m0 : 16|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_middle m0 : 28|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_right m0 : 40|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_rear m0 : 52|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_no_filt_left m1 : 16|12@1+ (0.1,0) [0|0] "" DBG + SG_ SENSOR_SONARS_no_filt_middle m1 : 28|12@1+ (0.1,0) [0|0] "" DBG + SG_ SENSOR_SONARS_no_filt_right m1 : 40|12@1+ (0.1,0) [0|0] "" DBG + SG_ SENSOR_SONARS_no_filt_rear m1 : 52|12@1+ (0.1,0) [0|0] "" DBG + `, + }, + + { + Name: "multiple multiplexer switches", + Data: ` +BO_ 200 SENSOR_SONARS: 8 SENSOR + SG_ SENSOR_SONARS_mux M : 0|4@1+ (1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_err_count M : 4|12@1+ (1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_left m0 : 16|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_middle m0 : 28|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_right m0 : 40|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_rear m0 : 52|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_no_filt_left m1 : 16|12@1+ (0.1,0) [0|0] "" DBG + SG_ SENSOR_SONARS_no_filt_middle m1 : 28|12@1+ (0.1,0) [0|0] "" DBG + SG_ SENSOR_SONARS_no_filt_right m1 : 40|12@1+ (0.1,0) [0|0] "" DBG + SG_ SENSOR_SONARS_no_filt_rear m1 : 52|12@1+ (0.1,0) [0|0] "" DBG + `, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 3, Column: 2}, + Message: "more than one multiplexer switch", + }, + }, + }, + + { + Name: "signed multiplexer switch", + Data: ` +BO_ 200 SENSOR_SONARS: 8 SENSOR + SG_ SENSOR_SONARS_mux M : 0|4@1- (1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_err_count : 4|12@1+ (1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_left m0 : 16|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_middle m0 : 28|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_right m0 : 40|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_rear m0 : 52|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_no_filt_left m1 : 16|12@1+ (0.1,0) [0|0] "" DBG + SG_ SENSOR_SONARS_no_filt_middle m1 : 28|12@1+ (0.1,0) [0|0] "" DBG + SG_ SENSOR_SONARS_no_filt_right m1 : 40|12@1+ (0.1,0) [0|0] "" DBG + SG_ SENSOR_SONARS_no_filt_rear m1 : 52|12@1+ (0.1,0) [0|0] "" DBG + `, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 2, Column: 2}, + Message: "signed multiplexer switch", + }, + }, + }, + + { + Name: "no multiplexer switch", + Data: ` +BO_ 200 SENSOR_SONARS: 8 SENSOR + SG_ SENSOR_SONARS_err_count : 4|12@1+ (1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_left m0 : 16|12@1+ (0.1,0) [0|0] "" DRIVER,IO + `, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 3, Column: 2}, + Message: "no multiplexer switch for multiplexed signal", + }, + }, + }, + + { + Name: "too big multiplexer switch", + Data: ` +BO_ 200 SENSOR_SONARS: 8 SENSOR + SG_ SENSOR_SONARS_mux M : 0|4@1+ (1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_err_count : 4|12@1+ (1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_left m16 : 16|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_middle m0 : 28|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_right m0 : 40|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_rear m0 : 52|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_no_filt_left m1 : 16|12@1+ (0.1,0) [0|0] "" DBG + SG_ SENSOR_SONARS_no_filt_middle m1 : 28|12@1+ (0.1,0) [0|0] "" DBG + SG_ SENSOR_SONARS_no_filt_right m1 : 40|12@1+ (0.1,0) [0|0] "" DBG + SG_ SENSOR_SONARS_no_filt_rear m1 : 52|12@1+ (0.1,0) [0|0] "" DBG + `, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 4, Column: 2}, + Message: "multiplexer switch exceeds max value: 15", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/newsymbols/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/newsymbols/analyzer.go new file mode 100644 index 0000000..38c3ba4 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/newsymbols/analyzer.go @@ -0,0 +1,27 @@ +package newsymbols + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "newsymbols", + Doc: "check that the new symbols definition is empty", + Run: run, + } +} + +func run(pass *analysis.Pass) error { + for _, def := range pass.File.Defs { + newSymbolsDef, ok := def.(*dbc.NewSymbolsDef) + if !ok { + continue // not a new symbols definition + } + if len(newSymbolsDef.Symbols) > 0 { + pass.Reportf(newSymbolsDef.Pos, "new symbols should be empty") + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/newsymbols/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/newsymbols/analyzer_test.go new file mode 100644 index 0000000..59b9397 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/newsymbols/analyzer_test.go @@ -0,0 +1,32 @@ +package newsymbols + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "ok", + Data: `NS_ :`, + }, + + { + Name: "not ok", + Data: ` +NS_ : + BA_DEF_DEF_REL_ + BA_DEF_SGTYPE_`, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 1, Column: 1}, + Message: "new symbols should be empty", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/nodereferences/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/nodereferences/analyzer.go new file mode 100644 index 0000000..af64f58 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/nodereferences/analyzer.go @@ -0,0 +1,58 @@ +package nodereferences + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "nodereferences", + Doc: "check that all node references refer to declared nodes", + Run: run, + } +} + +func run(pass *analysis.Pass) error { + declaredNodes := map[dbc.Identifier]struct{}{ + dbc.NodePlaceholder: {}, // placeholder is implicitly declared + } + // collect declared nodes + for _, def := range pass.File.Defs { + if def, ok := def.(*dbc.NodesDef); ok { + for _, nodeName := range def.NodeNames { + declaredNodes[nodeName] = struct{}{} + } + } + } + // verify node references + for _, def := range pass.File.Defs { + switch def := def.(type) { + case *dbc.MessageDef: + if _, ok := declaredNodes[def.Transmitter]; !ok { + pass.Reportf(def.Pos, "undeclared transmitter node: %v", def.Transmitter) + } + for i := range def.Signals { + signal := &def.Signals[i] + for _, receiver := range signal.Receivers { + if _, ok := declaredNodes[receiver]; !ok { + pass.Reportf(signal.Pos, "undeclared receiver node: %v", receiver) + } + } + } + case *dbc.EnvironmentVariableDef: + for _, accessNode := range def.AccessNodes { + if _, ok := declaredNodes[accessNode]; !ok { + pass.Reportf(def.Pos, "undeclared access node: %v", accessNode) + } + } + case *dbc.MessageTransmittersDef: + for _, transmitter := range def.Transmitters { + if _, ok := declaredNodes[transmitter]; !ok { + pass.Reportf(def.Pos, "undeclared transmitter node: %v", transmitter) + } + } + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/nodereferences/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/nodereferences/analyzer_test.go new file mode 100644 index 0000000..22f1e95 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/nodereferences/analyzer_test.go @@ -0,0 +1,52 @@ +package nodereferences + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "valid", + Data: ` +BU_: ECU1 ECU2 +BO_ 42 TestMessage: 8 ECU2 + SG_ CellTempLowest : 32|8@0+ (1,-40) [-40|215] "C" ECU1 + `, + }, + + { + Name: "undeclared transmitter", + Data: ` +BU_: ECU1 ECU2 +BO_ 42 TestMessage: 8 ECU3 + SG_ CellTempLowest : 32|8@0+ (1,-40) [-40|215] "C" ECU1 + `, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 2, Column: 1}, + Message: "undeclared transmitter node: ECU3", + }, + }, + }, + + { + Name: "undeclared receiver", + Data: ` +BU_: ECU1 ECU2 +BO_ 42 TestMessage: 8 ECU2 + SG_ CellTempLowest : 32|8@0+ (1,-40) [-40|215] "C" ECU2,ECU3 + `, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 3, Column: 2}, + Message: "undeclared receiver node: ECU3", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/noreservedsignals/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/noreservedsignals/analyzer.go new file mode 100644 index 0000000..d89ead7 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/noreservedsignals/analyzer.go @@ -0,0 +1,31 @@ +package noreservedsignals + +import ( + "strings" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "noreservedsignals", + Doc: `checks that no signals have the prefix "Reserved"`, + Run: run, + } +} + +func run(pass *analysis.Pass) error { + for _, d := range pass.File.Defs { + messageDef, ok := d.(*dbc.MessageDef) + if !ok { + continue + } + for _, signalDef := range messageDef.Signals { + if strings.HasPrefix(string(signalDef.Name), "Reserved") { + pass.Reportf(signalDef.Pos, "remove reserved signals") + } + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/noreservedsignals/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/noreservedsignals/analyzer_test.go new file mode 100644 index 0000000..764301d --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/noreservedsignals/analyzer_test.go @@ -0,0 +1,35 @@ +package noreservedsignals + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "ok", + Data: ` +BO_ 400 MotorStatus: 3 MOTOR + SG_ HasWheelError : 0|1@1+ (1,0) [0|0] "" DRIVER,IO +`, + }, + + { + Name: "not ok", + Data: ` +BO_ 400 MotorStatus: 3 MOTOR + SG_ Reserved1 : 0|1@1+ (1,0) [0|0] "" DRIVER,IO +`, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 2, Column: 2}, + Message: "remove reserved signals", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/requireddefinitions/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/requireddefinitions/analyzer.go new file mode 100644 index 0000000..8fb0f5f --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/requireddefinitions/analyzer.go @@ -0,0 +1,38 @@ +package requireddefinitions + +import ( + "reflect" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "requireddefinitions", + Doc: "check that the file contains exactly one of all required definitions", + Run: run, + } +} + +func requiredDefinitions() []dbc.Def { + return []dbc.Def{ + &dbc.BitTimingDef{}, + &dbc.NodesDef{}, + } +} + +func run(pass *analysis.Pass) error { + counts := make(map[reflect.Type]int) + for _, def := range pass.File.Defs { + counts[reflect.TypeOf(def)]++ + } + for _, requiredDef := range requiredDefinitions() { + if counts[reflect.TypeOf(requiredDef)] == 0 { + // we have no definition to return, so return the first + pass.Reportf(pass.File.Defs[0].Position(), "missing required definition(s)") + break + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/requireddefinitions/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/requireddefinitions/analyzer_test.go new file mode 100644 index 0000000..34a1ea7 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/requireddefinitions/analyzer_test.go @@ -0,0 +1,47 @@ +package requireddefinitions + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "ok", + Data: ` +BS_: +BU_: ECU1 + `, + }, + + { + Name: "missing bit timing", + Data: ` +BU_: ECU1 + `, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 1, Column: 1}, + Message: "missing required definition(s)", + }, + }, + }, + + { + Name: "missing nodes", + Data: ` +BS_: + `, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 1, Column: 1}, + Message: "missing required definition(s)", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/signalbounds/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/signalbounds/analyzer.go new file mode 100644 index 0000000..14d62b3 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/signalbounds/analyzer.go @@ -0,0 +1,31 @@ +package signalbounds + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "signalbounds", + Doc: "check that signal start and end bits are within bounds of the message size", + Run: run, + } +} + +func run(pass *analysis.Pass) error { + for _, def := range pass.File.Defs { + message, ok := def.(*dbc.MessageDef) + if !ok || dbc.IsIndependentSignalsMessage(message) { + continue + } + for i := range message.Signals { + signal := &message.Signals[i] + if signal.StartBit >= 8*message.Size { + pass.Reportf(signal.Pos, "start bit out of bounds") + } + // TODO: Check end bit + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/signalbounds/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/signalbounds/analyzer_test.go new file mode 100644 index 0000000..d2965ed --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/signalbounds/analyzer_test.go @@ -0,0 +1,41 @@ +package signalbounds + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "ok", + Data: ` +BO_ 500 IO_DEBUG: 4 IO + SG_ IO_DEBUG_test_unsigned : 0|8@1+ (1,0) [0|0] "" DBG + SG_ IO_DEBUG_test_enum : 8|8@1+ (1,0) [0|0] "" DBG + SG_ IO_DEBUG_test_signed : 16|8@1- (1,0) [0|0] "" DBG + SG_ IO_DEBUG_test_float : 24|8@1+ (0.5,0) [0|0] "" DBG +`, + }, + + { + Name: "start bit out of bounds", + Data: ` +BO_ 500 IO_DEBUG: 4 IO + SG_ IO_DEBUG_test_unsigned : 0|8@1+ (1,0) [0|0] "" DBG + SG_ IO_DEBUG_test_enum : 8|8@1+ (1,0) [0|0] "" DBG + SG_ IO_DEBUG_test_signed : 16|8@1- (1,0) [0|0] "" DBG + SG_ IO_DEBUG_test_float : 32|8@1+ (0.5,0) [0|0] "" DBG +`, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 5, Column: 2}, + Message: "start bit out of bounds", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/signalnames/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/signalnames/analyzer.go new file mode 100644 index 0000000..62985b4 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/signalnames/analyzer.go @@ -0,0 +1,30 @@ +package signalnames + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/internal/identifiers" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "signalnames", + Doc: "check that signal names are valid CamelCase identifiers", + Run: run, + } +} + +func run(pass *analysis.Pass) error { + for _, d := range pass.File.Defs { + messageDef, ok := d.(*dbc.MessageDef) + if !ok { + continue + } + for _, signalDef := range messageDef.Signals { + if !identifiers.IsCamelCase(string(signalDef.Name)) { + pass.Reportf(signalDef.Pos, "signal names must be CamelCase") + } + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/signalnames/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/signalnames/analyzer_test.go new file mode 100644 index 0000000..c80be72 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/signalnames/analyzer_test.go @@ -0,0 +1,35 @@ +package signalnames + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "ok", + Data: ` +BO_ 400 MotorStatus: 3 MOTOR + SG_ HasWheelError : 0|1@1+ (1,0) [0|0] "" DRIVER,IO +`, + }, + + { + Name: "not ok", + Data: ` +BO_ 400 MOTOR_STATUS: 3 MOTOR + SG_ IS_OVERHEATED : 0|1@1+ (1,0) [0|0] "" DRIVER,IO +`, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 2, Column: 2}, + Message: "signal names must be CamelCase", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/singletondefinitions/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/singletondefinitions/analyzer.go new file mode 100644 index 0000000..bac65ad --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/singletondefinitions/analyzer.go @@ -0,0 +1,42 @@ +package singletondefinitions + +import ( + "reflect" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "singletondefinitions", + Doc: "check that the file contains at most one of all singleton definitions", + Run: run, + } +} + +func singletonDefinitions() []dbc.Def { + return []dbc.Def{ + &dbc.VersionDef{}, + &dbc.NewSymbolsDef{}, + &dbc.BitTimingDef{}, + &dbc.NodesDef{}, + } +} + +func run(pass *analysis.Pass) error { + defsByType := make(map[reflect.Type][]dbc.Def) + for _, def := range pass.File.Defs { + t := reflect.TypeOf(def) + defsByType[t] = append(defsByType[t], def) + } + for _, singletonDef := range singletonDefinitions() { + singletonDefs := defsByType[reflect.TypeOf(singletonDef)] + if len(singletonDefs) > 1 { + for i := 1; i < len(singletonDefs); i++ { + pass.Reportf(singletonDefs[i].Position(), "more than one definition not allowed") + } + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/singletondefinitions/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/singletondefinitions/analyzer_test.go new file mode 100644 index 0000000..4e38a3e --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/singletondefinitions/analyzer_test.go @@ -0,0 +1,91 @@ +package singletondefinitions + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "ok", + Data: ` +VERSION "foo" +NS_: +BS_: +BU_: ECU1 + `, + }, + + { + Name: "multiple versions", + Data: ` +VERSION "foo" +VERSION "foo" +NS_: +BS_: +BU_: ECU1 + `, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 2, Column: 1}, + Message: "more than one definition not allowed", + }, + }, + }, + + { + Name: "multiple new symbols", + Data: ` +VERSION "foo" +NS_: +NS_: +BS_: +BU_: ECU1 + `, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 3, Column: 1}, + Message: "more than one definition not allowed", + }, + }, + }, + + { + Name: "multiple bit timing", + Data: ` +VERSION "foo" +NS_: +BS_: +BS_: +BU_: ECU1 + `, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 4, Column: 1}, + Message: "more than one definition not allowed", + }, + }, + }, + + { + Name: "multiple nodes", + Data: ` +VERSION "foo" +NS_: +BS_: +BU_: ECU1 +BU_: ECU2 + `, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 5, Column: 1}, + Message: "more than one definition not allowed", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/siunits/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/siunits/analyzer.go new file mode 100644 index 0000000..1be554e --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/siunits/analyzer.go @@ -0,0 +1,51 @@ +package siunits + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "unitsuffixes", + Doc: "check that signals with SI units have the correct symbols", + Run: run, + } +} + +const ( + metersPerSecond = "m/s" + kilometersPerHour = "km/h" + meters = "m" + degrees = "°" + radians = "rad" +) + +// symbolMap returns a map from non-standard unit symbols to SI unit symbols. +func symbolMap() map[string]string { + return map[string]string{ + "kph": kilometersPerHour, + "mps": metersPerSecond, + "meters/sec": metersPerSecond, + "meters": meters, + "deg": degrees, + "degrees": degrees, + "radians": radians, + } +} + +func run(pass *analysis.Pass) error { + symbols := symbolMap() + for _, def := range pass.File.Defs { + message, ok := def.(*dbc.MessageDef) + if !ok { + continue + } + for _, signal := range message.Signals { + if symbol, ok := symbols[signal.Unit]; ok { + pass.Reportf(signal.Pos, "signal with unit %s should have SI unit %s", signal.Unit, symbol) + } + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/siunits/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/siunits/analyzer_test.go new file mode 100644 index 0000000..5c6a40f --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/siunits/analyzer_test.go @@ -0,0 +1,35 @@ +package siunits + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "ok", + Data: ` +BO_ 400 TestMessage: 3 ECU1 + SG_ SpeedMps : 0|1@1+ (1,0) [0|0] "m/s" DRIVER,IO +`, + }, + + { + Name: "not ok", + Data: ` +BO_ 400 TestMessage: 3 ECU1 + SG_ SpeedMps : 0|1@1+ (1,0) [0|0] "meters/sec" DRIVER,IO +`, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 2, Column: 2}, + Message: "signal with unit meters/sec should have SI unit m/s", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/uniquenodenames/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/uniquenodenames/analyzer.go new file mode 100644 index 0000000..e88e89d --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/uniquenodenames/analyzer.go @@ -0,0 +1,29 @@ +package uniquenodenames + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "uniquenodenames", + Doc: "check that all declared node names are unique", + Run: run, + } +} + +func run(pass *analysis.Pass) error { + nodeNames := make(map[dbc.Identifier]struct{}) + for _, def := range pass.File.Defs { + if def, ok := def.(*dbc.NodesDef); ok { + for _, nodeName := range def.NodeNames { + if _, ok := nodeNames[nodeName]; ok { + pass.Reportf(def.Pos, "non-unique node name") + } + nodeNames[nodeName] = struct{}{} + } + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/uniquenodenames/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/uniquenodenames/analyzer_test.go new file mode 100644 index 0000000..51245ad --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/uniquenodenames/analyzer_test.go @@ -0,0 +1,29 @@ +package uniquenodenames + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "ok", + Data: `BU_: ECU1 ECU2 ECU3`, + }, + + { + Name: "duplicates", + Data: `BU_: ECU1 ECU2 ECU3 ECU1`, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 1, Column: 1}, + Message: "non-unique node name", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/uniquesignalnames/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/uniquesignalnames/analyzer.go new file mode 100644 index 0000000..7841ee8 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/uniquesignalnames/analyzer.go @@ -0,0 +1,33 @@ +package uniquesignalnames + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "uniquesignalnames", + Doc: "check that all signal names are unique", + Run: run, + } +} + +func run(pass *analysis.Pass) error { + for _, def := range pass.File.Defs { + message, ok := def.(*dbc.MessageDef) + if !ok || dbc.IsIndependentSignalsMessage(message) { + continue + } + signalNames := make(map[dbc.Identifier]struct{}) + for i := range message.Signals { + signal := &message.Signals[i] + if _, ok := signalNames[signal.Name]; ok { + pass.Reportf(signal.Pos, "non-unique signal name") + } else { + signalNames[signal.Name] = struct{}{} + } + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/uniquesignalnames/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/uniquesignalnames/analyzer_test.go new file mode 100644 index 0000000..25b90ec --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/uniquesignalnames/analyzer_test.go @@ -0,0 +1,37 @@ +package uniquesignalnames + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "ok", + Data: ` +BO_ 101 MOTOR_CMD: 1 DRIVER + SG_ MOTOR_CMD_steer : 0|4@1- (1,-5) [-5|5] "" MOTOR + SG_ MOTOR_CMD_drive : 4|4@1+ (1,0) [0|9] "" MOTOR + `, + }, + + { + Name: "duplicate", + Data: ` +BO_ 101 MOTOR_CMD: 1 DRIVER + SG_ MOTOR_CMD_steer : 0|4@1- (1,-5) [-5|5] "" MOTOR + SG_ MOTOR_CMD_steer : 4|4@1+ (1,0) [0|9] "" MOTOR + `, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 3, Column: 2}, + Message: "non-unique signal name", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/unitsuffixes/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/unitsuffixes/analyzer.go new file mode 100644 index 0000000..4c6fb4e --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/unitsuffixes/analyzer.go @@ -0,0 +1,44 @@ +package unitsuffixes + +import ( + "strings" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "unitsuffixes", + Doc: "check that signals with units have correct name suffixes", + Run: run, + } +} + +func unitSuffixes() map[string]string { + return map[string]string{ + "°": "Degrees", + "rad": "Radians", + "%": "Percent", + "km/h": "Kph", + "m/s": "Mps", + } +} + +func run(pass *analysis.Pass) error { + suffixes := unitSuffixes() + for _, def := range pass.File.Defs { + message, ok := def.(*dbc.MessageDef) + if !ok { + continue + } + for _, signal := range message.Signals { + if suffix, ok := suffixes[signal.Unit]; ok { + if !strings.HasSuffix(string(signal.Name), suffix) { + pass.Reportf(signal.Pos, "signal with unit %s must have suffix %s", signal.Unit, suffix) + } + } + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/unitsuffixes/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/unitsuffixes/analyzer_test.go new file mode 100644 index 0000000..208ed77 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/unitsuffixes/analyzer_test.go @@ -0,0 +1,35 @@ +package unitsuffixes + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "ok", + Data: ` +BO_ 400 TestMessage: 3 ECU1 + SG_ ValuePercent : 0|1@1+ (1,0) [0|0] "%" DRIVER,IO +`, + }, + + { + Name: "not ok", + Data: ` +BO_ 400 TestMessage: 3 ECU1 + SG_ ValuePct : 0|1@1+ (1,0) [0|0] "%" DRIVER,IO +`, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 2, Column: 2}, + Message: "signal with unit % must have suffix Percent", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/valuedescriptions/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/valuedescriptions/analyzer.go new file mode 100644 index 0000000..41eff29 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/valuedescriptions/analyzer.go @@ -0,0 +1,43 @@ +package valuedescriptions + +import ( + "fmt" + + "github.com/fiskerinc/cloud-services/pkg/can-go/internal/identifiers" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "valuedescriptions", + Doc: "check that value descriptions are valid CamelCase", + Run: run, + } +} + +func run(pass *analysis.Pass) error { + for _, def := range pass.File.Defs { + var valueDescriptions []dbc.ValueDescriptionDef + switch def := def.(type) { + case *dbc.ValueTableDef: + valueDescriptions = def.ValueDescriptions + case *dbc.ValueDescriptionsDef: + valueDescriptions = def.ValueDescriptions + default: + continue + } + for _, vd := range valueDescriptions { + vd := vd + if !identifiers.IsCamelCase(vd.Description) { + // Descriptor has format " " + // + // So we increase the column position by the size of value + 2 (space and quotes) so the lint + // error marker is on the description and not on the value + vd.Pos.Column += len(fmt.Sprintf("%d", int64(vd.Value))) + 2 + pass.Reportf(vd.Pos, "value description must be CamelCase (numbers ignored)") + } + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/valuedescriptions/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/valuedescriptions/analyzer_test.go new file mode 100644 index 0000000..f8b2af8 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/valuedescriptions/analyzer_test.go @@ -0,0 +1,42 @@ +package valuedescriptions + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "ok", + Data: `VAL_ 100 Command 2 "Reboot" 1 "Sync" 0 "Noop";`, + }, + { + Name: "ok", + Data: `VAL_ 100 Command 2 "11Reboot" 1 "123" 0 "Noop";`, + }, + { + Name: "underscore", + Data: `VAL_ 100 Command 2 "Reboot_Command" 1 "Sync" 0 "Noop";`, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 1, Column: 21}, + Message: "value description must be CamelCase (numbers ignored)", + }, + }, + }, + { + Name: "several digits value", + Data: `VAL_ 100 Command 234 "Reboot_Command" 1 "Sync" 0 "Noop";`, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 1, Column: 23}, + Message: "value description must be CamelCase (numbers ignored)", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/version/analyzer.go b/pkg/can-go/pkg/dbc/analysis/passes/version/analyzer.go new file mode 100644 index 0000000..7351ba7 --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/version/analyzer.go @@ -0,0 +1,27 @@ +package version + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" +) + +func Analyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "version", + Doc: "check that the version definition is empty", + Run: run, + } +} + +func run(pass *analysis.Pass) error { + for _, def := range pass.File.Defs { + versionDef, ok := def.(*dbc.VersionDef) + if !ok { + continue // not a version definition + } + if len(versionDef.Version) > 0 { + pass.Reportf(versionDef.Pos, "version should be empty") + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/analysis/passes/version/analyzer_test.go b/pkg/can-go/pkg/dbc/analysis/passes/version/analyzer_test.go new file mode 100644 index 0000000..271e60c --- /dev/null +++ b/pkg/can-go/pkg/dbc/analysis/passes/version/analyzer_test.go @@ -0,0 +1,29 @@ +package version + +import ( + "testing" + "text/scanner" + + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/analysistest" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, Analyzer(), []*analysistest.Case{ + { + Name: "ok", + Data: `VERSION ""`, + }, + + { + Name: "not ok", + Data: `VERSION "foo"`, + Diagnostics: []*analysis.Diagnostic{ + { + Pos: scanner.Position{Line: 1, Column: 1}, + Message: "version should be empty", + }, + }, + }, + }) +} diff --git a/pkg/can-go/pkg/dbc/attributevaluetype.go b/pkg/can-go/pkg/dbc/attributevaluetype.go new file mode 100644 index 0000000..b55f613 --- /dev/null +++ b/pkg/can-go/pkg/dbc/attributevaluetype.go @@ -0,0 +1,28 @@ +package dbc + +import "fmt" + +// AttributeValueType represents an attribute value type. +type AttributeValueType string + +const ( + AttributeValueTypeInt AttributeValueType = "INT" + AttributeValueTypeHex AttributeValueType = "HEX" + AttributeValueTypeFloat AttributeValueType = "FLOAT" + AttributeValueTypeString AttributeValueType = "STRING" + AttributeValueTypeEnum AttributeValueType = "ENUM" +) + +// Validate returns an error for invalid attribute value types. +func (a AttributeValueType) Validate() error { + switch a { + case AttributeValueTypeInt: + case AttributeValueTypeHex: + case AttributeValueTypeFloat: + case AttributeValueTypeString: + case AttributeValueTypeEnum: + default: + return fmt.Errorf("invalid attribute value type: %v", a) + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/attributevaluetype_test.go b/pkg/can-go/pkg/dbc/attributevaluetype_test.go new file mode 100644 index 0000000..f5c68fc --- /dev/null +++ b/pkg/can-go/pkg/dbc/attributevaluetype_test.go @@ -0,0 +1,23 @@ +package dbc + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestAttributeValueType_Validate(t *testing.T) { + for _, tt := range []AttributeValueType{ + AttributeValueTypeInt, + AttributeValueTypeHex, + AttributeValueTypeFloat, + AttributeValueTypeString, + AttributeValueTypeEnum, + } { + assert.NilError(t, tt.Validate()) + } +} + +func TestAttributeValueType_Validate_Error(t *testing.T) { + assert.ErrorContains(t, AttributeValueType("foo").Validate(), "invalid attribute value type") +} diff --git a/pkg/can-go/pkg/dbc/def.go b/pkg/can-go/pkg/dbc/def.go new file mode 100644 index 0000000..a7c7d50 --- /dev/null +++ b/pkg/can-go/pkg/dbc/def.go @@ -0,0 +1,720 @@ +package dbc + +import ( + "strconv" + "text/scanner" +) + +// Def represents a single definition within a DBC file. +type Def interface { + // Position of the definition. + Position() scanner.Position + + // parseFrom parses the definition from a parser. + parseFrom(*Parser) +} + +// VersionDef defines the version of a DBC file. +type VersionDef struct { + Pos scanner.Position + Version string +} + +var _ Def = &VersionDef{} + +func (d *VersionDef) parseFrom(p *Parser) { + d.Pos = p.keyword(KeywordVersion).pos + d.Version = p.string() +} + +// Position returns the position of the definition. +func (d *VersionDef) Position() scanner.Position { + return d.Pos +} + +// NewSymbolsDef defines new symbol entries in a DBC file. +type NewSymbolsDef struct { + Pos scanner.Position + Symbols []Keyword +} + +var _ Def = &NewSymbolsDef{} + +func (d *NewSymbolsDef) parseFrom(p *Parser) { + p.useWhitespace(significantTab) + defer p.useWhitespace(defaultWhitespace) + d.Pos = p.keyword(KeywordNewSymbols).pos + p.token(':') + for p.peekToken().typ == '\t' { + p.token('\t') + d.Symbols = append(d.Symbols, Keyword(p.identifier())) + } +} + +// Position returns the position of the definition. +func (d *NewSymbolsDef) Position() scanner.Position { + return d.Pos +} + +// BitTimingDef defines the baud rate and the settings of the BTR registers of a CAN network. +// +// This definition is obsolete and not used anymore. +type BitTimingDef struct { + Pos scanner.Position + BaudRate uint64 + BTR1 uint64 + BTR2 uint64 +} + +var _ Def = &BitTimingDef{} + +func (d *BitTimingDef) parseFrom(p *Parser) { + d.Pos = p.keyword(KeywordBitTiming).pos + p.token(':') + d.BaudRate = p.optionalUint() + if p.peekToken().typ == ':' { + d.BTR1 = p.optionalUint() + } + if p.peekToken().typ == ',' { + d.BTR2 = p.optionalUint() + } +} + +// Position returns the position of the definition. +func (d *BitTimingDef) Position() scanner.Position { + return d.Pos +} + +// NodesDef defines the names of all nodes participating in the network. +// +// This definition is required in every DBC file. +// +// All node names must be unique. +type NodesDef struct { + Pos scanner.Position + NodeNames []Identifier +} + +var _ Def = &NodesDef{} + +func (d *NodesDef) parseFrom(p *Parser) { + p.useWhitespace(significantNewline) + defer p.useWhitespace(defaultWhitespace) + d.Pos = p.keyword(KeywordNodes).pos + p.token(':') + for p.peekToken().typ == scanner.Ident { + d.NodeNames = append(d.NodeNames, p.identifier()) + } + if p.peekToken().typ != scanner.EOF { + p.token('\n') + } +} + +// Position returns the position of the definition. +func (d *NodesDef) Position() scanner.Position { + return d.Pos +} + +// ValueDescriptionDef defines a textual description for a single signal value. +// +// The value may either be a signal raw value transferred on the bus or the value of an environment variable in a +// remaining bus simulation. +type ValueDescriptionDef struct { + Pos scanner.Position + Value float64 + Description string +} + +var _ Def = &ValueDescriptionDef{} + +func (d *ValueDescriptionDef) parseFrom(p *Parser) { + d.Pos = p.peekToken().pos + d.Value = p.float() + d.Description = p.string() +} + +// Position returns the position of the definition. +func (d *ValueDescriptionDef) Position() scanner.Position { + return d.Pos +} + +// ValueTableDef defines a global value table. +// +// The value descriptions in value tables define value encodings for signal raw values. +// +// In commonly used DBC files, the global value tables aren't used, but the value descriptions are defined for each +// signal independently. +type ValueTableDef struct { + Pos scanner.Position + TableName Identifier + ValueDescriptions []ValueDescriptionDef +} + +var _ Def = &ValueTableDef{} + +func (d *ValueTableDef) parseFrom(p *Parser) { + d.Pos = p.keyword(KeywordValueTable).pos + d.TableName = p.identifier() + for p.peekToken().typ != ';' { + valueDescriptionDef := ValueDescriptionDef{} + valueDescriptionDef.parseFrom(p) + d.ValueDescriptions = append(d.ValueDescriptions, valueDescriptionDef) + } + p.token(';') +} + +// Position returns the position of the definition. +func (d *ValueTableDef) Position() scanner.Position { + return d.Pos +} + +// MessageDef defines a frame in the network. +// +// The definition includes the name of a frame as well as its properties and the signals transferred. +type MessageDef struct { + // Pos is the position of the message definition. + Pos scanner.Position + + // MessageID contains the message CAN ID. + // + // The CAN ID has to be unique within the DBC file. + // + // If the most significant bit of the message ID is set, the ID is an extended CAN ID. The extended CAN ID can be + // determined by masking out the most significant bit with the mask 0xCFFFFFFF. + MessageID MessageID + + // Name is the name of the message. + // + // The message name has to be unique within the DBC file. + Name Identifier + + // Size specifies the size of the message in bytes. + Size uint64 + + // Transmitter specifies the name of the node transmitting the message. + // + // The transmitter has to be defined in the set of node names in the nodes definition. + // + // If the message has no transmitter, the string 'Vector__XXX' has to be given here. + Transmitter Identifier + + // Signals specifies the signals of the message. + Signals []SignalDef +} + +var _ Def = &MessageDef{} + +func (d *MessageDef) parseFrom(p *Parser) { + d.Pos = p.keyword(KeywordMessage).pos + d.MessageID = p.messageID() + d.Name = p.identifier() + p.token(':') + d.Size = p.uint() + d.Transmitter = p.identifier() + for p.peekToken().typ != scanner.EOF && p.peekKeyword() == KeywordSignal { + signalDef := SignalDef{} + signalDef.parseFrom(p) + d.Signals = append(d.Signals, signalDef) + } +} + +// Position returns the position of the definition. +func (d *MessageDef) Position() scanner.Position { + return d.Pos +} + +// SignalDef defines a signal within a message. +type SignalDef struct { + // Pos is the position of the definition. + Pos scanner.Position + + // Name of the signal. + // + // Has to be unique for all signals within the same message. + Name Identifier + + // StartBit specifies the position of the signal within the data field of the frame. + // + // For signals with byte order Intel (little-endian) the position of the least-significant bit is given. + // + // For signals with byte order Motorola (big-endian) the position of the most significant bit is given. + // + // The bits are counted in a saw-tooth manner. + // + // The start bit has to be in the range of [0 ,8*message_size-1]. + StartBit uint64 + + // Size specifies the size of the signal in bits. + Size uint64 + + // IsBigEndian is true if the signal's byte order is Motorola (big-endian). + IsBigEndian bool + + // IsSigned is true if the signal is signed. + IsSigned bool + + // IsMultiplexerSwitch is true if the signal is a multiplexer switch. + // + // A multiplexer indicator of 'M' defines the signal as the multiplexer switch. + // Only one signal within a single message can be the multiplexer switch. + IsMultiplexerSwitch bool + + // IsMultiplexed is true if the signal is multiplexed by the message's multiplexer switch. + IsMultiplexed bool + + // MultiplexerSwitch is the multiplexer switch value of the signal. + // + // The multiplexed signal is transferred in the message if the switch value of the multiplexer signal is equal to + // its multiplexer switch value. + MultiplexerSwitch uint64 + + // Offset is the signals physical value offset. + // + // Together with the factor, the offset defines the linear conversion rule to convert the signal's raw value into + // the signal's physical value and vice versa. + // + // physical_value = raw_value * factor + offset + // raw_value = (physical_value - offset) / factor + Offset float64 + + // Factor is the signal's physical value factor. + // + // See: Offset. + Factor float64 + + // Minimum defines the signal's minimum physical value. + Minimum float64 + + // Maximum defines the signal's maximum physical value. + Maximum float64 + + // Unit defines the unit of the signal's physical value. + Unit string + + // Receivers specifies the nodes receiving the signal. + // + // If the signal has no receiver, the string 'Vector__XXX' has to be given here. + Receivers []Identifier +} + +var _ Def = &SignalDef{} + +func (d *SignalDef) parseFrom(p *Parser) { + d.Pos = p.keyword(KeywordSignal).pos + d.Name = p.identifier() + // Parse: Multiplexing + if p.peekToken().typ != ':' { + tok := p.nextToken() + if tok.typ != scanner.Ident { + p.failf(tok.pos, "expected ident") + } + switch { + case tok.txt == "M": + d.IsMultiplexerSwitch = true + case tok.txt[0] == 'm' && len(tok.txt) > 1: + d.IsMultiplexed = true + i, err := strconv.Atoi(tok.txt[1:]) + if err != nil || i < 0 { + p.failf(tok.pos, "invalid multiplexer value") + } + d.MultiplexerSwitch = uint64(i) + default: + p.failf(tok.pos, "expected multiplexer") + } + } + p.token(':') + d.StartBit = p.uint() + p.token('|') + d.Size = p.uint() + p.token('@') + d.IsBigEndian = p.intInRange(0, 1) == 0 + d.IsSigned = p.anyOf('-', '+') == '-' + p.token('(') + d.Factor = p.float() + p.token(',') + d.Offset = p.float() + p.token(')') + p.token('[') + d.Minimum = p.float() + p.token('|') + d.Maximum = p.float() + p.token(']') + d.Unit = p.string() + // Parse: Receivers + d.Receivers = append(d.Receivers, p.identifier()) + for p.peekToken().typ == ',' { + p.token(',') + d.Receivers = append(d.Receivers, p.identifier()) + } +} + +// Position returns the position of the definition. +func (d *SignalDef) Position() scanner.Position { + return d.Pos +} + +// SignalValueTypeDef defines an extended type definition for a signal. +type SignalValueTypeDef struct { + Pos scanner.Position + MessageID MessageID + SignalName Identifier + SignalValueType SignalValueType +} + +var _ Def = &SignalValueTypeDef{} + +func (d *SignalValueTypeDef) parseFrom(p *Parser) { + d.Pos = p.keyword(KeywordSignalValueType).pos + d.MessageID = p.messageID() + d.SignalName = p.identifier() + p.optionalToken(':') // SPECIAL-CASE: colon not part of spec, but encountered in the wild + d.SignalValueType = p.signalValueType() + p.token(';') +} + +// Position returns the position of the definition. +func (d *SignalValueTypeDef) Position() scanner.Position { + return d.Pos +} + +// MessageTransmittersDef defines multiple transmitter nodes of a single message. +// +// This definition is used to describe communication data for higher layer protocols. +// +// This is not used to define CAN layer-2 communication. +type MessageTransmittersDef struct { + Pos scanner.Position + MessageID MessageID + Transmitters []Identifier +} + +var _ Def = &MessageTransmittersDef{} + +func (d *MessageTransmittersDef) parseFrom(p *Parser) { + d.Pos = p.keyword(KeywordMessageTransmitters).pos + d.MessageID = p.messageID() + p.token(':') + for p.peekToken().typ != ';' { + d.Transmitters = append(d.Transmitters, p.identifier()) + // SPECIAL-CASE: Comma not included in spec, but encountered in the wild + p.optionalToken(',') + } + p.token(';') +} + +// Position returns the position of the definition. +func (d *MessageTransmittersDef) Position() scanner.Position { + return d.Pos +} + +// ValueDescriptionsDef defines inline descriptions for specific raw signal values. +type ValueDescriptionsDef struct { + Pos scanner.Position + ObjectType ObjectType + MessageID MessageID + SignalName Identifier + EnvironmentVariableName Identifier + ValueDescriptions []ValueDescriptionDef +} + +var _ Def = &ValueDescriptionsDef{} + +func (d *ValueDescriptionsDef) parseFrom(p *Parser) { + d.Pos = p.keyword(KeywordValueDescriptions).pos + if p.peekToken().typ == scanner.Ident { + d.ObjectType = ObjectTypeEnvironmentVariable + d.EnvironmentVariableName = p.identifier() + } else { + d.ObjectType = ObjectTypeSignal + d.MessageID = p.messageID() + d.SignalName = p.identifier() + } + for p.peekToken().typ != ';' { + valueDescriptionDef := ValueDescriptionDef{} + valueDescriptionDef.parseFrom(p) + d.ValueDescriptions = append(d.ValueDescriptions, valueDescriptionDef) + } + p.token(';') +} + +// Position returns the position of the definition. +func (d *ValueDescriptionsDef) Position() scanner.Position { + return d.Pos +} + +// EnvironmentVariableDef defines an environment variable. +// +// DBC files that describe the CAN communication and don't define any additional data for system or remaining bus +// simulations don't include environment variables. +type EnvironmentVariableDef struct { + Pos scanner.Position + Name Identifier + Type EnvironmentVariableType + Minimum float64 + Maximum float64 + Unit string + InitialValue float64 + ID uint64 + AccessType AccessType + AccessNodes []Identifier +} + +var _ Def = &EnvironmentVariableDef{} + +func (d *EnvironmentVariableDef) parseFrom(p *Parser) { + d.Pos = p.keyword(KeywordEnvironmentVariable).pos + d.Name = p.identifier() + p.token(':') + d.Type = p.environmentVariableType() + p.token('[') + d.Minimum = p.float() + p.token('|') + d.Maximum = p.float() + p.token(']') + d.Unit = p.string() + d.InitialValue = p.float() + d.ID = p.uint() + d.AccessType = p.accessType() + d.AccessNodes = append(d.AccessNodes, p.identifier()) + for p.peekToken().typ == ',' { + p.token(',') + d.AccessNodes = append(d.AccessNodes, p.identifier()) + } + p.token(';') +} + +// Position returns the position of the definition. +func (d *EnvironmentVariableDef) Position() scanner.Position { + return d.Pos +} + +// EnvironmentVariableDataDef defines an environment variable as being of type "data". +// +// Environment variables of this type can store an arbitrary binary data of the given length. +// The length is given in bytes. +type EnvironmentVariableDataDef struct { + Pos scanner.Position + // EnvironmentVariableName is the name of the environment variable. + EnvironmentVariableName Identifier + // DataSize is the size of the environment variable data in bytes. + DataSize uint64 +} + +var _ Def = &EnvironmentVariableDataDef{} + +func (d *EnvironmentVariableDataDef) parseFrom(p *Parser) { + d.Pos = p.keyword(KeywordEnvironmentVariableData).pos + d.EnvironmentVariableName = p.identifier() + p.token(':') + d.DataSize = p.uint() + p.token(';') +} + +// Position returns the position of the definition. +func (d *EnvironmentVariableDataDef) Position() scanner.Position { + return d.Pos +} + +// CommentDef defines a comment. +type CommentDef struct { + Pos scanner.Position + ObjectType ObjectType + NodeName Identifier + MessageID MessageID + SignalName Identifier + EnvironmentVariableName Identifier + Comment string +} + +var _ Def = &CommentDef{} + +func (d *CommentDef) parseFrom(p *Parser) { + d.Pos = p.keyword(KeywordComment).pos + d.ObjectType = p.optionalObjectType() + switch d.ObjectType { + case ObjectTypeNetworkNode: + d.NodeName = p.identifier() + case ObjectTypeMessage: + d.MessageID = p.messageID() + case ObjectTypeSignal: + d.MessageID = p.messageID() + d.SignalName = p.identifier() + case ObjectTypeEnvironmentVariable: + d.EnvironmentVariableName = p.identifier() + } + d.Comment = p.string() + p.token(';') +} + +// Position returns the position of the definition. +func (d *CommentDef) Position() scanner.Position { + return d.Pos +} + +// AttributeDef defines a user-defined attribute. +// +// User-defined attributes are a means to extend the object properties of the DBC file. +// +// These additional attributes have to be defined using an attribute definition with an attribute default value. +// +// For each object having a value defined for the attribute, an attribute value entry has to be defined. +// +// If no attribute value entry is defined for an object, the value of the object's attribute is the attribute's default. +type AttributeDef struct { + Pos scanner.Position + ObjectType ObjectType + Name Identifier + Type AttributeValueType + MinimumInt int64 + MaximumInt int64 + MinimumFloat float64 + MaximumFloat float64 + EnumValues []string +} + +var _ Def = &AttributeDef{} + +func (d *AttributeDef) parseFrom(p *Parser) { + d.Pos = p.keyword(KeywordAttribute).pos + d.ObjectType = p.optionalObjectType() + d.Name = p.stringIdentifier() + d.Type = p.attributeValueType() + switch d.Type { + case AttributeValueTypeInt, AttributeValueTypeHex: + if p.peekToken().typ != ';' { + d.MinimumInt = p.int() + d.MaximumInt = p.int() + } + case AttributeValueTypeFloat: + if p.peekToken().typ != ';' { + // SPECIAL CASE: Support attributes without min/max + d.MinimumFloat = p.float() + d.MaximumFloat = p.float() + } + case AttributeValueTypeEnum: + d.EnumValues = append(d.EnumValues, p.string()) + for p.peekToken().typ == ',' { + p.token(',') + d.EnumValues = append(d.EnumValues, p.string()) + } + } + p.token(';') +} + +// Position returns the position of the definition. +func (d *AttributeDef) Position() scanner.Position { + return d.Pos +} + +// AttributeDefaultValueDef defines the default value for an attribute. +type AttributeDefaultValueDef struct { + Pos scanner.Position + AttributeName Identifier + DefaultIntValue int64 + DefaultFloatValue float64 + DefaultStringValue string +} + +var _ Def = &AttributeDefaultValueDef{} + +func (d *AttributeDefaultValueDef) parseFrom(p *Parser) { + d.Pos = p.keyword(KeywordAttributeDefault).pos + d.AttributeName = Identifier(p.string()) + // look up attribute type + for _, prevDef := range p.defs { + if attributeDef, ok := prevDef.(*AttributeDef); ok && attributeDef.Name == d.AttributeName { + switch attributeDef.Type { + case AttributeValueTypeInt, AttributeValueTypeHex: + d.DefaultIntValue = p.int() + case AttributeValueTypeFloat: + d.DefaultFloatValue = p.float() + case AttributeValueTypeString: + d.DefaultStringValue = p.string() + case AttributeValueTypeEnum: + d.DefaultStringValue = p.enumValue(attributeDef.EnumValues) + } + break + } + } + p.token(';') +} + +// Position returns the position of the definition. +func (d *AttributeDefaultValueDef) Position() scanner.Position { + return d.Pos +} + +// AttributeValueForObjectDef defines a value for an attribute and an object. +type AttributeValueForObjectDef struct { + Pos scanner.Position + AttributeName Identifier + ObjectType ObjectType + MessageID MessageID + SignalName Identifier + NodeName Identifier + EnvironmentVariableName Identifier + IntValue int64 + FloatValue float64 + StringValue string +} + +var _ Def = &AttributeValueForObjectDef{} + +func (d *AttributeValueForObjectDef) parseFrom(p *Parser) { + d.Pos = p.keyword(KeywordAttributeValue).pos + d.AttributeName = Identifier(p.string()) + d.ObjectType = p.optionalObjectType() + switch d.ObjectType { + case ObjectTypeMessage: + d.MessageID = p.messageID() + case ObjectTypeSignal: + d.MessageID = p.messageID() + d.SignalName = p.identifier() + case ObjectTypeNetworkNode: + d.NodeName = p.identifier() + case ObjectTypeEnvironmentVariable: + d.EnvironmentVariableName = p.identifier() + } + // look up attribute type + for _, prevDef := range p.defs { + if attributeDef, ok := prevDef.(*AttributeDef); ok && attributeDef.Name == d.AttributeName { + switch attributeDef.Type { + case AttributeValueTypeInt, AttributeValueTypeHex: + d.IntValue = p.int() + case AttributeValueTypeFloat: + d.FloatValue = p.float() + case AttributeValueTypeString: + d.StringValue = p.string() + case AttributeValueTypeEnum: + d.StringValue = p.enumValue(attributeDef.EnumValues) + } + break + } + } + p.token(';') +} + +// Position returns the position of the definition. +func (d *AttributeValueForObjectDef) Position() scanner.Position { + return d.Pos +} + +// UnknownDef represents an unknown or unsupported DBC definition. +type UnknownDef struct { + Pos scanner.Position + Keyword Keyword +} + +var _ Def = &UnknownDef{} + +func (d *UnknownDef) parseFrom(p *Parser) { + tok := p.peekToken() + d.Pos = tok.pos + d.Keyword = Keyword(tok.txt) + p.discardLine() +} + +// Position returns the position of the definition. +func (d *UnknownDef) Position() scanner.Position { + return d.Pos +} diff --git a/pkg/can-go/pkg/dbc/doc.go b/pkg/can-go/pkg/dbc/doc.go new file mode 100644 index 0000000..ceafdc9 --- /dev/null +++ b/pkg/can-go/pkg/dbc/doc.go @@ -0,0 +1,4 @@ +// Package dbc provides primitives for parsing, formatting and linting DBC files. +// +// The implementation adheres to the "DBC File Format Documentation Version 01/2007" unless specified otherwise. +package dbc diff --git a/pkg/can-go/pkg/dbc/envvartype.go b/pkg/can-go/pkg/dbc/envvartype.go new file mode 100644 index 0000000..e86f8f3 --- /dev/null +++ b/pkg/can-go/pkg/dbc/envvartype.go @@ -0,0 +1,24 @@ +package dbc + +import "fmt" + +// EnvironmentVariableType represents the type of an environment variable. +type EnvironmentVariableType uint64 + +const ( + EnvironmentVariableTypeInteger EnvironmentVariableType = 0 + EnvironmentVariableTypeFloat EnvironmentVariableType = 1 + EnvironmentVariableTypeString EnvironmentVariableType = 2 +) + +// Validate returns an error for invalid environment variable types. +func (e EnvironmentVariableType) Validate() error { + switch e { + case EnvironmentVariableTypeInteger: + case EnvironmentVariableTypeFloat: + case EnvironmentVariableTypeString: + default: + return fmt.Errorf("invalid environment variable type: %v", e) + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/envvartype_test.go b/pkg/can-go/pkg/dbc/envvartype_test.go new file mode 100644 index 0000000..44ba0d3 --- /dev/null +++ b/pkg/can-go/pkg/dbc/envvartype_test.go @@ -0,0 +1,21 @@ +package dbc + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestEnvironmentVariableType_Validate(t *testing.T) { + for _, tt := range []EnvironmentVariableType{ + EnvironmentVariableTypeInteger, + EnvironmentVariableTypeFloat, + EnvironmentVariableTypeString, + } { + assert.NilError(t, tt.Validate()) + } +} + +func TestEnvironmentVariableType_Validate_Error(t *testing.T) { + assert.Error(t, EnvironmentVariableType(42).Validate(), "invalid environment variable type: 42") +} diff --git a/pkg/can-go/pkg/dbc/error.go b/pkg/can-go/pkg/dbc/error.go new file mode 100644 index 0000000..167c019 --- /dev/null +++ b/pkg/can-go/pkg/dbc/error.go @@ -0,0 +1,74 @@ +package dbc + +import ( + "errors" + "fmt" + "text/scanner" +) + +// Error represents an error in a DBC file. +type Error interface { + error + + // Position of the error in the DBC file. + Position() scanner.Position + + // Reason for the error. + Reason() string +} + +// validationError is an error resulting from an invalid DBC definition. +type validationError struct { + pos scanner.Position + reason string + cause error +} + +func (e *validationError) Unwrap() error { + return e.cause +} + +var _ Error = &validationError{} + +func (e *validationError) Error() string { + return fmt.Sprintf("%v: %s (validate)", e.Position(), e.reason) +} + +// Reason returns the reason for the error. +func (e *validationError) Reason() string { + return e.reason +} + +// Position returns the position of the validation error in the DBC file. +// +// When the validation error results from an invalid nested definition, the position of the nested definition is +// returned. +func (e *validationError) Position() scanner.Position { + var errValidation *validationError + if errors.As(e.cause, &errValidation) { + return errValidation.Position() + } + return e.pos +} + +// parseError is an error resulting from a failure to parse a DBC file. +type parseError struct { + pos scanner.Position + reason string +} + +var _ Error = &parseError{} + +func (e *parseError) Error() string { + return fmt.Sprintf("%v: %s (parse)", e.pos, e.reason) +} + +// Reason returns the reason for the error. +func (e *parseError) Reason() string { + return e.reason +} + +// Position returns the position of the parse error in the DBC file. +func (e *parseError) Position() scanner.Position { + return e.pos +} diff --git a/pkg/can-go/pkg/dbc/file.go b/pkg/can-go/pkg/dbc/file.go new file mode 100644 index 0000000..f80b2bc --- /dev/null +++ b/pkg/can-go/pkg/dbc/file.go @@ -0,0 +1,11 @@ +package dbc + +// File is a parsed DBC source file. +type File struct { + // Name of the file. + Name string + // Data contains the raw file data. + Data []byte + // Defs in the file. + Defs []Def +} diff --git a/pkg/can-go/pkg/dbc/identifier.go b/pkg/can-go/pkg/dbc/identifier.go new file mode 100644 index 0000000..7a4b722 --- /dev/null +++ b/pkg/can-go/pkg/dbc/identifier.go @@ -0,0 +1,36 @@ +package dbc + +import ( + "fmt" + + "github.com/fiskerinc/cloud-services/pkg/can-go/internal/identifiers" +) + +// Identifier represents a DBC identifier. +type Identifier string + +// maxIdentifierLength is the length of the longest valid DBC identifier. +const maxIdentifierLength = 128 + +// Validate returns an error for invalid DBC identifiers. +func (id Identifier) Validate() (err error) { + defer func() { + if err != nil { + err = fmt.Errorf("invalid identifier '%s': %w", id, err) + } + }() + if len(id) == 0 { + return fmt.Errorf("zero-length") + } + if len(id) > maxIdentifierLength { + return fmt.Errorf("length %v exceeds max length %v", len(id), maxIdentifierLength) + } + for i, r := range id { + if i == 0 && r != '_' && !identifiers.IsAlphaChar(r) { // first char + return fmt.Errorf("invalid first char: '%v'", r) + } else if i > 0 && r != '_' && !identifiers.IsAlphaChar(r) && !identifiers.IsNumChar(r) { + return fmt.Errorf("invalid char: '%v'", r) + } + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/identifier_test.go b/pkg/can-go/pkg/dbc/identifier_test.go new file mode 100644 index 0000000..8d9890c --- /dev/null +++ b/pkg/can-go/pkg/dbc/identifier_test.go @@ -0,0 +1,42 @@ +package dbc + +import ( + "fmt" + "strings" + "testing" + + "gotest.tools/v3/assert" +) + +func TestIdentifier_Validate(t *testing.T) { + for _, tt := range []Identifier{ + "_", + "_foo", + "foo", + "foo32", + "_43", + Identifier(strings.Repeat("a", maxIdentifierLength)), + } { + tt := tt + t.Run(fmt.Sprintf("%v", tt), func(t *testing.T) { + assert.NilError(t, tt.Validate()) + }) + } +} + +func TestIdentifier_Validate_Error(t *testing.T) { + for _, tt := range []Identifier{ + "42", + "", + "42foo", + "☃", + "foo☃", + "foo bar", + Identifier(strings.Repeat("a", maxIdentifierLength+1)), + } { + tt := tt + t.Run(fmt.Sprintf("%v", tt), func(t *testing.T) { + assert.ErrorContains(t, tt.Validate(), "invalid identifier") + }) + } +} diff --git a/pkg/can-go/pkg/dbc/independent_signals.go b/pkg/can-go/pkg/dbc/independent_signals.go new file mode 100644 index 0000000..fdfab6b --- /dev/null +++ b/pkg/can-go/pkg/dbc/independent_signals.go @@ -0,0 +1,22 @@ +package dbc + +// Independent signals constants. +// +// DBC files may contain a special message with the following message name and message ID. +// +// This message will have size 0 and may contain duplicate signal names. +const ( + // IndependentSignalsMessageName is the message name used by the special independent signals message. + IndependentSignalsMessageName Identifier = "VECTOR__INDEPENDENT_SIG_MSG" + // IndependentSignalsMessageName is the message ID used by the special independent signals message. + IndependentSignalsMessageID MessageID = 0xc0000000 + // IndependentSignalsMessageSize is the size used by the special independent signals message. + IndependentSignalsMessageSize = 0 +) + +// IsIndependentSignalsMessage returns true if m is the special independent signals message. +func IsIndependentSignalsMessage(m *MessageDef) bool { + return m.Name == IndependentSignalsMessageName && + m.MessageID == IndependentSignalsMessageID && + m.Size == IndependentSignalsMessageSize +} diff --git a/pkg/can-go/pkg/dbc/keyword.go b/pkg/can-go/pkg/dbc/keyword.go new file mode 100644 index 0000000..c0193e0 --- /dev/null +++ b/pkg/can-go/pkg/dbc/keyword.go @@ -0,0 +1,25 @@ +package dbc + +// Keyword represents a DBC keyword. +type Keyword string + +const ( + KeywordAttribute Keyword = "BA_DEF_" + KeywordAttributeDefault Keyword = "BA_DEF_DEF_" + KeywordAttributeValue Keyword = "BA_" + KeywordBitTiming Keyword = "BS_" + KeywordComment Keyword = "CM_" + KeywordEnvironmentVariable Keyword = "EV_" + KeywordEnvironmentVariableData Keyword = "ENVVAR_DATA_" + KeywordMessage Keyword = "BO_" + KeywordMessageTransmitters Keyword = "BO_TX_BU_" + KeywordNewSymbols Keyword = "NS_" + KeywordNodes Keyword = "BU_" + KeywordSignal Keyword = "SG_" + KeywordSignalGroup Keyword = "SIG_GROUP_" + KeywordSignalType Keyword = "SGTYPE_" + KeywordSignalValueType Keyword = "SIG_VALTYPE_" + KeywordValueDescriptions Keyword = "VAL_" + KeywordValueTable Keyword = "VAL_TABLE_" + KeywordVersion Keyword = "VERSION" +) diff --git a/pkg/can-go/pkg/dbc/messageid.go b/pkg/can-go/pkg/dbc/messageid.go new file mode 100644 index 0000000..728978b --- /dev/null +++ b/pkg/can-go/pkg/dbc/messageid.go @@ -0,0 +1,44 @@ +package dbc + +import "fmt" + +// MessageID represents a message ID. +type MessageID uint32 + +// ID constants. +const ( + // maxID is the largest valid standard CAN ID. + maxID = 0x7ff + // maxExtendedID is the largest valid extended CAN ID. + maxExtendedID = 0x1fffffff +) + +// messageIDExtendedFlag is a bit flag that is set for extended message IDs. +const messageIDExtendedFlag MessageID = 0x80000000 + +// messageIDIndependentSignals is a special message ID used for the "independent signals" message. +const messageIDIndependentSignals MessageID = 0xc0000000 + +// IsExtended returns true if the message ID is an extended CAN ID. +func (m MessageID) IsExtended() bool { + return m != messageIDIndependentSignals && m&messageIDExtendedFlag > 0 +} + +// ToCAN returns the CAN id value of the message ID (i.e. with bit flags removed). +func (m MessageID) ToCAN() uint32 { + return uint32(m &^ messageIDExtendedFlag) +} + +// Validate returns an error for invalid message IDs. +func (m MessageID) Validate() error { + if m == messageIDIndependentSignals { + return nil + } + if m.IsExtended() && m.ToCAN() > maxExtendedID { + return fmt.Errorf("invalid extended ID: %v", m) + } + if !m.IsExtended() && m.ToCAN() > maxID { + return fmt.Errorf("invalid standard ID: %v", m) + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/messageid_test.go b/pkg/can-go/pkg/dbc/messageid_test.go new file mode 100644 index 0000000..31d05f2 --- /dev/null +++ b/pkg/can-go/pkg/dbc/messageid_test.go @@ -0,0 +1,71 @@ +package dbc + +import ( + "fmt" + "testing" + + "gotest.tools/v3/assert" +) + +func TestMessageID_Validate(t *testing.T) { + for _, tt := range []MessageID{ + 0, + 1, + maxID, + 0 | messageIDExtendedFlag, + 1 | messageIDExtendedFlag, + maxID | messageIDExtendedFlag, + maxExtendedID | messageIDExtendedFlag, + messageIDIndependentSignals, + } { + tt := tt + t.Run(fmt.Sprintf("%v", tt), func(t *testing.T) { + assert.NilError(t, tt.Validate()) + }) + } +} + +func TestMessageID_Validate_Error(t *testing.T) { + for _, tt := range []MessageID{ + maxID + 1, + (maxExtendedID + 1) | messageIDExtendedFlag, + 0xffffffff, + } { + tt := tt + t.Run(fmt.Sprintf("%v", tt), func(t *testing.T) { + assert.ErrorContains(t, tt.Validate(), "invalid") + }) + } +} + +func TestMessageID_ToCAN(t *testing.T) { + for _, tt := range []struct { + messageID MessageID + expected uint32 + }{ + {messageID: 1, expected: 1}, + {messageID: messageIDIndependentSignals, expected: 0x40000000}, + {messageID: 2566857156, expected: 419373508}, + } { + tt := tt + t.Run(fmt.Sprintf("%v", tt.messageID), func(t *testing.T) { + assert.Equal(t, tt.expected, tt.messageID.ToCAN()) + }) + } +} + +func TestMessageID_IsExtended(t *testing.T) { + for _, tt := range []struct { + messageID MessageID + expected bool + }{ + {messageID: 1, expected: false}, + {messageID: messageIDIndependentSignals, expected: false}, + {messageID: 2566857156, expected: true}, + } { + tt := tt + t.Run(fmt.Sprintf("%v", tt.messageID), func(t *testing.T) { + assert.Equal(t, tt.expected, tt.messageID.IsExtended()) + }) + } +} diff --git a/pkg/can-go/pkg/dbc/objecttype.go b/pkg/can-go/pkg/dbc/objecttype.go new file mode 100644 index 0000000..307d9da --- /dev/null +++ b/pkg/can-go/pkg/dbc/objecttype.go @@ -0,0 +1,28 @@ +package dbc + +import "fmt" + +// ObjectType identifies the type of a DBC object. +type ObjectType string + +const ( + ObjectTypeUnspecified ObjectType = "" + ObjectTypeNetworkNode ObjectType = "BU_" + ObjectTypeMessage ObjectType = "BO_" + ObjectTypeSignal ObjectType = "SG_" + ObjectTypeEnvironmentVariable ObjectType = "EV_" +) + +// Validate returns an error for invalid object types. +func (o ObjectType) Validate() error { + switch o { + case ObjectTypeUnspecified: + case ObjectTypeNetworkNode: + case ObjectTypeMessage: + case ObjectTypeSignal: + case ObjectTypeEnvironmentVariable: + default: + return fmt.Errorf("invalid object type: %v", o) + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/objecttype_test.go b/pkg/can-go/pkg/dbc/objecttype_test.go new file mode 100644 index 0000000..b1727a5 --- /dev/null +++ b/pkg/can-go/pkg/dbc/objecttype_test.go @@ -0,0 +1,23 @@ +package dbc + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestObjectType_Validate(t *testing.T) { + for _, tt := range []ObjectType{ + ObjectTypeUnspecified, + ObjectTypeNetworkNode, + ObjectTypeMessage, + ObjectTypeSignal, + ObjectTypeEnvironmentVariable, + } { + assert.NilError(t, tt.Validate()) + } +} + +func TestObjectType_Validate_Error(t *testing.T) { + assert.ErrorContains(t, ObjectType("foo").Validate(), "invalid object type") +} diff --git a/pkg/can-go/pkg/dbc/parser.go b/pkg/can-go/pkg/dbc/parser.go new file mode 100644 index 0000000..3ab4f8d --- /dev/null +++ b/pkg/can-go/pkg/dbc/parser.go @@ -0,0 +1,441 @@ +package dbc + +import ( + "bytes" + "fmt" + "strconv" + "strings" + "text/scanner" + "unicode/utf8" +) + +const defaultScannerMode = scanner.ScanIdents | scanner.ScanFloats + +const ( + defaultWhitespace = scanner.GoWhitespace + significantNewline = defaultWhitespace & ^uint64(1<<'\n') + significantTab = defaultWhitespace & ^uint64(1<<'\t') +) + +type token struct { + typ rune + pos scanner.Position + txt string +} + +type Parser struct { + sc scanner.Scanner + curr token + lookahead token + hasLookahead bool + data []byte + defs []Def +} + +func NewParser(filename string, data []byte) *Parser { + p := &Parser{data: data} + p.sc.Init(bytes.NewReader(data)) + p.sc.Mode = defaultScannerMode + p.sc.Whitespace = defaultWhitespace + p.sc.Filename = filename + p.sc.Error = func(sc *scanner.Scanner, msg string) { + p.failf(sc.Pos(), msg) + } + return p +} + +func (p *Parser) Defs() []Def { + return p.defs +} + +func (p *Parser) File() *File { + return &File{ + Name: p.sc.Filename, + Data: p.data, + Defs: p.defs, + } +} + +func (p *Parser) Parse() (err Error) { + defer func() { + if r := recover(); r != nil { + // recover from parse errors only + if errParse, ok := r.(*parseError); ok { + err = errParse + } else { + panic(r) + } + } + }() + for p.peekToken().typ != scanner.EOF { + var def Def + switch p.peekKeyword() { + case KeywordVersion: + def = &VersionDef{} + case KeywordBitTiming: + def = &BitTimingDef{} + case KeywordNewSymbols: + def = &NewSymbolsDef{} + case KeywordNodes: + def = &NodesDef{} + case KeywordMessage: + def = &MessageDef{} + case KeywordSignal: + def = &SignalDef{} + case KeywordEnvironmentVariable: + def = &EnvironmentVariableDef{} + case KeywordComment: + def = &CommentDef{} + case KeywordAttribute: + def = &AttributeDef{} + case KeywordAttributeDefault: + def = &AttributeDefaultValueDef{} + case KeywordAttributeValue: + def = &AttributeValueForObjectDef{} + case KeywordValueDescriptions: + def = &ValueDescriptionsDef{} + case KeywordValueTable: + def = &ValueTableDef{} + case KeywordSignalValueType: + def = &SignalValueTypeDef{} + case KeywordMessageTransmitters: + def = &MessageTransmittersDef{} + case KeywordEnvironmentVariableData: + def = &EnvironmentVariableDataDef{} + default: + def = &UnknownDef{} + } + def.parseFrom(p) + p.defs = append(p.defs, def) + } + return nil +} + +func (p *Parser) failf(pos scanner.Position, format string, a ...interface{}) { + panic(&parseError{pos: pos, reason: fmt.Sprintf(format, a...)}) +} + +// +// Whitespace +// + +func (p *Parser) useWhitespace(whitespace uint64) { + p.sc.Whitespace = whitespace +} + +// +// Characters +// + +func (p *Parser) nextRune() rune { + if p.hasLookahead { + if utf8.RuneCountInString(p.lookahead.txt) > 1 { + p.failf(p.lookahead.pos, "cannot get next rune when lookahead contains a token") + } + p.hasLookahead = false + r, _ := utf8.DecodeRuneInString(p.lookahead.txt) + return r + } + return p.sc.Next() +} + +func (p *Parser) peekRune() rune { + if p.hasLookahead { + if utf8.RuneCountInString(p.lookahead.txt) > 1 { + p.failf(p.lookahead.pos, "cannot peek next rune when lookahead contains a token") + } + r, _ := utf8.DecodeRuneInString(p.lookahead.txt) + return r + } + return p.sc.Peek() +} + +func (p *Parser) discardLine() { + p.useWhitespace(significantNewline) + defer p.useWhitespace(defaultWhitespace) + for p.nextToken().typ != '\n' && p.nextToken().typ != scanner.EOF { + // skip all non-newline tokens + } +} + +// +// Tokens +// + +func (p *Parser) nextToken() token { + if p.hasLookahead { + p.hasLookahead = false + p.curr = p.lookahead + return p.lookahead + } + p.curr = token{typ: p.sc.Scan(), pos: p.sc.Position, txt: p.sc.TokenText()} + return p.curr +} + +func (p *Parser) peekToken() token { + if p.hasLookahead { + return p.lookahead + } + p.hasLookahead = true + p.lookahead = token{typ: p.sc.Scan(), pos: p.sc.Position, txt: p.sc.TokenText()} + return p.lookahead +} + +// +// Data types +// + +// string parses a string that may contain newlines. +func (p *Parser) string() string { + tok := p.nextToken() + if tok.typ != '"' { + p.failf(tok.pos, `expected token "`) + } + var b strings.Builder +ReadLoop: + for { + switch r := p.nextRune(); r { + case scanner.EOF: + p.failf(tok.pos, "unterminated string") + case '"': + break ReadLoop + case '\n': + if _, err := b.WriteRune(' '); err != nil { + p.failf(tok.pos, err.Error()) + } + case '\\': + if p.peekRune() == '"' { + _ = p.nextRune() // include escaped quotes in string + if _, err := b.WriteString(`\"`); err != nil { + p.failf(tok.pos, err.Error()) + } + continue + } + fallthrough + default: + if _, err := b.WriteRune(r); err != nil { + p.failf(tok.pos, err.Error()) + } + } + } + return b.String() +} + +func (p *Parser) identifier() Identifier { + tok := p.nextToken() + if tok.typ != scanner.Ident { + p.failf(tok.pos, "expected ident") + } + id := Identifier(tok.txt) + if err := id.Validate(); err != nil { + p.failf(tok.pos, err.Error()) + } + return id +} + +func (p *Parser) stringIdentifier() Identifier { + tok := p.peekToken() + id := Identifier(p.string()) + if err := id.Validate(); err != nil { + p.failf(tok.pos, err.Error()) + } + return id +} + +func (p *Parser) keyword(kw Keyword) token { + if p.peekKeyword() != kw { + p.failf(p.peekToken().pos, "expected keyword: %v", kw) + } + return p.nextToken() +} + +func (p *Parser) peekKeyword() Keyword { + tok := p.peekToken() + if tok.typ != scanner.Ident { + p.failf(p.peekToken().pos, "expected ident") + } + return Keyword(tok.txt) +} + +func (p *Parser) token(typ rune) { + if tok := p.nextToken(); tok.typ != typ { + p.failf( + p.peekToken().pos, + "expected token: %v, found: %v (%v)", + scanner.TokenString(typ), + scanner.TokenString(tok.typ), + tok.txt, + ) + } +} + +func (p *Parser) optionalToken(typ rune) { + if p.peekToken().typ == typ { + p.token(typ) + } +} + +func (p *Parser) enumValue(values []string) string { + tok := p.peekToken() + if tok.typ == scanner.Int { + // SPECIAL-CASE: Enum values by index encountered in the wild + i := p.uint() + if i >= uint64(len(values)) { + p.failf(tok.pos, "enum index out of bounds") + } + return values[i] + } + return p.string() +} + +func (p *Parser) float() float64 { + var isNegative bool + if p.peekToken().typ == '-' { + p.token('-') + isNegative = true + } + tok := p.nextToken() + if tok.typ != scanner.Int && tok.typ != scanner.Float { + p.failf(p.peekToken().pos, "expected int or float") + } + f, err := strconv.ParseFloat(tok.txt, 64) + if err != nil { + p.failf(tok.pos, "invalid float") + } + if isNegative { + f *= -1 + } + return f +} + +func (p *Parser) int() int64 { + var isNegative bool + if p.peekToken().typ == '-' { + p.token('-') + isNegative = true + } + tok := p.nextToken() + if tok.typ != scanner.Int { + p.failf(tok.pos, "expected int") + } + i, err := strconv.ParseInt(tok.txt, 10, 64) + if err != nil { + p.failf(tok.pos, "invalid int") + } + if isNegative { + i *= -1 + } + return i +} + +func (p *Parser) uint() uint64 { + tok := p.nextToken() + if tok.typ != scanner.Int { + p.failf(tok.pos, "expected int") + } + i, err := strconv.ParseUint(tok.txt, 10, 64) + if err != nil { + p.failf(tok.pos, "invalid uint") + } + return i +} + +func (p *Parser) intInRange(min, max int) int { + var isNegative bool + if p.peekToken().typ == '-' { + p.token('-') + isNegative = true + } + tok := p.nextToken() + i, err := strconv.Atoi(tok.txt) + if err != nil { + p.failf(tok.pos, "invalid int") + } + if isNegative { + i *= -1 + } + if i < min || i > max { + p.failf(tok.pos, "invalid value") + } + return i +} + +func (p *Parser) optionalUint() uint64 { + if p.peekToken().typ != scanner.Int { + return 0 + } + tok := p.nextToken() + i, err := strconv.ParseUint(tok.txt, 10, 64) + if err != nil { + p.failf(tok.pos, "invalid uint") + } + return i +} + +func (p *Parser) anyOf(tokenTypes ...rune) rune { + tok := p.nextToken() + for _, tokenType := range tokenTypes { + if tok.typ == tokenType { + return tok.typ + } + } + p.failf(tok.pos, "unexpected token") + return 0 +} + +func (p *Parser) optionalObjectType() ObjectType { + tok := p.peekToken() + if tok.typ != scanner.Ident { + return ObjectTypeUnspecified + } + objectType := ObjectType(p.identifier()) + if err := objectType.Validate(); err != nil { + p.failf(tok.pos, err.Error()) + } + return objectType +} + +func (p *Parser) messageID() MessageID { + tok := p.peekToken() + messageID := MessageID(p.uint()) + if err := messageID.Validate(); err != nil { + p.failf(tok.pos, err.Error()) + } + return messageID +} + +func (p *Parser) signalValueType() SignalValueType { + tok := p.peekToken() + signalValueType := SignalValueType(p.uint()) + if err := signalValueType.Validate(); err != nil { + p.failf(tok.pos, err.Error()) + } + return signalValueType +} + +func (p *Parser) environmentVariableType() EnvironmentVariableType { + tok := p.peekToken() + environmentVariableType := EnvironmentVariableType(p.uint()) + if err := environmentVariableType.Validate(); err != nil { + p.failf(tok.pos, err.Error()) + } + return environmentVariableType +} + +func (p *Parser) attributeValueType() AttributeValueType { + tok := p.peekToken() + attributeValueType := AttributeValueType(p.identifier()) + if err := attributeValueType.Validate(); err != nil { + p.failf(tok.pos, err.Error()) + } + return attributeValueType +} + +func (p *Parser) accessType() AccessType { + tok := p.peekToken() + accessType := AccessType(p.identifier()) + if err := accessType.Validate(); err != nil { + p.failf(tok.pos, "invalid access type") + } + return accessType +} diff --git a/pkg/can-go/pkg/dbc/parser_test.go b/pkg/can-go/pkg/dbc/parser_test.go new file mode 100644 index 0000000..e63bd40 --- /dev/null +++ b/pkg/can-go/pkg/dbc/parser_test.go @@ -0,0 +1,1082 @@ +package dbc + +import ( + "io/ioutil" + "os" + "strings" + "testing" + "text/scanner" + + "github.com/davecgh/go-spew/spew" + "gotest.tools/v3/assert" +) + +func shouldUpdateGoldenFiles() bool { + return os.Getenv("GOLDEN") == "true" +} + +func TestParse_ExampleDBC(t *testing.T) { + const inputFile = "../../testdata/dbc/example/example.dbc" + const goldenFile = "../../testdata/dbc/example/example.dbc.golden" + data, err := ioutil.ReadFile(inputFile) + assert.NilError(t, err) + p := NewParser(inputFile, data) + assert.NilError(t, p.Parse()) + if shouldUpdateGoldenFiles() { + assert.NilError(t, ioutil.WriteFile(goldenFile, []byte(dump(p.Defs())), 0600)) + } + goldenFileData, err := ioutil.ReadFile(goldenFile) + assert.NilError(t, err) + assert.Equal(t, string(goldenFileData), dump(p.Defs())) +} + +func TestParser_Parse(t *testing.T) { + for _, tt := range []struct { + name string + text string + defs []Def + }{ + { + name: "version.dbc", + text: `VERSION "foo"`, + defs: []Def{ + &VersionDef{ + Pos: scanner.Position{ + Filename: "version.dbc", + Line: 1, + Column: 1, + }, + Version: "foo", + }, + }, + }, + + { + name: "multiple_version.dbc", + text: strings.Join([]string{ + `VERSION "foo"`, + `VERSION "bar"`, + }, "\n"), + defs: []Def{ + &VersionDef{ + Pos: scanner.Position{ + Filename: "multiple_version.dbc", + Line: 1, + Column: 1, + }, + Version: "foo", + }, + &VersionDef{ + Pos: scanner.Position{ + Filename: "multiple_version.dbc", + Line: 2, + Column: 1, + Offset: 14, + }, + Version: "bar", + }, + }, + }, + + { + name: "no_bus_speed.dbc", + text: `BS_:`, + defs: []Def{ + &BitTimingDef{ + Pos: scanner.Position{ + Filename: "no_bus_speed.dbc", + Line: 1, + Column: 1, + }, + }, + }, + }, + + { + name: "bus_speed.dbc", + text: `BS_: 250000`, + defs: []Def{ + &BitTimingDef{ + Pos: scanner.Position{ + Filename: "bus_speed.dbc", + Line: 1, + Column: 1, + }, + BaudRate: 250000, + }, + }, + }, + + { + name: "symbols.dbc", + text: strings.Join([]string{ + "NS_ :", + "NS_DESC_", + "CM_", + "BA_DEF_", + "BA_", + "VAL_", + }, "\n\t"), + defs: []Def{ + &NewSymbolsDef{ + Pos: scanner.Position{ + Filename: "symbols.dbc", + Line: 1, + Column: 1, + }, + Symbols: []Keyword{ + "NS_DESC_", + "CM_", + "BA_DEF_", + "BA_", + "VAL_", + }, + }, + }, + }, + + { + name: "standard_message.dbc", + text: "BO_ 804 CRUISE: 8 PCM", + defs: []Def{ + &MessageDef{ + Pos: scanner.Position{ + Filename: "standard_message.dbc", + Line: 1, + Column: 1, + }, + Name: "CRUISE", + MessageID: 804, + Size: 8, + Transmitter: "PCM", + }, + }, + }, + + { + name: "extended_message.dbc", + text: "BO_ 2566857412 BMS2_4: 8 Vector__XXX", + defs: []Def{ + &MessageDef{ + Pos: scanner.Position{ + Filename: "extended_message.dbc", + Line: 1, + Column: 1, + }, + Name: "BMS2_4", + MessageID: 2566857412, + Size: 8, + Transmitter: "Vector__XXX", + }, + }, + }, + + { + name: "signal.dbc", + text: `SG_ CellTempLowest : 32|8@0+ (1,-40) [-40|215] "C" Vector__XXX`, + defs: []Def{ + &SignalDef{ + Pos: scanner.Position{ + Filename: "signal.dbc", + Line: 1, + Column: 1, + }, + Name: "CellTempLowest", + StartBit: 32, + Size: 8, + IsBigEndian: true, + Factor: 1, + Offset: -40, + Minimum: -40, + Maximum: 215, + Unit: "C", + Receivers: []Identifier{"Vector__XXX"}, + }, + }, + }, + + { + name: "multiplexer_signal.dbc", + text: `SG_ TestSignal M : 56|8@1+ (0.001,0) [0|0.255] "l/mm" XXX`, + defs: []Def{ + &SignalDef{ + Pos: scanner.Position{ + Filename: "multiplexer_signal.dbc", + Line: 1, + Column: 1, + }, + Name: "TestSignal", + StartBit: 56, + Size: 8, + Factor: 0.001, + Offset: 0, + Minimum: 0, + Maximum: 0.255, + Unit: "l/mm", + Receivers: []Identifier{"XXX"}, + IsMultiplexerSwitch: true, + }, + }, + }, + + { + name: "multiplexed_signal.dbc", + text: `SG_ TestSignal m2 : 56|8@1+ (0.001,0) [0|0.255] "l/mm" XXX`, + defs: []Def{ + &SignalDef{ + Pos: scanner.Position{ + Filename: "multiplexed_signal.dbc", + Line: 1, + Column: 1, + }, + Name: "TestSignal", + StartBit: 56, + Size: 8, + Factor: 0.001, + Offset: 0, + Minimum: 0, + Maximum: 0.255, + Unit: "l/mm", + Receivers: []Identifier{"XXX"}, + IsMultiplexed: true, + MultiplexerSwitch: 2, + }, + }, + }, + + { + name: "comment.dbc", + text: `CM_ "comment";`, + defs: []Def{ + &CommentDef{ + Pos: scanner.Position{ + Filename: "comment.dbc", + Line: 1, + Column: 1, + }, + Comment: "comment", + }, + }, + }, + + { + name: "node_comment.dbc", + text: `CM_ BU_ NodeName "node comment";`, + defs: []Def{ + &CommentDef{ + Pos: scanner.Position{ + Filename: "node_comment.dbc", + Line: 1, + Column: 1, + }, + ObjectType: ObjectTypeNetworkNode, + NodeName: "NodeName", + Comment: "node comment", + }, + }, + }, + + { + name: "message_comment.dbc", + text: `CM_ BO_ 1234 "message comment";`, + defs: []Def{ + &CommentDef{ + Pos: scanner.Position{ + Filename: "message_comment.dbc", + Line: 1, + Column: 1, + }, + ObjectType: ObjectTypeMessage, + MessageID: 1234, + Comment: "message comment", + }, + }, + }, + + { + name: "signal_comment.dbc", + text: `CM_ SG_ 1234 SignalName "signal comment";`, + defs: []Def{ + &CommentDef{ + Pos: scanner.Position{ + Filename: "signal_comment.dbc", + Line: 1, + Column: 1, + }, + ObjectType: ObjectTypeSignal, + MessageID: 1234, + SignalName: "SignalName", + Comment: "signal comment", + }, + }, + }, + + { + name: "int_attribute_definition.dbc", + text: `BA_DEF_ "AttributeName" INT 5 10;`, + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "int_attribute_definition.dbc", + Line: 1, + Column: 1, + }, + Name: "AttributeName", + Type: AttributeValueTypeInt, + MinimumInt: 5, + MaximumInt: 10, + }, + }, + }, + + { + name: "int_attribute_definition_no_min_or_max.dbc", + text: `BA_DEF_ "AttributeName" INT;`, + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "int_attribute_definition_no_min_or_max.dbc", + Line: 1, + Column: 1, + }, + Name: "AttributeName", + Type: AttributeValueTypeInt, + }, + }, + }, + + { + name: "float_attribute_definition.dbc", + text: `BA_DEF_ "AttributeName" FLOAT 0.5 1.5;`, + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "float_attribute_definition.dbc", + Line: 1, + Column: 1, + }, + Name: "AttributeName", + Type: AttributeValueTypeFloat, + MinimumFloat: 0.5, + MaximumFloat: 1.5, + }, + }, + }, + + { + name: "float_attribute_definition_no_min_or_max.dbc", + text: `BA_DEF_ "AttributeName" FLOAT;`, + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "float_attribute_definition_no_min_or_max.dbc", + Line: 1, + Column: 1, + }, + Name: "AttributeName", + Type: AttributeValueTypeFloat, + }, + }, + }, + + { + name: "string_attribute.dbc", + text: `BA_DEF_ "AttributeName" STRING;`, + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "string_attribute.dbc", + Line: 1, + Column: 1, + }, + Name: "AttributeName", + Type: AttributeValueTypeString, + }, + }, + }, + + { + name: "enum_attribute.dbc", + text: `BA_DEF_ "AttributeName" ENUM "value1","value2";`, + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "enum_attribute.dbc", + Line: 1, + Column: 1, + }, + Name: "AttributeName", + Type: AttributeValueTypeEnum, + EnumValues: []string{"value1", "value2"}, + }, + }, + }, + + { + name: "enum_attribute_for_messages.dbc", + text: `BA_DEF_ BO_ "VFrameFormat" ENUM "StandardCAN","ExtendedCAN","reserved","J1939PG";`, + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "enum_attribute_for_messages.dbc", + Line: 1, + Column: 1, + }, + Name: "VFrameFormat", + ObjectType: ObjectTypeMessage, + Type: AttributeValueTypeEnum, + EnumValues: []string{"StandardCAN", "ExtendedCAN", "reserved", "J1939PG"}, + }, + }, + }, + + { + name: "attribute_default_string.dbc", + text: strings.Join([]string{ + `BA_DEF_ "Foo" STRING;`, + `BA_DEF_DEF_ "Foo" "string value";`, + }, "\n"), + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "attribute_default_string.dbc", + Line: 1, + Column: 1, + }, + Name: "Foo", + Type: AttributeValueTypeString, + }, + &AttributeDefaultValueDef{ + Pos: scanner.Position{ + Filename: "attribute_default_string.dbc", + Line: 2, + Column: 1, + Offset: 22, + }, + AttributeName: "Foo", + DefaultStringValue: "string value", + }, + }, + }, + + { + name: "attribute_default_int.dbc", + text: strings.Join([]string{ + `BA_DEF_ "Foo" INT 0 200;`, + `BA_DEF_DEF_ "Foo" 100;`, + }, "\n"), + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "attribute_default_int.dbc", + Line: 1, + Column: 1, + }, + Name: "Foo", + Type: AttributeValueTypeInt, + MinimumInt: 0, + MaximumInt: 200, + }, + &AttributeDefaultValueDef{ + Pos: scanner.Position{ + Filename: "attribute_default_int.dbc", + Line: 2, + Column: 1, + Offset: 25, + }, + AttributeName: "Foo", + DefaultIntValue: 100, + }, + }, + }, + + { + name: "attribute_default_float.dbc", + text: strings.Join([]string{ + `BA_DEF_ "Foo" FLOAT 0.5 200.5;`, + `BA_DEF_DEF_ "Foo" 100.5;`, + }, "\n"), + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "attribute_default_float.dbc", + Line: 1, + Column: 1, + }, + Name: "Foo", + Type: AttributeValueTypeFloat, + MinimumFloat: 0.5, + MaximumFloat: 200.5, + }, + &AttributeDefaultValueDef{ + Pos: scanner.Position{ + Filename: "attribute_default_float.dbc", + Line: 2, + Column: 1, + Offset: 31, + }, + AttributeName: "Foo", + DefaultFloatValue: 100.5, + }, + }, + }, + + { + name: "attribute_value.dbc", + text: strings.Join([]string{ + `BA_DEF_ "Foo" FLOAT;`, + `BA_ "Foo" 100.5;`, + }, "\n"), + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "attribute_value.dbc", + Line: 1, + Column: 1, + }, + Name: "Foo", + Type: AttributeValueTypeFloat, + }, + &AttributeValueForObjectDef{ + Pos: scanner.Position{ + Filename: "attribute_value.dbc", + Line: 2, + Column: 1, + Offset: 21, + }, + AttributeName: "Foo", + FloatValue: 100.5, + }, + }, + }, + + { + name: "negative_attribute_value.dbc", + text: strings.Join([]string{ + `BA_DEF_ "Foo" INT;`, + `BA_ "Foo" -100;`, + }, "\n"), + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "negative_attribute_value.dbc", + Line: 1, + Column: 1, + }, + Name: "Foo", + Type: AttributeValueTypeInt, + }, + &AttributeValueForObjectDef{ + Pos: scanner.Position{ + Filename: "negative_attribute_value.dbc", + Line: 2, + Column: 1, + Offset: 19, + }, + AttributeName: "Foo", + IntValue: -100, + }, + }, + }, + + { + name: "node_attribute_value.dbc", + text: strings.Join([]string{ + `BA_DEF_ "Foo" INT;`, + `BA_ "Foo" BU_ TestNode 100;`, + }, "\n"), + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "node_attribute_value.dbc", + Line: 1, + Column: 1, + }, + Name: "Foo", + Type: AttributeValueTypeInt, + }, + &AttributeValueForObjectDef{ + Pos: scanner.Position{ + Filename: "node_attribute_value.dbc", + Line: 2, + Column: 1, + Offset: 19, + }, + AttributeName: "Foo", + ObjectType: ObjectTypeNetworkNode, + NodeName: "TestNode", + IntValue: 100, + }, + }, + }, + + { + name: "message_attribute_value.dbc", + text: strings.Join([]string{ + `BA_DEF_ "Foo" STRING;`, + `BA_ "Foo" BO_ 1234 "string value";`, + }, "\n"), + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "message_attribute_value.dbc", + Line: 1, + Column: 1, + }, + Name: "Foo", + Type: AttributeValueTypeString, + }, + &AttributeValueForObjectDef{ + Pos: scanner.Position{ + Filename: "message_attribute_value.dbc", + Line: 2, + Column: 1, + Offset: 22, + }, + AttributeName: "Foo", + ObjectType: ObjectTypeMessage, + MessageID: 1234, + StringValue: "string value", + }, + }, + }, + + { + name: "signal_attribute_value.dbc", + text: strings.Join([]string{ + `BA_DEF_ "Foo" STRING;`, + `BA_ "Foo" SG_ 1234 SignalName "string value";`, + }, "\n"), + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "signal_attribute_value.dbc", + Line: 1, + Column: 1, + }, + Name: "Foo", + Type: AttributeValueTypeString, + }, + &AttributeValueForObjectDef{ + Pos: scanner.Position{ + Filename: "signal_attribute_value.dbc", + Line: 2, + Column: 1, + Offset: 22, + }, + AttributeName: "Foo", + ObjectType: ObjectTypeSignal, + MessageID: 1234, + SignalName: "SignalName", + StringValue: "string value", + }, + }, + }, + + { + name: "enum_attribute_value_by_index.dbc", + text: strings.Join([]string{ + `BA_DEF_ BO_ "VFrameFormat" ENUM "StandardCAN","ExtendedCAN","reserved","J1939PG";`, + `BA_ "VFrameFormat" BO_ 1234 3;`, + }, "\n"), + defs: []Def{ + &AttributeDef{ + Pos: scanner.Position{ + Filename: "enum_attribute_value_by_index.dbc", + Line: 1, + Column: 1, + }, + Name: "VFrameFormat", + ObjectType: ObjectTypeMessage, + Type: AttributeValueTypeEnum, + EnumValues: []string{"StandardCAN", "ExtendedCAN", "reserved", "J1939PG"}, + }, + &AttributeValueForObjectDef{ + Pos: scanner.Position{ + Filename: "enum_attribute_value_by_index.dbc", + Line: 2, + Column: 1, + Offset: 84, + }, + AttributeName: "VFrameFormat", + ObjectType: ObjectTypeMessage, + MessageID: 1234, + StringValue: "J1939PG", + }, + }, + }, + + { + name: "value_descriptions_for_signal.dbc", + text: `VAL_ 3 StW_AnglSens_Id 2 "MUST" 0 "PSBL" 1 "SELF";`, + defs: []Def{ + &ValueDescriptionsDef{ + Pos: scanner.Position{ + Filename: "value_descriptions_for_signal.dbc", + Line: 1, + Column: 1, + }, + ObjectType: ObjectTypeSignal, + MessageID: 3, + SignalName: "StW_AnglSens_Id", + ValueDescriptions: []ValueDescriptionDef{ + { + Pos: scanner.Position{ + Filename: "value_descriptions_for_signal.dbc", + Line: 1, + Column: 24, + Offset: 23, + }, + Value: 2, + Description: "MUST", + }, + { + Pos: scanner.Position{ + Filename: "value_descriptions_for_signal.dbc", + Line: 1, + Column: 33, + Offset: 32, + }, + Value: 0, + Description: "PSBL", + }, + { + Pos: scanner.Position{ + Filename: "value_descriptions_for_signal.dbc", + Line: 1, + Column: 42, + Offset: 41, + }, + Value: 1, + Description: "SELF", + }, + }, + }, + }, + }, + + { + name: "value_table.dbc", + text: `VAL_TABLE_ DI_gear 7 "DI_GEAR_SNA" 4 "DI_GEAR_D";`, + defs: []Def{ + &ValueTableDef{ + Pos: scanner.Position{ + Filename: "value_table.dbc", + Line: 1, + Column: 1, + }, + TableName: "DI_gear", + ValueDescriptions: []ValueDescriptionDef{ + { + Pos: scanner.Position{ + Filename: "value_table.dbc", + Line: 1, + Column: 20, + Offset: 19, + }, + Value: 7, + Description: "DI_GEAR_SNA", + }, + { + Pos: scanner.Position{ + Filename: "value_table.dbc", + Line: 1, + Column: 36, + Offset: 35, + }, + Value: 4, + Description: "DI_GEAR_D", + }, + }, + }, + }, + }, + + { + name: "node_list.dbc", + text: `BU_: RSDS`, + defs: []Def{ + &NodesDef{ + Pos: scanner.Position{ + Filename: "node_list.dbc", + Line: 1, + Column: 1, + }, + NodeNames: []Identifier{"RSDS"}, + }, + }, + }, + + { + name: "node_list_followed_by_single_newline_followed_by_value_table.dbc", + text: strings.Join([]string{ + `BU_: RSDS`, + `VAL_TABLE_ TableName 3 "Value3" 2 "Value2" 1 "Value1" 0 "Value0";`, + }, "\n"), + defs: []Def{ + &NodesDef{ + Pos: scanner.Position{ + Filename: "node_list_followed_by_single_newline_followed_by_value_table.dbc", + Line: 1, + Column: 1, + }, + NodeNames: []Identifier{"RSDS"}, + }, + &ValueTableDef{ + Pos: scanner.Position{ + Filename: "node_list_followed_by_single_newline_followed_by_value_table.dbc", + Line: 2, + Column: 1, + Offset: 10, + }, + TableName: "TableName", + ValueDescriptions: []ValueDescriptionDef{ + { + Pos: scanner.Position{ + Filename: "node_list_followed_by_single_newline_followed_by_value_table.dbc", + Line: 2, + Column: 22, + Offset: 31, + }, + Value: 3, + Description: "Value3", + }, + { + Pos: scanner.Position{ + Filename: "node_list_followed_by_single_newline_followed_by_value_table.dbc", + Line: 2, + Column: 33, + Offset: 42, + }, + Value: 2, + Description: "Value2", + }, + { + Pos: scanner.Position{ + Filename: "node_list_followed_by_single_newline_followed_by_value_table.dbc", + Line: 2, + Column: 44, + Offset: 53, + }, + Value: 1, + Description: "Value1", + }, + { + Pos: scanner.Position{ + Filename: "node_list_followed_by_single_newline_followed_by_value_table.dbc", + Line: 2, + Column: 55, + Offset: 64, + }, + Value: 0, + Description: "Value0", + }, + }, + }, + }, + }, + + { + name: "signal_value_type.dbc", + text: `SIG_VALTYPE_ 42 TestSignal 2;`, + defs: []Def{ + &SignalValueTypeDef{ + Pos: scanner.Position{ + Filename: "signal_value_type.dbc", + Line: 1, + Column: 1, + }, + MessageID: 42, + SignalName: "TestSignal", + SignalValueType: SignalValueTypeFloat64, + }, + }, + }, + + { + name: "signal_value_type_with_colon.dbc", + text: `SIG_VALTYPE_ 42 TestSignal : 2;`, + defs: []Def{ + &SignalValueTypeDef{ + Pos: scanner.Position{ + Filename: "signal_value_type_with_colon.dbc", + Line: 1, + Column: 1, + }, + MessageID: 42, + SignalName: "TestSignal", + SignalValueType: SignalValueTypeFloat64, + }, + }, + }, + + { + name: "message_transmitters.dbc", + text: `BO_TX_BU_ 42: Node1 Node2;`, + defs: []Def{ + &MessageTransmittersDef{ + Pos: scanner.Position{ + Filename: "message_transmitters.dbc", + Line: 1, + Column: 1, + }, + MessageID: 42, + Transmitters: []Identifier{"Node1", "Node2"}, + }, + }, + }, + + { + name: "message_transmitters_comma_separated.dbc", + text: `BO_TX_BU_ 42: Node1,Node2;`, + defs: []Def{ + &MessageTransmittersDef{ + Pos: scanner.Position{ + Filename: "message_transmitters_comma_separated.dbc", + Line: 1, + Column: 1, + }, + MessageID: 42, + Transmitters: []Identifier{"Node1", "Node2"}, + }, + }, + }, + + { + name: "environment_variable_data.dbc", + text: `ENVVAR_DATA_ VariableName: 42;`, + defs: []Def{ + &EnvironmentVariableDataDef{ + Pos: scanner.Position{ + Filename: "environment_variable_data.dbc", + Line: 1, + Column: 1, + }, + EnvironmentVariableName: "VariableName", + DataSize: 42, + }, + }, + }, + + { + name: "environment_variable_value_descriptions.dbc", + text: `VAL_ VariableName 2 "Value2" 1 "Value1" 0 "Value0";`, + defs: []Def{ + &ValueDescriptionsDef{ + Pos: scanner.Position{ + Filename: "environment_variable_value_descriptions.dbc", + Line: 1, + Column: 1, + }, + ObjectType: ObjectTypeEnvironmentVariable, + EnvironmentVariableName: "VariableName", + ValueDescriptions: []ValueDescriptionDef{ + { + Pos: scanner.Position{ + Filename: "environment_variable_value_descriptions.dbc", + Line: 1, + Column: 19, + Offset: 18, + }, + Value: 2, + Description: "Value2", + }, + { + Pos: scanner.Position{ + Filename: "environment_variable_value_descriptions.dbc", + Line: 1, + Column: 30, + Offset: 29, + }, + Value: 1, + Description: "Value1", + }, + { + Pos: scanner.Position{ + Filename: "environment_variable_value_descriptions.dbc", + Line: 1, + Column: 41, + Offset: 40, + }, + Value: 0, + Description: "Value0", + }, + }, + }, + }, + }, + + { + name: "unknown_def.dbc", + text: `FOO_ Bar 2 Baz;`, + defs: []Def{ + &UnknownDef{ + Pos: scanner.Position{ + Filename: "unknown_def.dbc", + Line: 1, + Column: 1, + }, + Keyword: "FOO_", + }, + }, + }, + } { + tt := tt + t.Run(tt.name, func(t *testing.T) { + p := NewParser(tt.name, []byte(tt.text)) + assert.NilError(t, p.Parse()) + assert.DeepEqual(t, tt.defs, p.Defs()) + }) + } +} + +func TestParser_Parse_Error(t *testing.T) { + for _, tt := range []struct { + name string + text string + err *parseError + }{ + { + name: "non_string_version.dbc", + text: "VERSION foo", + err: &parseError{ + pos: scanner.Position{ + Filename: "non_string_version.dbc", + Line: 1, + Column: 9, + Offset: 8, + }, + reason: "expected token \"", + }, + }, + { + name: "invalid_utf8.dbc", + text: `VERSION "foo` + string([]byte{0xc3, 0x28}) + `"`, + err: &parseError{ + pos: scanner.Position{ + Filename: "invalid_utf8.dbc", + Line: 1, + Column: 13, + Offset: 12, + }, + reason: "invalid UTF-8 encoding", + }, + }, + } { + tt := tt + t.Run(tt.name, func(t *testing.T) { + p := NewParser(tt.name, []byte(tt.text)) + assert.Error(t, p.Parse(), tt.err.Error()) + }) + } +} + +func dump(data interface{}) string { + spewConfig := spew.ConfigState{ + Indent: " ", + DisablePointerAddresses: true, + DisableCapacities: true, + SortKeys: true, + } + return spewConfig.Sdump(data) +} diff --git a/pkg/can-go/pkg/dbc/placeholder.go b/pkg/can-go/pkg/dbc/placeholder.go new file mode 100644 index 0000000..faf1fd9 --- /dev/null +++ b/pkg/can-go/pkg/dbc/placeholder.go @@ -0,0 +1,4 @@ +package dbc + +// NodePlaceholder is the placeholder node name used when no actual node is specified. +const NodePlaceholder Identifier = "Vector__XXX" diff --git a/pkg/can-go/pkg/dbc/signalvaluetype.go b/pkg/can-go/pkg/dbc/signalvaluetype.go new file mode 100644 index 0000000..f461f6c --- /dev/null +++ b/pkg/can-go/pkg/dbc/signalvaluetype.go @@ -0,0 +1,24 @@ +package dbc + +import "fmt" + +// SignalValueType represents an extended signal value type. +type SignalValueType uint64 + +const ( + SignalValueTypeInt SignalValueType = 0 + SignalValueTypeFloat32 SignalValueType = 1 + SignalValueTypeFloat64 SignalValueType = 2 +) + +// Validate returns an error for invalid signal value types. +func (s SignalValueType) Validate() error { + switch s { + case SignalValueTypeInt: + case SignalValueTypeFloat32: + case SignalValueTypeFloat64: + default: + return fmt.Errorf("invalid signal value type: %v", s) + } + return nil +} diff --git a/pkg/can-go/pkg/dbc/signalvaluetype_test.go b/pkg/can-go/pkg/dbc/signalvaluetype_test.go new file mode 100644 index 0000000..fa1e2b7 --- /dev/null +++ b/pkg/can-go/pkg/dbc/signalvaluetype_test.go @@ -0,0 +1,21 @@ +package dbc + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestSignalValueType_Validate(t *testing.T) { + for _, tt := range []SignalValueType{ + SignalValueTypeInt, + SignalValueTypeFloat32, + SignalValueTypeFloat64, + } { + assert.NilError(t, tt.Validate()) + } +} + +func TestSignalValueType_Validate_Error(t *testing.T) { + assert.Error(t, SignalValueType(42).Validate(), "invalid signal value type: 42") +} diff --git a/pkg/can-go/pkg/descriptor/database.go b/pkg/can-go/pkg/descriptor/database.go new file mode 100644 index 0000000..4e36ae7 --- /dev/null +++ b/pkg/can-go/pkg/descriptor/database.go @@ -0,0 +1,55 @@ +package descriptor + +import ( + "path" + "strings" +) + +// Database represents a CAN database. +type Database struct { + // SourceFile of the database. + // + // Example: + // github.com/einride/can-databases/dbc/j1939.dbc + SourceFile string + // Version of the database. + Version string + // Messages in the database. + Messages [2048]*Message + // Nodes in the database. + Nodes []*Node + // ECUs in the database + ECUs map[string]bool +} + +func (d *Database) Node(nodeName string) (*Node, bool) { + for _, n := range d.Nodes { + if n.Name == nodeName { + return n, true + } + } + return nil, false +} + +func (d *Database) Message(id uint32) (*Message, bool) { + m := d.Messages[id] + return m, m != nil +} + +func (d *Database) Signal(messageID uint32, signalName string) (*Signal, bool) { + message, ok := d.Message(messageID) + if !ok { + return nil, false + } + for _, s := range message.Signals { + if s.Name == signalName { + return s, true + } + } + return nil, false +} + +// Description returns the name of the Database. +func (d *Database) Name() string { + return strings.TrimSuffix(path.Base(d.SourceFile), path.Ext(d.SourceFile)) +} diff --git a/pkg/can-go/pkg/descriptor/message.go b/pkg/can-go/pkg/descriptor/message.go new file mode 100644 index 0000000..0922b77 --- /dev/null +++ b/pkg/can-go/pkg/descriptor/message.go @@ -0,0 +1,73 @@ +package descriptor + +import ( + "time" + + "github.com/fiskerinc/cloud-services/pkg/can-go" +) + +// Message describes a CAN message. +type Message struct { + // Description of the message. + Name string + // ID of the message. + ID uint32 + // IsExtended is true if the message is an extended CAN message. + IsExtended bool + // SendType is the message's send type. + SendType SendType + // Length in bytes. + Length uint16 + // Description of the message. + Description string + // Signals in the message payload. + Signals []*Signal + // SenderNode is the name of the node sending the message. + SenderNode string + // CycleTime is the cycle time of a cyclic message. + CycleTime time.Duration + // DelayTime is the allowed delay between cyclic message sends. + DelayTime time.Duration +} + +// MultiplexerSignal returns the message's multiplexer signal. +func (m *Message) MultiplexerSignal() (*Signal, bool) { + for _, s := range m.Signals { + if s.IsMultiplexer { + return s, true + } + } + return nil, false +} + +// Decode decodes a can Payload into a decoded signal array. +func (m *Message) Decode(p *can.Payload) []DecodedSignal { + var data can.Data + if m.Length <= 8 { + copy(data[:], p.Data) + } + + numSignals := len(m.Signals) + + signals := make([]DecodedSignal, numSignals) + for i, signal := range m.Signals { + var valueDesc string + var value float64 + if m.Length > 8 { + valueDesc, _ = signal.UnmarshalValueDescriptionPayload(p) + value = signal.DecodePayload(p) + } else { + valueDesc, _ = signal.UnmarshalValueDescription(data) + value = signal.Decode(data) + } + + s := DecodedSignal{ + Value: value, + Description: valueDesc, + Signal: signal, + } + + signals[i] = s + } + return signals +} diff --git a/pkg/can-go/pkg/descriptor/message_test.go b/pkg/can-go/pkg/descriptor/message_test.go new file mode 100644 index 0000000..fe6d7da --- /dev/null +++ b/pkg/can-go/pkg/descriptor/message_test.go @@ -0,0 +1,37 @@ +package descriptor + +import ( + "testing" + + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestMessage_MultiplexerSignal(t *testing.T) { + mux := &Signal{ + Name: "Mux", + IsMultiplexer: true, + } + m := &Message{ + Signals: []*Signal{ + {Name: "NotMux"}, + mux, + {Name: "AlsoNotMux"}, + }, + } + actualMux, ok := m.MultiplexerSignal() + assert.Assert(t, ok) + assert.DeepEqual(t, mux, actualMux) +} + +func TestMessage_MultiplexerSignal_NotFound(t *testing.T) { + m := &Message{ + Signals: []*Signal{ + {Name: "NotMux"}, + {Name: "AlsoNotMux"}, + }, + } + actualMux, ok := m.MultiplexerSignal() + assert.Assert(t, !ok) + assert.Assert(t, is.Nil(actualMux)) +} diff --git a/pkg/can-go/pkg/descriptor/node.go b/pkg/can-go/pkg/descriptor/node.go new file mode 100644 index 0000000..ac5ac08 --- /dev/null +++ b/pkg/can-go/pkg/descriptor/node.go @@ -0,0 +1,9 @@ +package descriptor + +// Node describes a CAN node. +type Node struct { + // Description of the CAN node. + Name string + // Description of the CAN node. + Description string +} diff --git a/pkg/can-go/pkg/descriptor/sendtype.go b/pkg/can-go/pkg/descriptor/sendtype.go new file mode 100644 index 0000000..298daec --- /dev/null +++ b/pkg/can-go/pkg/descriptor/sendtype.go @@ -0,0 +1,31 @@ +package descriptor + +import "strings" + +// SendType represents the send type of a message. +type SendType uint8 + +//go:generate stringer -type SendType -trimprefix SendType + +const ( + // SendTypeNone means the send type is unknown or not specified. + SendTypeNone SendType = iota + // SendTypeCyclic means the message is sent cyclically. + SendTypeCyclic + // SendTypeEvent means the message is only sent upon event or request. + SendTypeEvent +) + +// UnmarshalString sets the value of *s from the provided string. +func (s *SendType) UnmarshalString(str string) error { + // TODO: Decide on conventions and make this more strict + switch strings.ToLower(str) { + case "cyclic", "cyclicifactive", "periodic", "fixedperiodic", "enabledperiodic", "eventperiodic": + *s = SendTypeCyclic + case "event", "onevent": + *s = SendTypeEvent + default: + *s = SendTypeNone + } + return nil +} diff --git a/pkg/can-go/pkg/descriptor/sendtype_string.go b/pkg/can-go/pkg/descriptor/sendtype_string.go new file mode 100644 index 0000000..8194b4e --- /dev/null +++ b/pkg/can-go/pkg/descriptor/sendtype_string.go @@ -0,0 +1,25 @@ +// Code generated by "stringer -type SendType -trimprefix SendType"; DO NOT EDIT. + +package descriptor + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[SendTypeNone-0] + _ = x[SendTypeCyclic-1] + _ = x[SendTypeEvent-2] +} + +const _SendType_name = "NoneCyclicEvent" + +var _SendType_index = [...]uint8{0, 4, 10, 15} + +func (i SendType) String() string { + if i >= SendType(len(_SendType_index)-1) { + return "SendType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _SendType_name[_SendType_index[i]:_SendType_index[i+1]] +} diff --git a/pkg/can-go/pkg/descriptor/sendtype_test.go b/pkg/can-go/pkg/descriptor/sendtype_test.go new file mode 100644 index 0000000..83c7cb8 --- /dev/null +++ b/pkg/can-go/pkg/descriptor/sendtype_test.go @@ -0,0 +1,26 @@ +package descriptor + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestSendType_UnmarshalString(t *testing.T) { + for _, tt := range []struct { + str string + expected SendType + }{ + {str: "Cyclic", expected: SendTypeCyclic}, + {str: "Periodic", expected: SendTypeCyclic}, + {str: "OnEvent", expected: SendTypeEvent}, + {str: "Event", expected: SendTypeEvent}, + } { + tt := tt + t.Run(tt.str, func(t *testing.T) { + var actual SendType + assert.NilError(t, actual.UnmarshalString(tt.str)) + assert.Equal(t, tt.expected, actual) + }) + } +} diff --git a/pkg/can-go/pkg/descriptor/signal.go b/pkg/can-go/pkg/descriptor/signal.go new file mode 100644 index 0000000..897f148 --- /dev/null +++ b/pkg/can-go/pkg/descriptor/signal.go @@ -0,0 +1,326 @@ +package descriptor + +import ( + "math" + + "github.com/fiskerinc/cloud-services/pkg/can-go" +) + +// Signal describes a CAN signal. +type Signal struct { + // Description of the signal. + Name string + // Start bit. + Start uint16 + // Length in bits. + Length uint16 + // IsBigEndian is true if the signal is big-endian. + IsBigEndian bool + // IsSigned is true if the signal uses raw signed values. + IsSigned bool + // IsMultiplexer is true if the signal is the multiplexor of a multiplexed message. + IsMultiplexer bool + // IsMultiplexed is true if the signal is multiplexed. + IsMultiplexed bool + // MultiplexerValue is the value of the multiplexer when this signal is present. + MultiplexerValue uint + // Offset for real-world transform. + Offset float64 + // Scale for real-world transform. + Scale float64 + // Min real-world value. + Min float64 + // Max real-world value. + Max float64 + // Unit of the signal. + Unit string + // Description of the signal. + Description string + // ValueDescriptions of the signal. + ValueDescriptions []*ValueDescription + // ReceiverNodes is the list of names of the nodes receiving the signal. + ReceiverNodes []string + // DefaultValue of the signal. + DefaultValue int +} + +type DecodedSignal struct { + // Value is the physical value of a decoded signal + Value float64 + // Description physical description of a decoded signal + Description string + // Signal is a pointer to the dbc signal + Signal *Signal +} + +// ValueDescription returns the value description for the provided value. +func (s *Signal) ValueDescription(value int) (string, bool) { + for _, vd := range s.ValueDescriptions { + if vd.Value == value { + return vd.Description, true + } + } + return "", false +} + +// ToPhysical converts a raw signal value to its physical value. +func (s *Signal) ToPhysical(value float64) float64 { + result := value + result *= s.Scale + result += s.Offset + if s.Min != 0 || s.Max != 0 { + result = math.Max(math.Min(result, s.Max), s.Min) + } + return result +} + +// FromPhysical converts a physical signal value to its raw value. +func (s *Signal) FromPhysical(physical float64) float64 { + result := physical + if s.Min != 0 || s.Max != 0 { + result = math.Max(math.Min(result, s.Max), s.Min) + } + result -= s.Offset + result /= s.Scale + // perform saturated cast + if s.IsSigned { + result = math.Max(float64(s.MinSigned()), math.Min(float64(s.MaxSigned()), result)) + } else { + result = math.Max(0, math.Min(float64(s.MaxUnsigned()), result)) + } + return result +} + +// UnmarshalPhysical returns the physical value of the signal in the provided CAN frame. +func (s *Signal) UnmarshalPhysical(d can.Data) float64 { + switch { + case uint8(s.Length) == 1: + if d.Bit(uint8(s.Start)) { + return 1 + } + return 0 + case s.IsSigned: + var value int64 + if s.IsBigEndian { + value = d.SignedBitsBigEndian(uint8(s.Start), uint8(s.Length)) + } else { + value = d.SignedBitsLittleEndian(uint8(s.Start), uint8(s.Length)) + } + return s.ToPhysical(float64(value)) + default: + var value uint64 + if s.IsBigEndian { + value = d.UnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length)) + } else { + value = d.UnsignedBitsLittleEndian(uint8(s.Start), uint8(s.Length)) + } + return s.ToPhysical(float64(value)) + } +} + +// Decode returns the physical value of the signal in the provided CAN frame. +func (s *Signal) Decode(d can.Data) float64 { + switch { + case uint8(s.Length) == 1: + if d.Bit(uint8(s.Start)) { + return 1 + } + return 0 + case s.IsSigned: + var value int64 + if s.IsBigEndian { + value = d.SignedBitsBigEndian(uint8(s.Start), uint8(s.Length)) + } else { + value = d.SignedBitsLittleEndian(uint8(s.Start), uint8(s.Length)) + } + return s.Offset + float64(value)*s.Scale + default: + var value uint64 + if s.IsBigEndian { + value = d.UnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length)) + } else { + value = d.UnsignedBitsLittleEndian(uint8(s.Start), uint8(s.Length)) + } + return s.Offset + float64(value)*s.Scale + } +} + +// UnmarshalPhysicalPayload returns the physical value of the signal in the provided CAN frame. +func (s *Signal) UnmarshalPhysicalPayload(p *can.Payload) float64 { + switch { + case uint8(s.Length) == 1: + if p.Bit(s.Start) { + return 1 + } + return 0 + case s.IsSigned: + var value int64 + if s.IsBigEndian { + value = p.SignedBitsBigEndian(s.Start, s.Length) + } else { + value = p.SignedBitsLittleEndian(s.Start, s.Length) + } + return s.ToPhysical(float64(value)) + default: + var value uint64 + if s.IsBigEndian { + value = p.UnsignedBitsBigEndian(s.Start, s.Length) + } else { + value = p.UnsignedBitsLittleEndian(s.Start, s.Length) + } + return s.ToPhysical(float64(value)) + } +} + +// DecodePayload returns the physical value of the signal in the provided CAN frame. +func (s *Signal) DecodePayload(p *can.Payload) float64 { + switch { + case uint8(s.Length) == 1: + if p.Bit(s.Start) { + return 1 + } + return 0 + case s.IsSigned: + var value int64 + if s.IsBigEndian { + value = p.SignedBitsBigEndian(s.Start, s.Length) + } else { + value = p.SignedBitsLittleEndian(s.Start, s.Length) + } + return s.Offset + float64(value)*s.Scale + default: + var value uint64 + if s.IsBigEndian { + value = p.UnsignedBitsBigEndian(s.Start, s.Length) + } else { + value = p.UnsignedBitsLittleEndian(s.Start, s.Length) + } + return s.Offset + float64(value)*s.Scale + } +} + +// UnmarshalUnsigned returns the unsigned value of the signal in the provided CAN frame. +func (s *Signal) UnmarshalUnsigned(d can.Data) uint64 { + if s.IsBigEndian { + return d.UnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length)) + } + return d.UnsignedBitsLittleEndian(uint8(s.Start), uint8(s.Length)) +} + +// UnmarshalUnsignedPayload returns the unsigned value of the signal in the provided CAN frame. +func (s *Signal) UnmarshalUnsignedPayload(p *can.Payload) uint64 { + if s.IsBigEndian { + return p.UnsignedBitsBigEndian(s.Start, s.Length) + } + return p.UnsignedBitsLittleEndian(s.Start, s.Length) +} + +// UnmarshalValueDescription returns the value description of the signal in the provided CAN data. +func (s *Signal) UnmarshalValueDescription(d can.Data) (string, bool) { + if len(s.ValueDescriptions) == 0 { + return "", false + } + var intValue int + if s.IsSigned { + intValue = int(s.UnmarshalSigned(d)) + } else { + intValue = int(s.UnmarshalUnsigned(d)) + } + return s.ValueDescription(intValue) +} + +// UnmarshalValueDescriptionPayload returns the value description of the signal in the provided CAN data. +func (s *Signal) UnmarshalValueDescriptionPayload(p *can.Payload) (string, bool) { + if len(s.ValueDescriptions) == 0 { + return "", false + } + var intValue int + if s.IsSigned { + intValue = int(s.UnmarshalSignedPayload(p)) + } else { + intValue = int(s.UnmarshalUnsignedPayload(p)) + } + return s.ValueDescription(intValue) +} + +// UnmarshalSigned returns the signed value of the signal in the provided CAN frame. +func (s *Signal) UnmarshalSigned(d can.Data) int64 { + if s.IsBigEndian { + return d.SignedBitsBigEndian(uint8(s.Start), uint8(s.Length)) + } + return d.SignedBitsLittleEndian(uint8(s.Start), uint8(s.Length)) +} + +// UnmarshalSignedPayload returns the signed value of the signal in the provided CAN frame. +func (s *Signal) UnmarshalSignedPayload(p *can.Payload) int64 { + if s.IsBigEndian { + return p.SignedBitsBigEndian(s.Start, s.Length) + } + return p.SignedBitsLittleEndian(s.Start, s.Length) +} + +// UnmarshalBool returns the bool value of the signal in the provided CAN frame. +func (s *Signal) UnmarshalBool(d can.Data) bool { + return d.Bit(uint8(s.Start)) +} + +// MarshalUnsigned sets the unsigned value of the signal in the provided CAN frame. +func (s *Signal) MarshalUnsigned(d *can.Data, value uint64) { + if s.IsBigEndian { + d.SetUnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value) + } else { + d.SetUnsignedBitsLittleEndian(uint8(s.Start), uint8(s.Length), value) + } +} + +// MarshalSigned sets the signed value of the signal in the provided CAN frame. +func (s *Signal) MarshalSigned(d *can.Data, value int64) { + if s.IsBigEndian { + d.SetSignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value) + } else { + d.SetSignedBitsLittleEndian(uint8(s.Start), uint8(s.Length), value) + } +} + +// MarshalBool sets the bool value of the signal in the provided CAN frame. +func (s *Signal) MarshalBool(d *can.Data, value bool) { + d.SetBit(uint8(s.Start), value) +} + +// MaxUnsigned returns the maximum unsigned value representable by the signal. +func (s *Signal) MaxUnsigned() uint64 { + return (2 << (uint8(s.Length) - 1)) - 1 +} + +// MinSigned returns the minimum signed value representable by the signal. +func (s *Signal) MinSigned() int64 { + return (2 << (uint8(s.Length) - 1) / 2) * -1 +} + +// MaxSigned returns the maximum signed value representable by the signal. +func (s *Signal) MaxSigned() int64 { + return (2 << (uint8(s.Length) - 1) / 2) - 1 +} + +// SaturatedCastSigned performs a saturated cast of an int64 to the value domain of the signal. +func (s *Signal) SaturatedCastSigned(value int64) int64 { + min := s.MinSigned() + max := s.MaxSigned() + switch { + case value < min: + return min + case value > max: + return max + default: + return value + } +} + +// SaturatedCastUnsigned performs a saturated cast of a uint64 to the value domain of the signal. +func (s *Signal) SaturatedCastUnsigned(value uint64) uint64 { + max := s.MaxUnsigned() + if value > max { + return max + } + return value +} diff --git a/pkg/can-go/pkg/descriptor/signal_test.go b/pkg/can-go/pkg/descriptor/signal_test.go new file mode 100644 index 0000000..66d08cc --- /dev/null +++ b/pkg/can-go/pkg/descriptor/signal_test.go @@ -0,0 +1,294 @@ +package descriptor + +import ( + "encoding/hex" + "math" + "testing" + + can "github.com/fiskerinc/cloud-services/pkg/can-go" + "gotest.tools/v3/assert" +) + +func TestSignal_Decode_UnsignedBigEndian(t *testing.T) { + s := &Signal{ + Name: "TestSignal", + IsSigned: false, + IsBigEndian: true, + Offset: -1, + Scale: 3.0517578125e-05, + Length: 10, + Start: 32, + Min: 0, + Max: 1, + } + const value uint64 = 180 + + // Testing can.Data + var data can.Data + data.SetUnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value) + actual := s.Decode(data) + assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual) + + // Testing payload + p, _ := can.PayloadFromHex(hex.EncodeToString(data[:])) + actual = s.DecodePayload(&p) + assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual) +} + +func TestSignal_Decode_SignedBigEndian(t *testing.T) { + s := &Signal{ + Name: "TestSignal", + IsSigned: true, + IsBigEndian: true, + Offset: -1, + Scale: 3.0517578125e-05, + Length: 10, + Start: 32, + Min: -1, + Max: 1, + } + const value int64 = -180 + + // Testing can.Data + var data can.Data + data.SetSignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value) + actual := s.Decode(data) + assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual) + + // Testing payload + p, _ := can.PayloadFromHex(hex.EncodeToString(data[:])) + actual = s.DecodePayload(&p) + assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual) +} + +func TestSignal_Decode_UnsignedLittleEndian(t *testing.T) { + s := &Signal{ + Name: "TestSignal", + IsSigned: false, + IsBigEndian: false, + Offset: -1, + Scale: 3.0517578125e-05, + Length: 10, + Start: 32, + Min: 0, + Max: 1, + } + const value uint64 = 180 + + // Testing can.Data + var data can.Data + data.SetUnsignedBitsLittleEndian(uint8(s.Start), uint8(s.Length), value) + actual := s.Decode(data) + assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual) + + // Testing payload + p, _ := can.PayloadFromHex(hex.EncodeToString(data[:])) + actual = s.DecodePayload(&p) + assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual) +} + +func TestSignal_Decode_SignedLittleEndian(t *testing.T) { + s := &Signal{ + Name: "TestSignal", + IsSigned: true, + IsBigEndian: false, + Offset: -1, + Scale: 3.0517578125e-05, + Length: 10, + Start: 32, + Min: -1, + Max: 1, + } + const value int64 = -180 + + // Testing can.Data + var data can.Data + data.SetSignedBitsLittleEndian(uint8(s.Start), uint8(s.Length), value) + actual := s.Decode(data) + assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual) + + // Testing payload + p, _ := can.PayloadFromHex(hex.EncodeToString(data[:])) + actual = s.DecodePayload(&p) + assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual) +} + +func TestSignal_UnmarshalPhysical_UnsignedBigEndian(t *testing.T) { + s := &Signal{ + Name: "TestSignal", + IsSigned: false, + IsBigEndian: true, + Offset: -1, + Scale: 0.5, + Length: 10, + Start: 32, + Min: 0, + Max: 50, + } + const value uint64 = 180 + + // Testing can.Data + var data can.Data + data.SetUnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value) + actual := s.UnmarshalPhysical(data) + assert.DeepEqual(t, s.Max, actual) + + // Testing payload + p, _ := can.PayloadFromHex(hex.EncodeToString(data[:])) + actual = s.UnmarshalPhysicalPayload(&p) + assert.DeepEqual(t, s.Max, actual) +} + +func TestSignal_UnmarshalPhysical_SignedBigEndian(t *testing.T) { + s := &Signal{ + Name: "TestSignal", + IsSigned: true, + IsBigEndian: true, + Offset: -1, + Scale: 0.5, + Length: 10, + Start: 32, + Min: -50, + Max: 0, + } + const value int64 = -180 + + // Testing can.Data + var data can.Data + data.SetSignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value) + actual := s.UnmarshalPhysical(data) + assert.DeepEqual(t, s.Min, actual) + + // Testing payload + p, _ := can.PayloadFromHex(hex.EncodeToString(data[:])) + actual = s.UnmarshalPhysicalPayload(&p) + assert.DeepEqual(t, s.Min, actual) +} + +func TestSignal_UnmarshalPhysical_UnsignedLittleEndian(t *testing.T) { + s := &Signal{ + Name: "TestSignal", + IsSigned: false, + IsBigEndian: false, + Offset: -1, + Scale: 0.5, + Length: 10, + Start: 32, + Min: 0, + Max: 50, + } + const value uint64 = 180 + + // Testing can.Data + var data can.Data + data.SetUnsignedBitsLittleEndian(uint8(s.Start), uint8(s.Length), value) + actual := s.UnmarshalPhysical(data) + assert.DeepEqual(t, s.Max, actual) + + // Testing payload + p, _ := can.PayloadFromHex(hex.EncodeToString(data[:])) + actual = s.UnmarshalPhysicalPayload(&p) + assert.DeepEqual(t, s.Max, actual) +} + +func TestSignal_UnmarshalPhysical_SignedLittleEndian(t *testing.T) { + s := &Signal{ + Name: "TestSignal", + IsSigned: true, + IsBigEndian: false, + Offset: -1, + Scale: 0.5, + Length: 10, + Start: 32, + Min: -50, + Max: 0, + } + const value int64 = -180 + + // Testing can.Data + var data can.Data + data.SetSignedBitsLittleEndian(uint8(s.Start), uint8(s.Length), value) + actual := s.UnmarshalPhysical(data) + assert.DeepEqual(t, s.Min, actual) + + // Testing payload + p, _ := can.PayloadFromHex(hex.EncodeToString(data[:])) + actual = s.UnmarshalPhysicalPayload(&p) + assert.DeepEqual(t, s.Min, actual) +} + +func TestSignal_FromPhysical_SaturatedCast(t *testing.T) { + s := &Signal{ + Name: "TestSignal", + Offset: -1, + Scale: 3.0517578125e-05, + Min: -1, + Max: 1, + Length: 16, + } + // without a saturated cast, the result would be math.MaxUint16 + 1, which would wrap around to 0 + assert.Equal(t, uint16(math.MaxUint16), uint16(s.FromPhysical(180))) +} + +func TestSignal_SaturatedCastSigned(t *testing.T) { + s := &Signal{ + Name: "TestSignal", + IsSigned: true, + Length: 6, + } + assert.Equal(t, int64(31), s.SaturatedCastSigned(254)) + assert.Equal(t, int64(-32), s.SaturatedCastSigned(-255)) +} + +func TestSignal_SaturatedCastUnsigned(t *testing.T) { + s := &Signal{ + Name: "TestSignal", + Length: 6, + } + assert.Equal(t, uint64(63), s.SaturatedCastUnsigned(255)) +} + +func TestSignal_UnmarshalSigned_BigEndian(t *testing.T) { + s := &Signal{ + Name: "TestSignal", + IsSigned: true, + IsBigEndian: true, + Length: 8, + Start: 32, + } + const value int64 = -8 + var data can.Data + data.SetSignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value) + assert.Equal(t, value, s.UnmarshalSigned(data)) +} + +func TestSignal_MarshalUnsigned_BigEndian(t *testing.T) { + s := &Signal{ + Name: "TestSignal", + IsBigEndian: true, + Length: 8, + Start: 32, + } + const value uint64 = 8 + var expected can.Data + expected.SetUnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value) + var actual can.Data + s.MarshalUnsigned(&actual, value) + assert.DeepEqual(t, expected, actual) +} + +func TestSignal_MarshalSigned_BigEndian(t *testing.T) { + s := &Signal{ + Name: "TestSignal", + IsSigned: true, + IsBigEndian: true, + Length: 8, + Start: 32, + } + const value int64 = -8 + var expected can.Data + expected.SetSignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value) + var actual can.Data + s.MarshalSigned(&actual, value) + assert.DeepEqual(t, expected, actual) +} diff --git a/pkg/can-go/pkg/descriptor/valuedescription.go b/pkg/can-go/pkg/descriptor/valuedescription.go new file mode 100644 index 0000000..f874c5b --- /dev/null +++ b/pkg/can-go/pkg/descriptor/valuedescription.go @@ -0,0 +1,6 @@ +package descriptor + +type ValueDescription struct { + Value int + Description string +} diff --git a/pkg/can-go/pkg/generated/message.go b/pkg/can-go/pkg/generated/message.go new file mode 100644 index 0000000..2235f00 --- /dev/null +++ b/pkg/can-go/pkg/generated/message.go @@ -0,0 +1,26 @@ +// Package generated provides primitives for working with code-generated CAN messages. +package generated + +import ( + "fmt" + + can "github.com/fiskerinc/cloud-services/pkg/can-go" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" +) + +// Message represents a code-generated CAN message. +type Message interface { + can.Message + fmt.Stringer + + // Descriptor returns the message descriptor. + Descriptor() *descriptor.Message + + // Reset the message signals to their default values. + Reset() + + // Frame returns a CAN frame representing the message. + // + // A generated message ensures that its signals are valid and is always convertible to a CAN frame. + Frame() can.Frame +} diff --git a/pkg/can-go/pkg/socketcan/canrawaddr.go b/pkg/can-go/pkg/socketcan/canrawaddr.go new file mode 100644 index 0000000..d7b4e34 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/canrawaddr.go @@ -0,0 +1,20 @@ +package socketcan + +import "net" + +const canRawNetwork = "can" + +// canRawAddr represents a CAN_RAW address. +type canRawAddr struct { + device string +} + +var _ net.Addr = &canRawAddr{} + +func (a *canRawAddr) Network() string { + return canRawNetwork +} + +func (a *canRawAddr) String() string { + return a.device +} diff --git a/pkg/can-go/pkg/socketcan/canrawaddr_test.go b/pkg/can-go/pkg/socketcan/canrawaddr_test.go new file mode 100644 index 0000000..86a4b39 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/canrawaddr_test.go @@ -0,0 +1,17 @@ +package socketcan + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestCanRawAddr_Network(t *testing.T) { + addr := &canRawAddr{device: "can0"} + assert.Equal(t, "can0", addr.String()) +} + +func TestCanRawAddr_String(t *testing.T) { + addr := &canRawAddr{device: "can0"} + assert.Equal(t, "can", addr.Network()) +} diff --git a/pkg/can-go/pkg/socketcan/controllererror.go b/pkg/can-go/pkg/socketcan/controllererror.go new file mode 100644 index 0000000..f6b2e0a --- /dev/null +++ b/pkg/can-go/pkg/socketcan/controllererror.go @@ -0,0 +1,16 @@ +package socketcan + +type ControllerError uint8 + +//go:generate stringer -type ControllerError -trimprefix ControllerError + +const ( + ControllerErrorUnspecified ControllerError = 0x00 + ControllerErrorRxBufferOverflow ControllerError = 0x01 + ControllerErrorTxBufferOverflow ControllerError = 0x02 + ControllerErrorRxWarning ControllerError = 0x04 + ControllerErrorTxWarning ControllerError = 0x08 + ControllerErrorRxPassive ControllerError = 0x10 + ControllerErrorTxPassive ControllerError = 0x20 // at least one error counter exceeds 127 + ControllerErrorActive ControllerError = 0x40 +) diff --git a/pkg/can-go/pkg/socketcan/controllererror_string.go b/pkg/can-go/pkg/socketcan/controllererror_string.go new file mode 100644 index 0000000..cf4e4ec --- /dev/null +++ b/pkg/can-go/pkg/socketcan/controllererror_string.go @@ -0,0 +1,51 @@ +// Code generated by "stringer -type ControllerError -trimprefix ControllerError"; DO NOT EDIT. + +package socketcan + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[ControllerErrorUnspecified-0] + _ = x[ControllerErrorRxBufferOverflow-1] + _ = x[ControllerErrorTxBufferOverflow-2] + _ = x[ControllerErrorRxWarning-4] + _ = x[ControllerErrorTxWarning-8] + _ = x[ControllerErrorRxPassive-16] + _ = x[ControllerErrorTxPassive-32] + _ = x[ControllerErrorActive-64] +} + +const ( + _ControllerError_name_0 = "UnspecifiedRxBufferOverflowTxBufferOverflow" + _ControllerError_name_1 = "RxWarning" + _ControllerError_name_2 = "TxWarning" + _ControllerError_name_3 = "RxPassive" + _ControllerError_name_4 = "TxPassive" + _ControllerError_name_5 = "Active" +) + +var ( + _ControllerError_index_0 = [...]uint8{0, 11, 27, 43} +) + +func (i ControllerError) String() string { + switch { + case i <= 2: + return _ControllerError_name_0[_ControllerError_index_0[i]:_ControllerError_index_0[i+1]] + case i == 4: + return _ControllerError_name_1 + case i == 8: + return _ControllerError_name_2 + case i == 16: + return _ControllerError_name_3 + case i == 32: + return _ControllerError_name_4 + case i == 64: + return _ControllerError_name_5 + default: + return "ControllerError(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/pkg/can-go/pkg/socketcan/dial.go b/pkg/can-go/pkg/socketcan/dial.go new file mode 100644 index 0000000..e7024a2 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/dial.go @@ -0,0 +1,79 @@ +package socketcan + +import ( + "context" + "net" +) + +const udp = "udp" + +// Dial connects to the address on the named net. +// +// Linux only: If net is "can" it creates a SocketCAN connection to the device +// (address is interpreted as a device name). +// +// If net is "udp" it assumes UDP multicast and sets up 2 connections, one for +// receiving and one for transmitting. +// See: https://golang.org/pkg/net/#Dial +func Dial(network, address string) (net.Conn, error) { + switch network { + case udp: + return udpTransceiver(network, address) + case canRawNetwork: + return dialRaw(address) // platform-specific + default: + return net.Dial(network, address) + } +} + +// DialContext connects to the address on the named net using +// the provided context. +// +// Linux only: If net is "can" it creates a SocketCAN connection to the device +// (address is interpreted as a device name). +// +// See: https://golang.org/pkg/net/#Dialer.DialContext +func DialContext(ctx context.Context, network, address string) (net.Conn, error) { + switch network { + case canRawNetwork: + return dialCtx(ctx, func() (net.Conn, error) { + return dialRaw(address) + }) + case udp: + return dialCtx(ctx, func() (net.Conn, error) { + return udpTransceiver(network, address) + }) + default: + var d net.Dialer + return d.DialContext(ctx, network, address) + } +} + +func dialCtx(ctx context.Context, connProvider func() (net.Conn, error)) (net.Conn, error) { + resultChan := make(chan struct { + conn net.Conn + err error + }) + go func() { + conn, err := connProvider() + resultChan <- struct { + conn net.Conn + err error + }{conn: conn, err: err} + }() + // wait for connection or timeout + select { + case result := <-resultChan: + return result.conn, result.err + case <-ctx.Done(): + // timeout - make sure we clean up the connection + // error handling not possible since we've already returned + go func() { + result := <-resultChan + if result.conn != nil { + _ = result.conn.Close() + } + }() + return nil, ctx.Err() + } +} diff --git a/pkg/can-go/pkg/socketcan/dial_test.go b/pkg/can-go/pkg/socketcan/dial_test.go new file mode 100644 index 0000000..a301e05 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/dial_test.go @@ -0,0 +1,80 @@ +package socketcan + +import ( + "context" + "net" + "testing" + "time" + + "github.com/fiskerinc/cloud-services/pkg/can-go" + "golang.org/x/sync/errgroup" + "gotest.tools/v3/assert" +) + +func TestDial_TCP(t *testing.T) { + lis, err := net.Listen("tcp", "localhost:0") + assert.NilError(t, err) + var g errgroup.Group + g.Go(func() error { + conn, err := lis.Accept() + if err != nil { + return err + } + return conn.Close() + }) + conn, err := Dial("tcp", lis.Addr().String()) + assert.NilError(t, err) + assert.NilError(t, conn.Close()) + assert.NilError(t, g.Wait()) +} + +func TestDialContext_TCP(t *testing.T) { + lis, err := net.Listen("tcp", "localhost:0") + assert.NilError(t, err) + var g errgroup.Group + g.Go(func() error { + conn, err := lis.Accept() + if err != nil { + return err + } + return conn.Close() + }) + ctx, done := context.WithTimeout(context.Background(), time.Second) + defer done() + conn, err := DialContext(ctx, "tcp", lis.Addr().String()) + assert.NilError(t, err) + assert.NilError(t, conn.Close()) + assert.NilError(t, g.Wait()) +} + +func TestConn_TransmitReceiveTCP(t *testing.T) { + // Given: A TCP listener that writes a frame on an accepted connection + lis, err := net.Listen("tcp", "localhost:0") + assert.NilError(t, err) + var g errgroup.Group + frame := can.Frame{ID: 42, Length: 5, Data: can.Data{'H', 'e', 'l', 'l', 'o'}} + g.Go(func() error { + conn, err := lis.Accept() + if err != nil { + return err + } + tr := NewTransmitter(conn) + ctx, done := context.WithTimeout(context.Background(), time.Second) + defer done() + if err := tr.TransmitFrame(ctx, frame); err != nil { + return err + } + return conn.Close() + }) + // When: We connect to the listener + ctx, done := context.WithTimeout(context.Background(), time.Second) + defer done() + conn, err := DialContext(ctx, "tcp", lis.Addr().String()) + assert.NilError(t, err) + rec := NewReceiver(conn) + assert.Assert(t, rec.Receive()) + assert.Assert(t, !rec.HasErrorFrame()) + assert.DeepEqual(t, frame, rec.Frame()) + assert.NilError(t, conn.Close()) + assert.NilError(t, g.Wait()) +} diff --git a/pkg/can-go/pkg/socketcan/dialraw_linux.go b/pkg/can-go/pkg/socketcan/dialraw_linux.go new file mode 100644 index 0000000..939366f --- /dev/null +++ b/pkg/can-go/pkg/socketcan/dialraw_linux.go @@ -0,0 +1,36 @@ +// +build linux +// +build go1.12 + +package socketcan + +import ( + "fmt" + "net" + "os" + + "golang.org/x/sys/unix" +) + +func dialRaw(device string) (conn net.Conn, err error) { + defer func() { + if err != nil { + err = &net.OpError{Op: "dial", Net: canRawNetwork, Addr: &canRawAddr{device: device}, Err: err} + } + }() + ifi, err := net.InterfaceByName(device) + if err != nil { + return nil, fmt.Errorf("interface %s: %w", device, err) + } + fd, err := unix.Socket(unix.AF_CAN, unix.SOCK_RAW, unix.CAN_RAW) + if err != nil { + return nil, fmt.Errorf("socket: %w", err) + } + // put fd in non-blocking mode so the created file will be registered by the runtime poller (Go >= 1.12) + if err := unix.SetNonblock(fd, true); err != nil { + return nil, fmt.Errorf("set nonblock: %w", err) + } + if err := unix.Bind(fd, &unix.SockaddrCAN{Ifindex: ifi.Index}); err != nil { + return nil, fmt.Errorf("bind: %w", err) + } + return &fileConn{ra: &canRawAddr{device: device}, f: os.NewFile(uintptr(fd), "can")}, nil +} diff --git a/pkg/can-go/pkg/socketcan/dialraw_linux_test.go b/pkg/can-go/pkg/socketcan/dialraw_linux_test.go new file mode 100644 index 0000000..41d98f9 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/dialraw_linux_test.go @@ -0,0 +1,171 @@ +// +build linux +// +build go1.12 + +package socketcan + +import ( + "context" + "fmt" + "net" + "runtime" + "testing" + "time" + + "github.com/fiskerinc/cloud-services/pkg/can-go" + "golang.org/x/sync/errgroup" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestDial_CANRaw(t *testing.T) { + requireVCAN0(t) + conn, err := Dial("can", "vcan0") + assert.NilError(t, err) + assert.NilError(t, conn.Close()) +} + +func TestDialContext_CANRaw(t *testing.T) { + requireVCAN0(t) + ctx, done := context.WithTimeout(context.Background(), time.Second) + defer done() + conn, err := DialContext(ctx, "can", "vcan0") + assert.NilError(t, err) + assert.NilError(t, conn.Close()) +} + +func TestConn_DialFail(t *testing.T) { + t.Run("bad file name", func(t *testing.T) { + _, err := Dial("can", "badFileName#") + assert.ErrorContains(t, err, "dial") + }) + t.Run("timeout", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + _, err := DialContext(ctx, "can", "vcan0") + assert.ErrorContains(t, err, "context canceled") + }) +} + +func TestConn_Addr(t *testing.T) { + requireVCAN0(t) + conn, err := Dial("can", "vcan0") + assert.NilError(t, err) + assert.Assert(t, is.Nil(conn.LocalAddr())) // SocketCAN connections don't have a local connection + assert.Equal(t, "can", conn.RemoteAddr().Network()) + assert.Equal(t, "vcan0", conn.RemoteAddr().String()) +} + +func TestConn_SetDeadline(t *testing.T) { + requireVCAN0(t) + // Given that a vcan device exists and that I can open a connection to it + receiver, err := Dial("can", "vcan0") + assert.NilError(t, err) + // When I set the can + timeout := 20 * time.Millisecond + assert.NilError(t, receiver.SetDeadline(time.Now().Add(timeout))) + // Then I expect a read without a corresponding write to time out + data := make([]byte, lengthOfFrame) + n, err := receiver.Read(data) + assert.Equal(t, 0, n) + assert.Assert(t, is.ErrorContains(err, "")) + // When I clear the timeouts + assert.NilError(t, receiver.SetDeadline(time.Time{})) + // Then I don't expect the read to timeout anymore + errChan := make(chan error, 1) + go func() { + _, err = receiver.Read(data) + errChan <- err + }() + select { + case <-errChan: + t.Fatal("unexpected read result") + case <-time.After(timeout): + assert.NilError(t, receiver.Close()) + assert.Assert(t, is.ErrorContains(<-errChan, "")) + } +} + +func TestConn_ReadWrite(t *testing.T) { + requireVCAN0(t) + // given a reader and writer + reader, err := Dial("can", "vcan0") + assert.NilError(t, err) + writer, err := Dial("can", "vcan0") + assert.NilError(t, err) + // when the reader reads + var g errgroup.Group + var readFrame can.Frame + g.Go(func() error { + rec := NewReceiver(reader) + if !rec.Receive() { + return fmt.Errorf("receive") + } + readFrame = rec.Frame() + return reader.Close() + }) + // and the writer writes + writeFrame := can.Frame{ID: 32} + tr := NewTransmitter(writer) + ctx, done := context.WithTimeout(context.Background(), time.Second) + defer done() + assert.NilError(t, tr.TransmitFrame(ctx, writeFrame)) + assert.NilError(t, writer.Close()) + // then the written and read frames should be identical + assert.NilError(t, g.Wait()) + assert.DeepEqual(t, writeFrame, readFrame) +} + +func TestConn_WriteOnClosedFails(t *testing.T) { + requireVCAN0(t) + conn, err := Dial("can", "vcan0") + assert.NilError(t, err) + tr := NewTransmitter(conn) + ctx, done := context.WithTimeout(context.Background(), time.Second) + defer done() + assert.NilError(t, tr.TransmitFrame(ctx, can.Frame{})) + // When I close the connection and then write to it + assert.NilError(t, conn.Close()) + // Then it should fail + assert.Assert(t, is.ErrorContains(tr.TransmitFrame(ctx, can.Frame{}), ""), "WriteFrame on a closed Conn should fail") +} + +func TestConn_ReadOnClose(t *testing.T) { + requireVCAN0(t) + t.Run("close then read", func(t *testing.T) { + conn, err := Dial("can", "vcan0") + assert.NilError(t, err) + // When I close the connection and then read from it + assert.NilError(t, conn.Close()) + rec := NewReceiver(conn) + assert.Assert(t, !rec.Receive()) + assert.Assert(t, is.ErrorContains(rec.Err(), "")) + }) + t.Run("read then close", func(t *testing.T) { + conn, err := Dial("can", "vcan0") + assert.NilError(t, err) + // And when I read from a connection + var g errgroup.Group + var receiveErr error + g.Go(func() error { + rec := NewReceiver(conn) + if rec.Receive() { + return fmt.Errorf("receive") + } + receiveErr = rec.Err() + return nil + }) + runtime.Gosched() + // And then close it + assert.NilError(t, conn.Close()) + // Then the read operation should fail + assert.NilError(t, g.Wait()) + assert.Assert(t, is.ErrorContains(receiveErr, "")) + }) +} + +func requireVCAN0(t *testing.T) { + t.Helper() + if _, err := net.InterfaceByName("vcan0"); err != nil { + t.Skip("device vcan0 not available") + } +} diff --git a/pkg/can-go/pkg/socketcan/dialraw_others.go b/pkg/can-go/pkg/socketcan/dialraw_others.go new file mode 100644 index 0000000..0e18ce6 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/dialraw_others.go @@ -0,0 +1,13 @@ +// +build !linux !go1.12 + +package socketcan + +import ( + "fmt" + "net" + "runtime" +) + +func dialRaw(interfaceName string) (net.Conn, error) { + return nil, fmt.Errorf("SocketCAN not supported on OS %s and runtime %s", runtime.GOOS, runtime.Version()) +} diff --git a/pkg/can-go/pkg/socketcan/emulator.go b/pkg/can-go/pkg/socketcan/emulator.go new file mode 100644 index 0000000..a1b6ede --- /dev/null +++ b/pkg/can-go/pkg/socketcan/emulator.go @@ -0,0 +1,270 @@ +package socketcan + +import ( + "context" + "fmt" + "log" + "net" + "os" + "strings" + "sync" + "time" + + "github.com/fiskerinc/cloud-services/pkg/can-go" + "golang.org/x/sync/errgroup" +) + +type emulatorCfg struct { + address string + logger *log.Logger +} + +func defaultCfg() emulatorCfg { + stdLogger := log.New(os.Stderr, "emulator: ", log.Lshortfile|log.Ltime) + return emulatorCfg{ + address: "239.64.142.206:0", + logger: stdLogger, + } +} + +// EmulatorOption represents a way to configure an Emulator prior to creating it. +type EmulatorOption func(*emulatorCfg) + +// WithMulticastAddress sets the address for the multicast group that the Emulator should listen on. +// A multicast address starts with 239.x.x.x, and using an address that does not conform to this +// will lead to undefined behaviour. +func WithMulticastAddress(address string) EmulatorOption { + return func(cfg *emulatorCfg) { + cfg.address = address + } +} + +// WithLogger makes the Emulator print out status messages with the provided logger. +func WithLogger(l *log.Logger) EmulatorOption { + return func(cfg *emulatorCfg) { + cfg.logger = l + } +} + +// NoLogger disables logging in the Emulator. +func NoLogger(cfg *emulatorCfg) { + cfg.logger = log.New(&writeSink{}, "", log.LstdFlags) +} + +// writeSink is an io.Writer which does not write to anything. +// +// Can be thought of as a /dev/null for writers. +type writeSink struct{} + +// Write returns without actually writing to anything. +func (w *writeSink) Write(buf []byte) (int, error) { + return len(buf), nil +} + +// Emulator emulates a CAN bus. +// +// Emulator emulates a CAN bus by using UDP multicast. The emulator itself +// does not own the multicast group but rather establishes a common +// address/port pair for the CAN bus to be emulated on. +// Emulator exposes a thread-safe API to callees and may therefore be +// shared among different goroutines. +type Emulator struct { + transceiver *udpTxRx + logger *log.Logger + reqSenderChan chan chan int + closeChan chan struct{} + sync.Mutex + g *errgroup.Group + rg errgroup.Group +} + +// NewEmulator creates an Emulator to emulate a CAN bus. +// +// If no error is returned it is safe to `socketcan.Dial` the address +// of the Emulator. The emulator will default to using multicast group `239.64.142.206` +// with a random port that's decided when calling Emulator +// +// N.B. It is not possible to simply use `net.Dial` as for UDP multicast both +// a transmitting connection and a writing connection. This is handled +// by `socketcan.Dial` under the hood. +func NewEmulator(options ...EmulatorOption) (*Emulator, error) { + cfg := defaultCfg() + for _, update := range options { + update(&cfg) + } + c, err := udpTransceiver("udp", cfg.address) + if err != nil { + return nil, err + } + return &Emulator{ + transceiver: c, + logger: cfg.logger, + closeChan: make(chan struct{}), + reqSenderChan: make(chan chan int), + }, nil +} + +// Run an Emulator. +// +// This starts the listener and waits until the context is canceled +// before tidying up. +func (e *Emulator) Run(ctx context.Context) error { + e.Lock() + e.g, ctx = errgroup.WithContext(ctx) + ctxDone := ctx.Done() + + // Listen for incoming frames. + // Keep track of unique senders, and notify newSenderChan. + newSenderChan := make(chan struct{}) + e.g.Go(func() error { + e.logger.Printf("waiting for SocketCAN connection requests on udp://%s\n", e.Addr().String()) + registeredSenders := make(map[string]bool) + for { + buffer := make([]byte, 8096) + _, _, src, err := e.transceiver.rx.ReadFrom(buffer) + if err != nil { + if isClosedError(err) { + return nil + } + return fmt.Errorf("read from udp: %w", err) + } + if !registeredSenders[src.String()] { + e.logger.Printf("received first frame from %s", src.String()) + registeredSenders[src.String()] = true + select { + case <-ctxDone: + return nil + case newSenderChan <- struct{}{}: + } + } + } + }) + + // Close multicast listener when ctx is canceled + e.g.Go(func() error { + <-ctxDone + e.logger.Println("closing SocketCAN listener...") + e.Lock() + defer e.Unlock() + return e.transceiver.Close() + }) + + // Stop all started receivers when ctx is canceled + e.g.Go(func() error { + <-ctxDone + e.logger.Println("stopping receivers...") + close(e.closeChan) + e.Lock() + defer e.Unlock() + return e.rg.Wait() + }) + + // Keep track of the number of unique senders of the received frames, when the number of senders + // are requested on the reqSenderChan, send them on the provided channel. + e.g.Go(func() error { + nSenders := 0 + for { + select { + case <-ctxDone: + return nil + case <-newSenderChan: + nSenders++ + case req := <-e.reqSenderChan: + req <- nSenders + } + } + }) + e.Unlock() + e.logger.Println("started emulator, waiting for cancel signal") + return e.g.Wait() +} + +// Addr returns the address of the Emulator's multicast group. +func (e *Emulator) Addr() net.Addr { + return e.transceiver.RemoteAddr() +} + +// Receiver returns a Receiver connected to the Emulator. +// +// The emulator owns the underlying network connection an +// will close it when the emulator is closed. +func (e *Emulator) Receiver() (*Receiver, error) { + conn, err := udpTransceiver(e.Addr().Network(), e.Addr().String()) + if err != nil { + return nil, err + } + e.Lock() + e.rg.Go(func() error { + <-e.closeChan + return conn.Close() + }) + e.Unlock() + return NewReceiver(conn), nil +} + +// TransmitFrame sends a CAN frame to the Emulator's multicast group. +func (e *Emulator) TransmitFrame(ctx context.Context, f can.Frame) error { + conn, err := udpTransceiver(e.Addr().Network(), e.Addr().String()) + if err != nil { + return fmt.Errorf("transmit CAN frame: %w", err) + } + errChan := make(chan error) + go func() { + if err := NewTransmitter(conn).TransmitFrame(ctx, f); err != nil { + errChan <- fmt.Errorf("transmit CAN frame: %w", err) + } + close(errChan) + }() + select { + case <-ctx.Done(): + _ = conn.Close() + return ctx.Err() + case err := <-errChan: + _ = conn.Close() + if err != nil { + return fmt.Errorf("emulator: %w", err) + } + return nil + } +} + +// TransmitMessage sends a CAN message to every emulator connection. +func (e *Emulator) TransmitMessage(ctx context.Context, m can.Message) error { + f, err := m.MarshalFrame() + if err != nil { + return fmt.Errorf("transmit CAN message: %w", err) + } + if err := e.TransmitFrame(ctx, f); err != nil { + return fmt.Errorf("transmit CAN message: %w", err) + } + return nil +} + +// WaitForSenders waits until either, n unique senders have been sending messages to the +// multicast group, or the timeout is reached. +func (e *Emulator) WaitForSenders(n int, timeout time.Duration) error { + reqChan := make(chan int) + timeoutChannel := time.After(timeout) + for { + select { + case <-timeoutChannel: + return fmt.Errorf("emulator timeout waiting for senders") + case e.reqSenderChan <- reqChan: + conns := <-reqChan + if conns < n { + // We don't want to keep the emulator + // busy with our requests all the time. + time.Sleep(time.Millisecond) + continue + } + return nil + } + } +} + +func isClosedError(e error) bool { + if e == nil { + return false + } + return strings.Contains(e.Error(), "closed") +} diff --git a/pkg/can-go/pkg/socketcan/emulator_test.go b/pkg/can-go/pkg/socketcan/emulator_test.go new file mode 100644 index 0000000..c98df7a --- /dev/null +++ b/pkg/can-go/pkg/socketcan/emulator_test.go @@ -0,0 +1,357 @@ +package socketcan + +import ( + "context" + "fmt" + "log" + "testing" + "time" + + "github.com/fiskerinc/cloud-services/pkg/can-go" + "golang.org/x/sync/errgroup" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestEmulate_Close(t *testing.T) { + // Given: an emulator + e, err := NewEmulator() + assert.NilError(t, err) + ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) + defer cancel() + // When: I start the emulator + g, ctx := errgroup.WithContext(ctx) + g.Go(func() error { + return e.Run(ctx) + }) + // Then: I should be able to close it + assert.NilError(t, g.Wait()) +} + +func TestEmulate_SendToAll(t *testing.T) { + for _, tt := range []struct { + receivers int + }{ + {receivers: 1}, + {receivers: 5}, + {receivers: 100}, + } { + tt := tt + t.Run(fmt.Sprintf("receivers:%v", tt.receivers), func(t *testing.T) { + // Given: A listener with an Emulator + ctx, cancel := context.WithCancel(context.Background()) + eg, eCtx := errgroup.WithContext(ctx) + e, err := NewEmulator() + assert.NilError(t, err) + eg.Go(func() error { + return e.Run(eCtx) + }) + // When: I start multiple receivers connected to the Emulator + g := errgroup.Group{} + for i := 0; i < tt.receivers; i++ { + r, err := e.Receiver() + assert.NilError(t, err) + g.Go(func() error { + if ok := r.Receive(); !ok { + return fmt.Errorf("failed to receive CAN frame: %w", r.Err()) + } + if r.HasErrorFrame() { + return fmt.Errorf("received error frame: %v", r.ErrorFrame()) + } + return r.Err() + }) + } + // And then the emulator transmits a CAN frame + txFrame := can.Frame{ID: 42, Length: 4, Data: can.Data{1, 2, 3, 4}} + err = e.TransmitFrame(context.Background(), txFrame) + assert.NilError(t, err) + // Then: Every receiver should receive the frame and not return an error + assert.NilError(t, g.Wait()) + cancel() + assert.NilError(t, eg.Wait()) + }) + } +} + +func TestEmulate_ConnectMany(t *testing.T) { + // TODO: Fix raciness or remove this test. + t.Skip("racy") + for _, tt := range []struct { + noTransmitters int + canFrames []can.Frame + }{ + { + noTransmitters: 1, + canFrames: []can.Frame{ + {ID: 42}, + {ID: 43, Length: 4, Data: can.Data{1, 2, 3, 4}}, + }, + }, + { + noTransmitters: 10, + canFrames: []can.Frame{ + {ID: 42}, + {ID: 43, Length: 4, Data: can.Data{1, 2, 3, 4}}, + {ID: 44, IsRemote: true}, + }, + }, + { + noTransmitters: 50, + canFrames: []can.Frame{ + {ID: 42}, + {ID: 43, Length: 4, Data: can.Data{1, 2, 3, 4}}, + {ID: 44, IsRemote: true}, + {ID: 45, Length: 7, Data: can.Data{1, 2, 3, 4, 5, 6, 7}}, + {ID: 46, IsExtended: false}, + {ID: 47, Length: 1, Data: can.Data{1}}, + {ID: 48, IsRemote: false}, + }, + }, + } { + tt := tt + name := fmt.Sprintf("transmitters:%v,frames:%v", tt.noTransmitters, len(tt.canFrames)) + t.Run(name, func(t *testing.T) { + // Given: A listener with an Emulator + e, err := NewEmulator(NoLogger) + assert.NilError(t, err) + ctx, cancel := context.WithCancel(context.Background()) + eg, eCtx := errgroup.WithContext(ctx) + eg.Go(func() error { + return e.Run(eCtx) + }) + r, err := e.Receiver() + assert.NilError(t, err) + receiver := errgroup.Group{} + receiver.Go(func() error { + for i := 0; i < len(tt.canFrames)*tt.noTransmitters; i++ { + i := i + if ok := r.Receive(); !ok { + cancel() + assert.NilError(t, eg.Wait()) + t.Fatal("Not all CAN frames were received", i, r.Err()) + } + assert.Assert(t, is.Contains(tt.canFrames, r.Frame())) + } + return nil + }) + // When: I connect multiple transmitters and transmit CAN frame on every transmitter + transmits, txCtx := errgroup.WithContext(ctx) + for i := 0; i < tt.noTransmitters; i++ { + transmits.Go(func() error { + conn, err := DialContext(txCtx, e.Addr().Network(), e.Addr().String()) + if err != nil { + return err + } + tx := NewTransmitter(conn) + for _, frame := range tt.canFrames { + if err := tx.TransmitFrame(txCtx, frame); err != nil { + log.Printf("failed to transmit frame: %+v\n", err) + return err + } + } + return conn.Close() + }) + } + assert.NilError(t, transmits.Wait()) + // Then: Every CAN frame should have been delivered to the emulator + assert.NilError(t, receiver.Wait()) + cancel() + assert.NilError(t, eg.Wait()) + }) + } +} + +func TestEmulate_SendReceive(t *testing.T) { + for _, tt := range []struct { + transmitters int + receivers int + }{ + { + transmitters: 1, + receivers: 2, + }, + { + transmitters: 10, + receivers: 50, + }, + { + transmitters: 50, + receivers: 50, + }, + } { + tt := tt + name := fmt.Sprintf("transmitters: %v,receivers: %v", tt.transmitters, tt.receivers) + t.Run(name, func(t *testing.T) { + // Given: A listener and an emulator + e, err := NewEmulator() + assert.NilError(t, err) + ctx, cancel := context.WithCancel(context.Background()) + eg, eCtx := errgroup.WithContext(ctx) + eg.Go(func() error { + return e.Run(eCtx) + }) + canFrames := make(map[uint32]can.Frame) + canFrames[42] = can.Frame{ID: 42} + canFrames[43] = can.Frame{ID: 43, IsRemote: true} + canFrames[44] = can.Frame{ID: 44, IsExtended: true} + // When: I start a number of receivers + rx := errgroup.Group{} + for i := 0; i < tt.receivers; i++ { + r, err := e.Receiver() + assert.NilError(t, err) + rx.Go(func() error { + for i := 0; i < tt.transmitters*len(canFrames); i++ { + if ok := r.Receive(); !ok { + return fmt.Errorf("receive frames: %w", r.Err()) + } + if r.HasErrorFrame() { + return fmt.Errorf("received error frame: %v", r.ErrorFrame()) + } + if _, ok := canFrames[r.Frame().ID]; !ok { + return fmt.Errorf("received unexpected frame: %v", r.Frame()) + } + } + return nil + }) + } + // And then start a number of transmitters that will transmit a number of CAN frames + tx, txCtx := errgroup.WithContext(ctx) + for i := 0; i < tt.transmitters; i++ { + conn, err := DialContext(txCtx, e.Addr().Network(), e.Addr().String()) + assert.NilError(t, err) + tx.Go(func() (err error) { + t := NewTransmitter(conn) + for _, f := range canFrames { + if err := t.TransmitFrame(txCtx, f); err != nil { + return fmt.Errorf("transmit frame: %w", err) + } + } + if err := conn.Close(); err != nil { + return fmt.Errorf("close transmitter: %w", err) + } + return nil + }) + } + // Then: The transmissions should not fail + assert.NilError(t, tx.Wait()) + // And every receiver should receive every CAN frame + assert.NilError(t, rx.Wait()) + cancel() + assert.NilError(t, eg.Wait()) + }) + } +} + +func TestEmulator_Isolation(t *testing.T) { + // Given 5 separate emulators + const nEmulators = 5 + var emulators []*Emulator + ctx, cancel := context.WithCancel(context.Background()) + eg, eCtx := errgroup.WithContext(ctx) + for i := 0; i < nEmulators; i++ { + e, err := NewEmulator() + assert.NilError(t, err) + emulators = append(emulators, e) + eg.Go(func() error { + return e.Run(eCtx) + }) + } + // When starting one transmitter/receiver pair per emulator sending 10 frames + const nFrames = 10 + rx := errgroup.Group{} + tx := errgroup.Group{} + for i := 0; i < nEmulators; i++ { + i := i + r, err := emulators[i].Receiver() + assert.NilError(t, err) + rx.Go(func() error { + for j := 0; j < nFrames; j++ { + if ok := r.Receive(); !ok { + return fmt.Errorf("receive frame: %w", r.Err()) + } + if r.HasErrorFrame() { + return fmt.Errorf("received error frame: %v", r.ErrorFrame()) + } + if r.Frame().ID != uint32(i) { + return fmt.Errorf("receiver(%v) received unexpected frame: %v", i, r.Frame()) + } + } + return nil + }) + for j := 0; j < nFrames; j++ { + frame := can.Frame{ID: uint32(i)} + tx.Go(func() error { + return emulators[i].TransmitFrame(context.Background(), frame) + }) + } + } + // Then all transmitted frames should be received by correct receiver + assert.NilError(t, rx.Wait()) + assert.NilError(t, tx.Wait()) + cancel() + assert.NilError(t, eg.Wait()) +} + +func TestEmulator_WaitForSenders(t *testing.T) { + // Given a started emulator + ctx, cancel := context.WithCancel(context.Background()) + eg, eCtx := errgroup.WithContext(ctx) + e, err := NewEmulator() + assert.NilError(t, err) + eg.Go(func() error { + return e.Run(eCtx) + }) + // When one transmitter is transmitting a frame + txg := errgroup.Group{} + txg.Go(func() error { + return e.TransmitFrame(context.Background(), can.Frame{ID: 1234}) + }) + // Then WaitForSenders should return without an error + err = e.WaitForSenders(1, time.Second) + assert.NilError(t, err) + assert.NilError(t, txg.Wait()) + cancel() + assert.NilError(t, eg.Wait()) +} + +func TestEmulator_WaitForSenders_Multiple(t *testing.T) { + // Given a started emulator + ctx, cancel := context.WithCancel(context.Background()) + eg, eCtx := errgroup.WithContext(ctx) + e, err := NewEmulator() + assert.NilError(t, err) + eg.Go(func() error { + return e.Run(eCtx) + }) + // When one transmitter is transmitting a frame + txg := errgroup.Group{} + txg.Go(func() error { + return e.TransmitFrame(context.Background(), can.Frame{ID: 1234}) + }) + txg.Go(func() error { + return e.TransmitFrame(context.Background(), can.Frame{ID: 4321}) + }) + // Then WaitForSenders should return without an error + err = e.WaitForSenders(2, time.Second) + assert.NilError(t, err) + assert.NilError(t, txg.Wait()) + cancel() + assert.NilError(t, eg.Wait()) +} + +func TestEmulator_WaitForSenders_Timeout(t *testing.T) { + // Given a started emulator + ctx, cancel := context.WithCancel(context.Background()) + eg, eCtx := errgroup.WithContext(ctx) + e, err := NewEmulator() + assert.NilError(t, err) + eg.Go(func() error { + return e.Run(eCtx) + }) + // When no transmitters have connected and transmitted frames + // Then WaitForSenders should timeout + err = e.WaitForSenders(1, 100*time.Millisecond) + assert.ErrorContains(t, err, "timeout") + cancel() + assert.NilError(t, eg.Wait()) +} diff --git a/pkg/can-go/pkg/socketcan/errorclass.go b/pkg/can-go/pkg/socketcan/errorclass.go new file mode 100644 index 0000000..120450f --- /dev/null +++ b/pkg/can-go/pkg/socketcan/errorclass.go @@ -0,0 +1,17 @@ +package socketcan + +type ErrorClass uint32 + +//go:generate stringer -type ErrorClass -trimprefix ErrorClass + +const ( + ErrorClassTxTimeout ErrorClass = 0x00000001 + ErrorClassLostArbitration ErrorClass = 0x00000002 + ErrorClassController ErrorClass = 0x00000004 + ErrorClassProtocolViolation ErrorClass = 0x00000008 + ErrorClassTransceiver ErrorClass = 0x00000010 + ErrorClassNoAck ErrorClass = 0x00000020 + ErrorClassBusOff ErrorClass = 0x00000040 + ErrorClassBusError ErrorClass = 0x00000080 + ErrorClassRestarted ErrorClass = 0x00000100 +) diff --git a/pkg/can-go/pkg/socketcan/errorclass_string.go b/pkg/can-go/pkg/socketcan/errorclass_string.go new file mode 100644 index 0000000..98d7621 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/errorclass_string.go @@ -0,0 +1,59 @@ +// Code generated by "stringer -type ErrorClass -trimprefix ErrorClass"; DO NOT EDIT. + +package socketcan + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[ErrorClassTxTimeout-1] + _ = x[ErrorClassLostArbitration-2] + _ = x[ErrorClassController-4] + _ = x[ErrorClassProtocolViolation-8] + _ = x[ErrorClassTransceiver-16] + _ = x[ErrorClassNoAck-32] + _ = x[ErrorClassBusOff-64] + _ = x[ErrorClassBusError-128] + _ = x[ErrorClassRestarted-256] +} + +const ( + _ErrorClass_name_0 = "TxTimeoutLostArbitration" + _ErrorClass_name_1 = "Controller" + _ErrorClass_name_2 = "ProtocolViolation" + _ErrorClass_name_3 = "Transceiver" + _ErrorClass_name_4 = "NoAck" + _ErrorClass_name_5 = "BusOff" + _ErrorClass_name_6 = "BusError" + _ErrorClass_name_7 = "Restarted" +) + +var ( + _ErrorClass_index_0 = [...]uint8{0, 9, 24} +) + +func (i ErrorClass) String() string { + switch { + case 1 <= i && i <= 2: + i -= 1 + return _ErrorClass_name_0[_ErrorClass_index_0[i]:_ErrorClass_index_0[i+1]] + case i == 4: + return _ErrorClass_name_1 + case i == 8: + return _ErrorClass_name_2 + case i == 16: + return _ErrorClass_name_3 + case i == 32: + return _ErrorClass_name_4 + case i == 64: + return _ErrorClass_name_5 + case i == 128: + return _ErrorClass_name_6 + case i == 256: + return _ErrorClass_name_7 + default: + return "ErrorClass(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/pkg/can-go/pkg/socketcan/errorframe.go b/pkg/can-go/pkg/socketcan/errorframe.go new file mode 100644 index 0000000..11114d0 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/errorframe.go @@ -0,0 +1,63 @@ +package socketcan + +import ( + "encoding/hex" + "fmt" +) + +type ErrorFrame struct { + // Class is the error class + ErrorClass ErrorClass + // LostArbitrationBit contains the bit number when the error class is LostArbitration. + LostArbitrationBit uint8 + // ControllerError contains error information when the error class is Controller. + ControllerError ControllerError + // ProtocolViolationError contains error information when the error class is Protocol. + ProtocolError ProtocolViolationError + // ProtocolViolationErrorLocation contains error location when the error class is Protocol. + ProtocolViolationErrorLocation ProtocolViolationErrorLocation + // TransceiverError contains error information when the error class is Transceiver. + TransceiverError TransceiverError + // ControllerSpecificInformation contains controller-specific additional error information. + ControllerSpecificInformation [3]byte +} + +func (e *ErrorFrame) String() string { + switch e.ErrorClass { + case ErrorClassLostArbitration: + return fmt.Sprintf( + "%s in bit %d (%s)", + e.ErrorClass, + e.LostArbitrationBit, + hex.EncodeToString(e.ControllerSpecificInformation[:]), + ) + case ErrorClassController: + return fmt.Sprintf( + "%s: %s (%v)", + e.ErrorClass, + e.ControllerError, + hex.EncodeToString(e.ControllerSpecificInformation[:]), + ) + case ErrorClassProtocolViolation: + return fmt.Sprintf( + "%s: %s: location %s (%v)", + e.ErrorClass, + e.ProtocolError, + e.ProtocolViolationErrorLocation, + hex.EncodeToString(e.ControllerSpecificInformation[:]), + ) + case ErrorClassTransceiver: + return fmt.Sprintf( + "%s: %s (%v)", + e.ErrorClass, + e.TransceiverError, + hex.EncodeToString(e.ControllerSpecificInformation[:]), + ) + default: + return fmt.Sprintf( + "%s (%v)", + e.ErrorClass, + hex.EncodeToString(e.ControllerSpecificInformation[:]), + ) + } +} diff --git a/pkg/can-go/pkg/socketcan/errorframe_test.go b/pkg/can-go/pkg/socketcan/errorframe_test.go new file mode 100644 index 0000000..1bbb0a1 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/errorframe_test.go @@ -0,0 +1,62 @@ +package socketcan + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestErrorFrame_String(t *testing.T) { + for _, tt := range []struct { + msg string + f ErrorFrame + expected string + }{ + { + msg: "lost arbitration", + f: ErrorFrame{ + ErrorClass: ErrorClassLostArbitration, + LostArbitrationBit: 42, + }, + expected: "LostArbitration in bit 42 (000000)", + }, + { + msg: "controller", + f: ErrorFrame{ + ErrorClass: ErrorClassController, + ControllerError: ControllerErrorRxBufferOverflow, + }, + expected: "Controller: RxBufferOverflow (000000)", + }, + { + msg: "protocol violation", + f: ErrorFrame{ + ErrorClass: ErrorClassProtocolViolation, + ProtocolError: ProtocolViolationErrorFrameFormat, + ProtocolViolationErrorLocation: ProtocolViolationErrorLocationID20To18, + }, + expected: "ProtocolViolation: FrameFormat: location ID20To18 (000000)", + }, + { + msg: "transceiver", + f: ErrorFrame{ + ErrorClass: ErrorClassTransceiver, + TransceiverError: TransceiverErrorCANHShortToGND, + }, + expected: "Transceiver: CANHShortToGND (000000)", + }, + { + msg: "controller specific information", + f: ErrorFrame{ + ErrorClass: ErrorClassTxTimeout, + ControllerSpecificInformation: [3]byte{0x12, 0x34, 0x56}, + }, + expected: "TxTimeout (123456)", + }, + } { + tt := tt + t.Run(tt.msg, func(t *testing.T) { + assert.Equal(t, tt.expected, tt.f.String()) + }) + } +} diff --git a/pkg/can-go/pkg/socketcan/fileconn.go b/pkg/can-go/pkg/socketcan/fileconn.go new file mode 100644 index 0000000..526f320 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/fileconn.go @@ -0,0 +1,93 @@ +package socketcan + +import ( + "errors" + "net" + "os" + "time" +) + +// file is an interface for mocking file operations performed by fileConn. +type file interface { + Read([]byte) (int, error) + Write([]byte) (int, error) + SetDeadline(time.Time) error + SetReadDeadline(time.Time) error + SetWriteDeadline(time.Time) error + Close() error +} + +// fileConn provides a net.Conn API for file-like types. +type fileConn struct { + // f is the file to provide a net.Conn API for. + f file + // net is the connection's network. + net string + // la is the connection's local address, if any. + la net.Addr + // ra is the connection's remote address, if any. + ra net.Addr +} + +var _ net.Conn = &fileConn{} + +func (c *fileConn) Read(b []byte) (int, error) { + n, err := c.f.Read(b) + if err != nil { + return n, &net.OpError{Op: "read", Net: c.net, Source: c.la, Addr: c.ra, Err: unwrapPathError(err)} + } + return n, nil +} + +func (c *fileConn) Write(b []byte) (int, error) { + n, err := c.f.Write(b) + if err != nil { + return n, &net.OpError{Op: "write", Net: c.net, Source: c.la, Addr: c.ra, Err: unwrapPathError(err)} + } + return n, nil +} + +func (c *fileConn) LocalAddr() net.Addr { + return c.la +} + +func (c *fileConn) RemoteAddr() net.Addr { + return c.ra +} + +func (c *fileConn) SetDeadline(t time.Time) error { + if err := c.f.SetDeadline(t); err != nil { + return &net.OpError{Op: "set deadline", Net: c.net, Source: c.la, Addr: c.ra, Err: unwrapPathError(err)} + } + return nil +} + +func (c *fileConn) SetReadDeadline(t time.Time) error { + if err := c.f.SetReadDeadline(t); err != nil { + return &net.OpError{Op: "set read deadline", Net: c.net, Source: c.la, Addr: c.ra, Err: unwrapPathError(err)} + } + return nil +} + +func (c *fileConn) SetWriteDeadline(t time.Time) error { + if err := c.f.SetWriteDeadline(t); err != nil { + return &net.OpError{Op: "set write deadline", Net: c.net, Source: c.la, Addr: c.ra, Err: unwrapPathError(err)} + } + return nil +} + +func (c *fileConn) Close() error { + if err := c.f.Close(); err != nil { + return &net.OpError{Op: "close", Net: c.net, Source: c.la, Addr: c.ra, Err: unwrapPathError(err)} + } + return nil +} + +// unwrapPathError unwraps one level of *os.PathError from the provided error. +func unwrapPathError(err error) error { + var pe *os.PathError + if errors.As(err, &pe) { + return pe.Err + } + return err +} diff --git a/pkg/can-go/pkg/socketcan/fileconn_test.go b/pkg/can-go/pkg/socketcan/fileconn_test.go new file mode 100644 index 0000000..decff76 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/fileconn_test.go @@ -0,0 +1,154 @@ +package socketcan + +import ( + "fmt" + "net" + "os" + "testing" + "time" + + "github.com/fiskerinc/cloud-services/pkg/can-go/internal/gen/mock/mocksocketcan" + "github.com/golang/mock/gomock" + "gotest.tools/v3/assert" +) + +func TestUnwrapPathError(t *testing.T) { + innerErr := fmt.Errorf("inner error") + for _, tt := range []struct { + msg string + err error + expected error + }{ + { + msg: "no path error", + err: innerErr, + expected: innerErr, + }, + { + msg: "single path error", + err: &os.PathError{Op: "read", Err: innerErr}, + expected: innerErr, + }, + { + msg: "double path error", + err: &os.PathError{Op: "read", Err: &os.PathError{Op: "read", Err: innerErr}}, + expected: &os.PathError{Op: "read", Err: innerErr}, + }, + } { + tt := tt + t.Run(tt.msg, func(t *testing.T) { + assert.Error(t, unwrapPathError(tt.err), tt.expected.Error()) + }) + } +} + +func TestFileConn_ReadWrite(t *testing.T) { + for _, tt := range []struct { + op string + fn func(file, []byte) (int, error) + mockFn func(*mocksocketcan.MockfileMockRecorder, interface{}) *gomock.Call + }{ + { + op: "read", + fn: file.Read, + mockFn: (*mocksocketcan.MockfileMockRecorder).Read, + }, + { + op: "write", + fn: file.Write, + mockFn: (*mocksocketcan.MockfileMockRecorder).Write, + }, + } { + tt := tt + t.Run(tt.op, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + f := mocksocketcan.NewMockfile(ctrl) + fc := &fileConn{f: f, net: "can", ra: &canRawAddr{device: "can0"}} + t.Run("no error", func(t *testing.T) { + var data []byte + tt.mockFn(f.EXPECT(), data).Return(42, nil) + n, err := tt.fn(fc, data) + assert.Equal(t, 42, n) + assert.NilError(t, err) + }) + t.Run("error", func(t *testing.T) { + var data []byte + cause := fmt.Errorf("boom") + tt.mockFn(f.EXPECT(), data).Return(0, &os.PathError{Err: cause}) + n, err := tt.fn(fc, data) + assert.Equal(t, 0, n) + assert.ErrorContains(t, &net.OpError{Op: tt.op, Net: fc.net, Addr: fc.RemoteAddr(), Err: err}, "boom") + }) + }) + } +} + +func TestFileConn_Addr(t *testing.T) { + fc := &fileConn{la: &canRawAddr{device: "can0"}, ra: &canRawAddr{device: "can1"}} + t.Run("local", func(t *testing.T) { + assert.Equal(t, fc.la, fc.LocalAddr()) + }) + t.Run("remote", func(t *testing.T) { + assert.Equal(t, fc.ra, fc.RemoteAddr()) + }) +} + +func TestFileConn_SetDeadlines(t *testing.T) { + for _, tt := range []struct { + op string + fn func(file, time.Time) error + mockFn func(*mocksocketcan.MockfileMockRecorder, interface{}) *gomock.Call + }{ + { + op: "set deadline", + fn: file.SetDeadline, + mockFn: (*mocksocketcan.MockfileMockRecorder).SetDeadline, + }, + { + op: "set read deadline", + fn: file.SetReadDeadline, + mockFn: (*mocksocketcan.MockfileMockRecorder).SetReadDeadline, + }, + { + op: "set write deadline", + fn: file.SetWriteDeadline, + mockFn: (*mocksocketcan.MockfileMockRecorder).SetWriteDeadline, + }, + } { + tt := tt + t.Run(tt.op, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + f := mocksocketcan.NewMockfile(ctrl) + fc := &fileConn{f: f, net: "can", ra: &canRawAddr{device: "can0"}} + t.Run("no error", func(t *testing.T) { + tt.mockFn(f.EXPECT(), time.Unix(0, 1)).Return(nil) + assert.NilError(t, tt.fn(fc, time.Unix(0, 1))) + }) + t.Run("error", func(t *testing.T) { + cause := fmt.Errorf("boom") + tt.mockFn(f.EXPECT(), time.Unix(0, 1)).Return(&os.PathError{Err: cause}) + err := tt.fn(fc, time.Unix(0, 1)) + assert.Error(t, err, (&net.OpError{Op: tt.op, Net: fc.net, Addr: fc.RemoteAddr(), Err: cause}).Error()) + }) + }) + } +} + +func TestFileConn_Close(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + f := mocksocketcan.NewMockfile(ctrl) + fc := &fileConn{f: f, net: "can", ra: &canRawAddr{device: "can0"}} + t.Run("no error", func(t *testing.T) { + f.EXPECT().Close().Return(nil) + assert.NilError(t, fc.Close()) + }) + t.Run("error", func(t *testing.T) { + cause := fmt.Errorf("boom") + f.EXPECT().Close().Return(&os.PathError{Err: cause}) + err := fc.Close() + assert.Error(t, err, (&net.OpError{Op: "close", Net: fc.net, Addr: fc.RemoteAddr(), Err: cause}).Error()) + }) +} diff --git a/pkg/can-go/pkg/socketcan/frame.go b/pkg/can-go/pkg/socketcan/frame.go new file mode 100644 index 0000000..e8b051c --- /dev/null +++ b/pkg/can-go/pkg/socketcan/frame.go @@ -0,0 +1,183 @@ +package socketcan + +import ( + "encoding/binary" + + "github.com/fiskerinc/cloud-services/pkg/can-go" +) + +const ( + // lengthOfFrame is the length of a SocketCAN frame in bytes. + lengthOfFrame = 16 + // maxLengthOfData is the max length of a SocketCAN frame payload in bytes. + maxLengthOfData = 8 + // indexOfID is the index of the first byte of the frame ID. + indexOfID = 0 + // lengthOfID is the length of a frame ID in bytes. + lengthOfID = 4 + // indexOfDataLengthCode is the index of the first byte of the frame dataLengthCode. + indexOfDataLengthCode = indexOfID + lengthOfID + // lengthOfDataLengthCode is the length of a frame dataLengthCode in bytes. + lengthOfDataLengthCode = 1 + // indexOfPadding is the index of the first byte of frame padding. + indexOfPadding = indexOfDataLengthCode + lengthOfDataLengthCode + // lengthOfPadding is the length of frame padding in bytes. + lengthOfPadding = 3 + // indexOfData is the index of the first byte of data in a frame. + indexOfData = indexOfPadding + lengthOfPadding +) + +// error frame flag indices. +const ( + // indexOfLostArbitrationBit is the byte index of the lost arbitration bit in an error frame. + indexOfLostArbitrationBit = 0 + // indexOfControllerError is the byte index of the controller error in an error frame. + indexOfControllerError = 1 + // indexOfProtocolError is the byte index of the protocol error in an error frame. + indexOfProtocolError = 2 + // indexOfProtocolViolationErrorLocation is the byte index of the protocol error location in an error frame. + indexOfProtocolViolationErrorLocation = 3 + // indexOfTransceiverError is the byte index of the transceiver error in an error frame. + indexOfTransceiverError = 4 + // indexOfControllerSpecificInformation is the starting byte of controller specific information in an error frame. + indexOfControllerSpecificInformation = 5 + // LengthOfControllerSpecificInformation is the number of error frame bytes with controller-specific information. + LengthOfControllerSpecificInformation = 3 +) + +var _ [lengthOfFrame]struct{} = [indexOfData + maxLengthOfData]struct{}{} + +// id flags (copied from x/sys/unix). +const ( + idFlagExtended = 0x80000000 + idFlagError = 0x20000000 + idFlagRemote = 0x40000000 + idMaskExtended = 0x1fffffff + idMaskStandard = 0x7ff +) + +// FrameInterceptor provides a hook to intercept the transmission of a CAN frame. +// The interceptor is called if and only if the frame transmission/receival is a success. +type FrameInterceptor func(fr can.Frame) + +// frame represents a SocketCAN frame. +// +// The format specified in the Linux SocketCAN kernel module: +// +// struct can_frame { +// canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ +// __u8 can_dlc; /* frame payload length in byte (0 .. 8) */ +// __u8 __pad; /* padding */ +// __u8 __res0; /* reserved / padding */ +// __u8 __res1; /* reserved / padding */ +// __u8 data[8] __attribute__((aligned(8))); +// }; +type frame struct { + // idAndFlags is the combined CAN ID and flags. + idAndFlags uint32 + // dataLengthCode is the frame payload length in bytes. + dataLengthCode uint16 + // padding+reserved fields + _ [3]byte + // bytes contains the frame payload. + data [8]byte +} + +func (f *frame) unmarshalBinary(b []byte) { + _ = b[lengthOfFrame-1] // bounds check + f.idAndFlags = binary.LittleEndian.Uint32(b[indexOfID : indexOfID+lengthOfID]) + f.dataLengthCode = uint16(b[indexOfDataLengthCode]) + copy(f.data[:], b[indexOfData:lengthOfFrame]) +} + +func (f *frame) marshalBinary(b []byte) { + _ = b[lengthOfFrame-1] // bounds check + binary.LittleEndian.PutUint32(b[indexOfID:indexOfID+lengthOfID], f.idAndFlags) + b[indexOfDataLengthCode] = uint8(f.dataLengthCode) + copy(b[indexOfData:], f.data[:]) +} + +func (f *frame) decodeFrame() can.Frame { + return can.Frame{ + ID: f.id(), + Length: f.dataLengthCode, + Data: f.data, + IsExtended: f.isExtended(), + IsRemote: f.isRemote(), + } +} + +func (f *frame) encodeFrame(cf can.Frame) { + f.idAndFlags = cf.ID + if cf.IsRemote { + f.idAndFlags |= idFlagRemote + } + if cf.IsExtended { + f.idAndFlags |= idFlagExtended + } + f.dataLengthCode = cf.Length + f.data = cf.Data +} + +func (f *frame) isExtended() bool { + return f.idAndFlags&idFlagExtended > 0 +} + +func (f *frame) isRemote() bool { + return f.idAndFlags&idFlagRemote > 0 +} + +func (f *frame) isError() bool { + return f.idAndFlags&idFlagError > 0 +} + +func (f *frame) id() uint32 { + if f.isExtended() { + return f.idAndFlags & idMaskExtended + } + return f.idAndFlags & idMaskStandard +} + +func (f *frame) decodeErrorFrame() ErrorFrame { + return ErrorFrame{ + ErrorClass: f.errorClass(), + LostArbitrationBit: f.lostArbitrationBit(), + ControllerError: f.controllerError(), + ProtocolError: f.protocolError(), + ProtocolViolationErrorLocation: f.protocolErrorLocation(), + TransceiverError: f.transceiverError(), + ControllerSpecificInformation: f.controllerSpecificInformation(), + } +} + +func (f *frame) errorClass() ErrorClass { + return ErrorClass(f.idAndFlags &^ idFlagError) +} + +func (f *frame) lostArbitrationBit() uint8 { + return f.data[indexOfLostArbitrationBit] +} + +func (f *frame) controllerError() ControllerError { + return ControllerError(f.data[indexOfControllerError]) +} + +func (f *frame) protocolError() ProtocolViolationError { + return ProtocolViolationError(f.data[indexOfProtocolError]) +} + +func (f *frame) protocolErrorLocation() ProtocolViolationErrorLocation { + return ProtocolViolationErrorLocation(f.data[indexOfProtocolViolationErrorLocation]) +} + +func (f *frame) transceiverError() TransceiverError { + return TransceiverError(f.data[indexOfTransceiverError]) +} + +func (f *frame) controllerSpecificInformation() [LengthOfControllerSpecificInformation]byte { + var ret [LengthOfControllerSpecificInformation]byte + start := indexOfControllerSpecificInformation + end := start + LengthOfControllerSpecificInformation + copy(ret[:], f.data[start:end]) + return ret +} diff --git a/pkg/can-go/pkg/socketcan/frame_test.go b/pkg/can-go/pkg/socketcan/frame_test.go new file mode 100644 index 0000000..7aba3dd --- /dev/null +++ b/pkg/can-go/pkg/socketcan/frame_test.go @@ -0,0 +1,194 @@ +package socketcan + +import ( + "testing" + "testing/quick" + + "github.com/fiskerinc/cloud-services/pkg/can-go" + "gotest.tools/v3/assert" +) + +func TestFrame_MarshalUnmarshalBinary_Property_Idempotent(t *testing.T) { + f := func(data [lengthOfFrame]byte) [lengthOfFrame]byte { + data[5], data[6], data[7] = 0, 0, 0 // padding+reserved fields + return data + } + g := func(data [lengthOfFrame]byte) [lengthOfFrame]byte { + var f frame + f.unmarshalBinary(data[:]) + var newData [lengthOfFrame]byte + f.marshalBinary(newData[:]) + return newData + } + assert.NilError(t, quick.CheckEqual(f, g, nil)) +} + +func TestFrame_EncodeDecode(t *testing.T) { + for _, tt := range []struct { + msg string + frame can.Frame + socketCANFrame frame + }{ + { + msg: "data", + frame: can.Frame{ + ID: 0x00000001, + Length: 8, + Data: can.Data{1, 2, 3, 4, 5, 6, 7, 8}, + }, + socketCANFrame: frame{ + idAndFlags: 0x00000001, + dataLengthCode: 8, + data: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}, + }, + }, + { + msg: "extended", + frame: can.Frame{ + ID: 0x00000001, + IsExtended: true, + }, + socketCANFrame: frame{ + idAndFlags: 0x80000001, + }, + }, + { + msg: "remote", + frame: can.Frame{ + ID: 0x00000001, + IsRemote: true, + }, + socketCANFrame: frame{ + idAndFlags: 0x40000001, + }, + }, + { + msg: "extended and remote", + frame: can.Frame{ + ID: 0x00000001, + IsExtended: true, + IsRemote: true, + }, + socketCANFrame: frame{ + idAndFlags: 0xc0000001, + }, + }, + } { + tt := tt + t.Run(tt.msg, func(t *testing.T) { + t.Run("encode", func(t *testing.T) { + var actual frame + actual.encodeFrame(tt.frame) + assert.Equal(t, tt.socketCANFrame, actual) + }) + t.Run("decode", func(t *testing.T) { + assert.Equal(t, tt.frame, tt.socketCANFrame.decodeFrame()) + }) + }) + } +} + +func TestFrame_IsError(t *testing.T) { + assert.Assert(t, (&frame{idAndFlags: 0x20000001}).isError()) + assert.Assert(t, !(&frame{idAndFlags: 0x00000001}).isError()) +} + +func TestFrame_DecodeErrorFrame(t *testing.T) { + for _, tt := range []struct { + msg string + f frame + expected ErrorFrame + }{ + { + msg: "lost arbitration", + f: frame{ + idAndFlags: 0x20000002, + dataLengthCode: 8, + data: [8]byte{ + 42, + }, + }, + expected: ErrorFrame{ + ErrorClass: ErrorClassLostArbitration, + LostArbitrationBit: 42, + }, + }, + { + msg: "controller", + f: frame{ + idAndFlags: 0x20000004, + dataLengthCode: 8, + data: [8]byte{ + 0, + 0x04, + }, + }, + expected: ErrorFrame{ + ErrorClass: ErrorClassController, + ControllerError: ControllerErrorRxWarning, + }, + }, + { + msg: "protocol violation", + f: frame{ + idAndFlags: 0x20000008, + dataLengthCode: 8, + data: [8]byte{ + 0, + 0, + 0x10, + 0x02, + }, + }, + expected: ErrorFrame{ + ErrorClass: ErrorClassProtocolViolation, + ProtocolError: ProtocolViolationErrorBit1, + ProtocolViolationErrorLocation: ProtocolViolationErrorLocationID28To21, + }, + }, + { + msg: "transceiver", + f: frame{ + idAndFlags: 0x20000010, + dataLengthCode: 8, + data: [8]byte{ + 0, + 0, + 0, + 0, + 0x07, + }, + }, + expected: ErrorFrame{ + ErrorClass: ErrorClassTransceiver, + TransceiverError: TransceiverErrorCANHShortToGND, + }, + }, + { + msg: "controller-specific information", + f: frame{ + idAndFlags: 0x20000001, + dataLengthCode: 8, + data: [8]byte{ + 0, + 0, + 0, + 0, + 0, + 1, + 2, + 3, + }, + }, + expected: ErrorFrame{ + ErrorClass: ErrorClassTxTimeout, + ControllerSpecificInformation: [3]byte{1, 2, 3}, + }, + }, + } { + tt := tt + t.Run(tt.msg, func(t *testing.T) { + assert.Equal(t, tt.expected, tt.f.decodeErrorFrame()) + }) + } +} diff --git a/pkg/can-go/pkg/socketcan/main_test.go b/pkg/can-go/pkg/socketcan/main_test.go new file mode 100644 index 0000000..74cb38f --- /dev/null +++ b/pkg/can-go/pkg/socketcan/main_test.go @@ -0,0 +1,13 @@ +package socketcan + +import ( + "os" + "testing" + + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) + os.Exit(m.Run()) +} diff --git a/pkg/can-go/pkg/socketcan/protocolviolationerror.go b/pkg/can-go/pkg/socketcan/protocolviolationerror.go new file mode 100644 index 0000000..bafd2d8 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/protocolviolationerror.go @@ -0,0 +1,17 @@ +package socketcan + +type ProtocolViolationError uint8 + +//go:generate stringer -type ProtocolViolationError -trimprefix ProtocolViolationError + +const ( + ProtocolViolationErrorUnspecified ProtocolViolationError = 0x00 + ProtocolViolationErrorSingleBit ProtocolViolationError = 0x01 + ProtocolViolationErrorFrameFormat ProtocolViolationError = 0x02 + ProtocolViolationErrorBitStuffing ProtocolViolationError = 0x04 + ProtocolViolationErrorBit0 ProtocolViolationError = 0x08 // unable to send dominant bit + ProtocolViolationErrorBit1 ProtocolViolationError = 0x10 // unable to send recessive bit + ProtocolViolationErrorBusOverload ProtocolViolationError = 0x20 + ProtocolViolationErrorActive ProtocolViolationError = 0x40 // active error announcement + ProtocolViolationErrorTx ProtocolViolationError = 0x80 // error occurred on transmission +) diff --git a/pkg/can-go/pkg/socketcan/protocolviolationerror_string.go b/pkg/can-go/pkg/socketcan/protocolviolationerror_string.go new file mode 100644 index 0000000..2d81593 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/protocolviolationerror_string.go @@ -0,0 +1,55 @@ +// Code generated by "stringer -type ProtocolViolationError -trimprefix ProtocolViolationError"; DO NOT EDIT. + +package socketcan + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[ProtocolViolationErrorUnspecified-0] + _ = x[ProtocolViolationErrorSingleBit-1] + _ = x[ProtocolViolationErrorFrameFormat-2] + _ = x[ProtocolViolationErrorBitStuffing-4] + _ = x[ProtocolViolationErrorBit0-8] + _ = x[ProtocolViolationErrorBit1-16] + _ = x[ProtocolViolationErrorBusOverload-32] + _ = x[ProtocolViolationErrorActive-64] + _ = x[ProtocolViolationErrorTx-128] +} + +const ( + _ProtocolViolationError_name_0 = "UnspecifiedSingleBitFrameFormat" + _ProtocolViolationError_name_1 = "BitStuffing" + _ProtocolViolationError_name_2 = "Bit0" + _ProtocolViolationError_name_3 = "Bit1" + _ProtocolViolationError_name_4 = "BusOverload" + _ProtocolViolationError_name_5 = "Active" + _ProtocolViolationError_name_6 = "Tx" +) + +var ( + _ProtocolViolationError_index_0 = [...]uint8{0, 11, 20, 31} +) + +func (i ProtocolViolationError) String() string { + switch { + case i <= 2: + return _ProtocolViolationError_name_0[_ProtocolViolationError_index_0[i]:_ProtocolViolationError_index_0[i+1]] + case i == 4: + return _ProtocolViolationError_name_1 + case i == 8: + return _ProtocolViolationError_name_2 + case i == 16: + return _ProtocolViolationError_name_3 + case i == 32: + return _ProtocolViolationError_name_4 + case i == 64: + return _ProtocolViolationError_name_5 + case i == 128: + return _ProtocolViolationError_name_6 + default: + return "ProtocolViolationError(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/pkg/can-go/pkg/socketcan/protocolviolationerrorlocation.go b/pkg/can-go/pkg/socketcan/protocolviolationerrorlocation.go new file mode 100644 index 0000000..a9a9a91 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/protocolviolationerrorlocation.go @@ -0,0 +1,28 @@ +package socketcan + +type ProtocolViolationErrorLocation uint8 + +//go:generate stringer -type ProtocolViolationErrorLocation -trimprefix ProtocolViolationErrorLocation + +const ( + ProtocolViolationErrorLocationUnspecified ProtocolViolationErrorLocation = 0x00 + ProtocolViolationErrorLocationStartOfFrame ProtocolViolationErrorLocation = 0x03 + ProtocolViolationErrorLocationID28To21 ProtocolViolationErrorLocation = 0x02 // standard frames: 10 - 3 + ProtocolViolationErrorLocationID20To18 ProtocolViolationErrorLocation = 0x06 // standard frames: 2 - 0 + ProtocolViolationErrorLocationSubstituteRTR ProtocolViolationErrorLocation = 0x04 // standard frames: RTR + ProtocolViolationErrorLocationIDExtension ProtocolViolationErrorLocation = 0x05 + ProtocolViolationErrorLocationIDBits17To13 ProtocolViolationErrorLocation = 0x07 + ProtocolViolationErrorLocationIDBits12To05 ProtocolViolationErrorLocation = 0x0F + ProtocolViolationErrorLocationIDBits04To00 ProtocolViolationErrorLocation = 0x0E + ProtocolViolationErrorLocationRTR ProtocolViolationErrorLocation = 0x0C + ProtocolViolationErrorLocationReservedBit1 ProtocolViolationErrorLocation = 0x0D + ProtocolViolationErrorLocationReservedBit0 ProtocolViolationErrorLocation = 0x09 + ProtocolViolationErrorLocationDataLengthCode ProtocolViolationErrorLocation = 0x0B + ProtocolViolationErrorLocationData ProtocolViolationErrorLocation = 0x0A + ProtocolViolationErrorLocationCRCSequence ProtocolViolationErrorLocation = 0x08 + ProtocolViolationErrorLocationCRCDelimiter ProtocolViolationErrorLocation = 0x18 + ProtocolViolationErrorLocationACKSlot ProtocolViolationErrorLocation = 0x19 + ProtocolViolationErrorLocationACKDelimiter ProtocolViolationErrorLocation = 0x1B + ProtocolViolationErrorLocationEndOfFrame ProtocolViolationErrorLocation = 0x1A + ProtocolViolationErrorLocationIntermission ProtocolViolationErrorLocation = 0x12 +) diff --git a/pkg/can-go/pkg/socketcan/protocolviolationerrorlocation_string.go b/pkg/can-go/pkg/socketcan/protocolviolationerrorlocation_string.go new file mode 100644 index 0000000..dab3963 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/protocolviolationerrorlocation_string.go @@ -0,0 +1,60 @@ +// Code generated by "stringer -type ProtocolViolationErrorLocation -trimprefix ProtocolViolationErrorLocation"; DO NOT EDIT. + +package socketcan + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[ProtocolViolationErrorLocationUnspecified-0] + _ = x[ProtocolViolationErrorLocationStartOfFrame-3] + _ = x[ProtocolViolationErrorLocationID28To21-2] + _ = x[ProtocolViolationErrorLocationID20To18-6] + _ = x[ProtocolViolationErrorLocationSubstituteRTR-4] + _ = x[ProtocolViolationErrorLocationIDExtension-5] + _ = x[ProtocolViolationErrorLocationIDBits17To13-7] + _ = x[ProtocolViolationErrorLocationIDBits12To05-15] + _ = x[ProtocolViolationErrorLocationIDBits04To00-14] + _ = x[ProtocolViolationErrorLocationRTR-12] + _ = x[ProtocolViolationErrorLocationReservedBit1-13] + _ = x[ProtocolViolationErrorLocationReservedBit0-9] + _ = x[ProtocolViolationErrorLocationDataLengthCode-11] + _ = x[ProtocolViolationErrorLocationData-10] + _ = x[ProtocolViolationErrorLocationCRCSequence-8] + _ = x[ProtocolViolationErrorLocationCRCDelimiter-24] + _ = x[ProtocolViolationErrorLocationACKSlot-25] + _ = x[ProtocolViolationErrorLocationACKDelimiter-27] + _ = x[ProtocolViolationErrorLocationEndOfFrame-26] + _ = x[ProtocolViolationErrorLocationIntermission-18] +} + +const ( + _ProtocolViolationErrorLocation_name_0 = "Unspecified" + _ProtocolViolationErrorLocation_name_1 = "ID28To21StartOfFrameSubstituteRTRIDExtensionID20To18IDBits17To13CRCSequenceReservedBit0DataDataLengthCodeRTRReservedBit1IDBits04To00IDBits12To05" + _ProtocolViolationErrorLocation_name_2 = "Intermission" + _ProtocolViolationErrorLocation_name_3 = "CRCDelimiterACKSlotEndOfFrameACKDelimiter" +) + +var ( + _ProtocolViolationErrorLocation_index_1 = [...]uint8{0, 8, 20, 33, 44, 52, 64, 75, 87, 91, 105, 108, 120, 132, 144} + _ProtocolViolationErrorLocation_index_3 = [...]uint8{0, 12, 19, 29, 41} +) + +func (i ProtocolViolationErrorLocation) String() string { + switch { + case i == 0: + return _ProtocolViolationErrorLocation_name_0 + case 2 <= i && i <= 15: + i -= 2 + return _ProtocolViolationErrorLocation_name_1[_ProtocolViolationErrorLocation_index_1[i]:_ProtocolViolationErrorLocation_index_1[i+1]] + case i == 18: + return _ProtocolViolationErrorLocation_name_2 + case 24 <= i && i <= 27: + i -= 24 + return _ProtocolViolationErrorLocation_name_3[_ProtocolViolationErrorLocation_index_3[i]:_ProtocolViolationErrorLocation_index_3[i+1]] + default: + return "ProtocolViolationErrorLocation(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/pkg/can-go/pkg/socketcan/receiver.go b/pkg/can-go/pkg/socketcan/receiver.go new file mode 100644 index 0000000..47fd3e3 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/receiver.go @@ -0,0 +1,83 @@ +package socketcan + +import ( + "bufio" + "io" + + "github.com/fiskerinc/cloud-services/pkg/can-go" +) + +type ReceiverOption func(*receiverOpts) + +type receiverOpts struct { + frameInterceptor FrameInterceptor +} + +type Receiver struct { + opts receiverOpts + rc io.ReadCloser + sc *bufio.Scanner + frame frame +} + +func NewReceiver(rc io.ReadCloser, opt ...ReceiverOption) *Receiver { + opts := receiverOpts{} + for _, f := range opt { + f(&opts) + } + sc := bufio.NewScanner(rc) + sc.Split(scanFrames) + return &Receiver{ + rc: rc, + opts: opts, + sc: sc, + } +} + +func scanFrames(data []byte, _ bool) (int, []byte, error) { + if len(data) < lengthOfFrame { + // not enough data for a full frame + return 0, nil, nil + } + return lengthOfFrame, data[0:lengthOfFrame], nil +} + +func (r *Receiver) Receive() bool { + ok := r.sc.Scan() + r.frame = frame{} + if ok { + r.frame.unmarshalBinary(r.sc.Bytes()) + if r.opts.frameInterceptor != nil { + r.opts.frameInterceptor(r.frame.decodeFrame()) + } + } + return ok +} + +func (r *Receiver) HasErrorFrame() bool { + return r.frame.isError() +} + +func (r *Receiver) Frame() can.Frame { + return r.frame.decodeFrame() +} + +func (r *Receiver) ErrorFrame() ErrorFrame { + return r.frame.decodeErrorFrame() +} + +func (r *Receiver) Err() error { + return r.sc.Err() +} + +func (r *Receiver) Close() error { + return r.rc.Close() +} + +// ReceiverFrameInterceptor returns a ReceiverOption that sets the FrameInterceptor for the +// receiver. Only one frame interceptor can be installed. +func ReceiverFrameInterceptor(i FrameInterceptor) ReceiverOption { + return func(o *receiverOpts) { + o.frameInterceptor = i + } +} diff --git a/pkg/can-go/pkg/socketcan/receiver_test.go b/pkg/can-go/pkg/socketcan/receiver_test.go new file mode 100644 index 0000000..b79d90f --- /dev/null +++ b/pkg/can-go/pkg/socketcan/receiver_test.go @@ -0,0 +1,136 @@ +package socketcan + +import ( + "bytes" + "io/ioutil" + "testing" + + "github.com/fiskerinc/cloud-services/pkg/can-go" + "gotest.tools/v3/assert" +) + +func TestReceiver_ReceiveFrames_Options(t *testing.T) { + testReceive := func(opt ReceiverOption) { + input := []byte{ + // id---------------> | dlc | padding-------> | data----------------------------------------> | + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } + expected := can.Frame{ID: 0x01, Length: 2, Data: can.Data{0x12, 0x34}} + receiver := NewReceiver(ioutil.NopCloser(bytes.NewReader(input)), opt) + assert.Assert(t, receiver.Receive(), "expecting 1 CAN frames") + assert.NilError(t, receiver.Err()) + assert.Assert(t, !receiver.HasErrorFrame()) + assert.DeepEqual(t, expected, receiver.Frame()) + assert.Assert(t, !receiver.Receive(), "expecting exactly 1 CAN frames") + assert.NilError(t, receiver.Err()) + } + + // no options + testReceive(func(*receiverOpts) {}) + + // frame interceptor + run := false + intFunc := func(can.Frame) { + run = true + } + testReceive(ReceiverFrameInterceptor(intFunc)) + assert.Assert(t, run) +} + +func TestReceiver_ReceiveFrames(t *testing.T) { + for _, tt := range []struct { + msg string + input []byte + expectedFrames []can.Frame + }{ + { + msg: "no data", + input: []byte{}, + expectedFrames: []can.Frame{}, + }, + { + msg: "incomplete frame", + input: []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + expectedFrames: []can.Frame{}, + }, + { + msg: "whole single frame", + input: []byte{ + // id---------------> | dlc | padding-------> | data----------------------------------------> | + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + expectedFrames: []can.Frame{ + {ID: 0x01, Length: 2, Data: can.Data{0x12, 0x34}}, + }, + }, + { + msg: "one whole one incomplete", + input: []byte{ + // id---------------> | dlc | padding-------> | data----------------------------------------> | + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + }, + expectedFrames: []can.Frame{ + {ID: 0x01, Length: 2, Data: can.Data{0x12, 0x34}}, + }, + }, + { + msg: "two whole frames", + input: []byte{ + // id---------------> | dlc | padding-------> | data----------------------------------------> | + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // id---------------> | dlc | padding-------> | data----------------------------------------> | + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + expectedFrames: []can.Frame{ + {ID: 0x01, Length: 2, Data: can.Data{0x12, 0x34}}, + {ID: 0x02, Length: 2, Data: can.Data{0x56, 0x78}}, + }, + }, + } { + tt := tt + t.Run(tt.msg, func(t *testing.T) { + receiver := NewReceiver(ioutil.NopCloser(bytes.NewReader(tt.input))) + for i, expected := range tt.expectedFrames { + assert.Assert(t, receiver.Receive(), "expecting %d CAN frames", i+1) + assert.NilError(t, receiver.Err()) + assert.Assert(t, !receiver.HasErrorFrame()) + assert.DeepEqual(t, expected, receiver.Frame()) + } + assert.Assert(t, !receiver.Receive(), "expecting exactly %d CAN frames", len(tt.expectedFrames)) + assert.NilError(t, receiver.Err()) + }) + } +} + +func TestReceiver_ReceiveErrorFrame(t *testing.T) { + input := []byte{ + // frame + // id---------------> | dlc | padding-------> | data----------------------------------------> | + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // error frame + // id---------------> | dlc | padding-------> | data----------------------------------------> | + 0x01, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // frame + // id---------------> | dlc | padding-------> | data----------------------------------------> | + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } + receiver := NewReceiver(ioutil.NopCloser(bytes.NewReader(input))) + // expect frame + assert.Assert(t, receiver.Receive()) + assert.Assert(t, !receiver.HasErrorFrame()) + assert.Equal(t, can.Frame{ID: 0x01, Length: 2, Data: can.Data{0x12, 0x34}}, receiver.Frame()) + // expect error frame + assert.Assert(t, receiver.Receive()) + assert.Assert(t, receiver.HasErrorFrame()) + assert.Equal(t, ErrorFrame{ErrorClass: ErrorClassTxTimeout}, receiver.ErrorFrame()) + // expect frame + assert.Assert(t, receiver.Receive()) + assert.Assert(t, !receiver.HasErrorFrame()) + assert.Equal(t, can.Frame{ID: 0x02, Length: 2, Data: can.Data{0x12, 0x34}}, receiver.Frame()) + // expect end of stream + assert.Assert(t, !receiver.Receive()) + assert.NilError(t, receiver.Err()) +} diff --git a/pkg/can-go/pkg/socketcan/tools.go b/pkg/can-go/pkg/socketcan/tools.go new file mode 100644 index 0000000..79e0973 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/tools.go @@ -0,0 +1,5 @@ +// +build tools + +package socketcan + +import _ "github.com/golang/mock/mockgen" diff --git a/pkg/can-go/pkg/socketcan/transceivererror.go b/pkg/can-go/pkg/socketcan/transceivererror.go new file mode 100644 index 0000000..c1c7db7 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/transceivererror.go @@ -0,0 +1,18 @@ +package socketcan + +type TransceiverError uint8 + +//go:generate stringer -type TransceiverError -trimprefix TransceiverError + +const ( + TransceiverErrorUnspecified TransceiverError = 0x00 + TransceiverErrorCANHNoWire TransceiverError = 0x04 + TransceiverErrorCANHShortToBat TransceiverError = 0x05 + TransceiverErrorCANHShortToVCC TransceiverError = 0x06 + TransceiverErrorCANHShortToGND TransceiverError = 0x07 + TransceiverErrorCANLNoWire TransceiverError = 0x40 + TransceiverErrorCANLShortToBat TransceiverError = 0x50 + TransceiverErrorCANLShortToVcc TransceiverError = 0x60 + TransceiverErrorCANLShortToGND TransceiverError = 0x70 + TransceiverErrorCANLShortToCANH TransceiverError = 0x80 +) diff --git a/pkg/can-go/pkg/socketcan/transceivererror_string.go b/pkg/can-go/pkg/socketcan/transceivererror_string.go new file mode 100644 index 0000000..26ef4bd --- /dev/null +++ b/pkg/can-go/pkg/socketcan/transceivererror_string.go @@ -0,0 +1,57 @@ +// Code generated by "stringer -type TransceiverError -trimprefix TransceiverError"; DO NOT EDIT. + +package socketcan + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[TransceiverErrorUnspecified-0] + _ = x[TransceiverErrorCANHNoWire-4] + _ = x[TransceiverErrorCANHShortToBat-5] + _ = x[TransceiverErrorCANHShortToVCC-6] + _ = x[TransceiverErrorCANHShortToGND-7] + _ = x[TransceiverErrorCANLNoWire-64] + _ = x[TransceiverErrorCANLShortToBat-80] + _ = x[TransceiverErrorCANLShortToVcc-96] + _ = x[TransceiverErrorCANLShortToGND-112] + _ = x[TransceiverErrorCANLShortToCANH-128] +} + +const ( + _TransceiverError_name_0 = "Unspecified" + _TransceiverError_name_1 = "CANHNoWireCANHShortToBatCANHShortToVCCCANHShortToGND" + _TransceiverError_name_2 = "CANLNoWire" + _TransceiverError_name_3 = "CANLShortToBat" + _TransceiverError_name_4 = "CANLShortToVcc" + _TransceiverError_name_5 = "CANLShortToGND" + _TransceiverError_name_6 = "CANLShortToCANH" +) + +var ( + _TransceiverError_index_1 = [...]uint8{0, 10, 24, 38, 52} +) + +func (i TransceiverError) String() string { + switch { + case i == 0: + return _TransceiverError_name_0 + case 4 <= i && i <= 7: + i -= 4 + return _TransceiverError_name_1[_TransceiverError_index_1[i]:_TransceiverError_index_1[i+1]] + case i == 64: + return _TransceiverError_name_2 + case i == 80: + return _TransceiverError_name_3 + case i == 96: + return _TransceiverError_name_4 + case i == 112: + return _TransceiverError_name_5 + case i == 128: + return _TransceiverError_name_6 + default: + return "TransceiverError(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/pkg/can-go/pkg/socketcan/transmitter.go b/pkg/can-go/pkg/socketcan/transmitter.go new file mode 100644 index 0000000..95cda27 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/transmitter.go @@ -0,0 +1,75 @@ +package socketcan + +import ( + "context" + "fmt" + "net" + + "github.com/fiskerinc/cloud-services/pkg/can-go" +) + +type TransmitterOption func(*transmitterOpts) + +type transmitterOpts struct { + frameInterceptor FrameInterceptor +} + +// Transmitter transmits CAN frames. +type Transmitter struct { + opts transmitterOpts + conn net.Conn +} + +// NewTransmitter creates a new transmitter that transmits CAN frames to the provided io.Writer. +func NewTransmitter(conn net.Conn, opt ...TransmitterOption) *Transmitter { + opts := transmitterOpts{} + for _, f := range opt { + f(&opts) + } + return &Transmitter{ + conn: conn, + opts: opts, + } +} + +// TransmitMessage transmits a CAN message. +func (t *Transmitter) TransmitMessage(ctx context.Context, m can.Message) error { + f, err := m.MarshalFrame() + if err != nil { + return fmt.Errorf("transmit message: %w", err) + } + return t.TransmitFrame(ctx, f) +} + +// TransmitFrame transmits a CAN frame. +func (t *Transmitter) TransmitFrame(ctx context.Context, f can.Frame) error { + var scf frame + scf.encodeFrame(f) + data := make([]byte, lengthOfFrame) + scf.marshalBinary(data) + if deadline, ok := ctx.Deadline(); ok { + if err := t.conn.SetWriteDeadline(deadline); err != nil { + return fmt.Errorf("transmit frame: %w", err) + } + } + if _, err := t.conn.Write(data); err != nil { + return fmt.Errorf("transmit frame: %w", err) + } + if t.opts.frameInterceptor != nil { + t.opts.frameInterceptor(f) + } + return nil +} + +// Close the transmitter's underlying connection. +func (t *Transmitter) Close() error { + return t.conn.Close() +} + +// TransmitterFrameInterceptor returns a TransmitterOption that sets the FrameInterceptor for the +// transmitter. Only one frame interceptor can be installed. +func TransmitterFrameInterceptor(i FrameInterceptor) TransmitterOption { + return func(o *transmitterOpts) { + o.frameInterceptor = i + } +} diff --git a/pkg/can-go/pkg/socketcan/transmitter_test.go b/pkg/can-go/pkg/socketcan/transmitter_test.go new file mode 100644 index 0000000..acf900d --- /dev/null +++ b/pkg/can-go/pkg/socketcan/transmitter_test.go @@ -0,0 +1,145 @@ +package socketcan + +import ( + "context" + "errors" + "fmt" + "io" + "net" + "testing" + "time" + + "github.com/fiskerinc/cloud-services/pkg/can-go" + "golang.org/x/sync/errgroup" + "gotest.tools/v3/assert" +) + +func TestTransmitter_TransmitMessage(t *testing.T) { + testTransmit := func(opt TransmitterOption) { + w, r := net.Pipe() + f := can.Frame{ + ID: 0x12, + Length: 8, + Data: can.Data{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, + } + msg := &testMessage{frame: f} + expected := []byte{ + // id---------------> | dlc | padding-------> | data----------------------------------------> | + 0x12, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, + } + // write + var g errgroup.Group + g.Go(func() error { + tr := NewTransmitter(w, opt) + ctx, done := context.WithTimeout(context.Background(), time.Second) + defer done() + if err := tr.TransmitMessage(ctx, msg); err != nil { + return err + } + return w.Close() + }) + // read + actual := make([]byte, len(expected)) + _, err := io.ReadFull(r, actual) + assert.NilError(t, err) + assert.NilError(t, r.Close()) + // assert + assert.DeepEqual(t, expected, actual) + assert.NilError(t, g.Wait()) + } + + // No opts + testTransmit(func(*transmitterOpts) {}) + + // Frame Interceptor + run := false + intFunc := func(fr can.Frame) { + run = true + } + testTransmit(TransmitterFrameInterceptor(intFunc)) + assert.Assert(t, run) +} + +func TestTransmitter_TransmitMessage_Error(t *testing.T) { + cause := fmt.Errorf("boom") + msg := &testMessage{err: cause} + tr := NewTransmitter(nil) + ctx, done := context.WithTimeout(context.Background(), time.Second) + defer done() + err := tr.TransmitMessage(ctx, msg) + assert.Error(t, err, "transmit message: boom") + assert.Equal(t, cause, errors.Unwrap(err)) +} + +func TestTransmitter_TransmitFrame_Error(t *testing.T) { + t.Run("set deadline", func(t *testing.T) { + cause := fmt.Errorf("boom") + w := &errCon{deadlineErr: cause} + tr := NewTransmitter(w) + ctx, done := context.WithTimeout(context.Background(), time.Second) + defer done() + err := tr.TransmitFrame(ctx, can.Frame{}) + assert.ErrorContains(t, err, "boom") + assert.Equal(t, cause, errors.Unwrap(err)) + }) + t.Run("write", func(t *testing.T) { + cause := fmt.Errorf("boom") + w := &errCon{writeErr: cause} + tr := NewTransmitter(w) + ctx, done := context.WithTimeout(context.Background(), time.Second) + defer done() + err := tr.TransmitFrame(ctx, can.Frame{}) + assert.ErrorContains(t, err, "boom") + assert.Equal(t, cause, errors.Unwrap(err)) + }) +} + +type testMessage struct { + frame can.Frame + err error +} + +func (t *testMessage) MarshalFrame() (can.Frame, error) { + return t.frame, t.err +} + +func (t *testMessage) UnmarshalFrame(can.Frame) error { + panic("should not be called") +} + +type errCon struct { + deadlineErr error + writeErr error +} + +func (e *errCon) Write(b []byte) (n int, err error) { + return 0, e.writeErr +} + +func (e *errCon) SetWriteDeadline(t time.Time) error { + return e.deadlineErr +} + +func (e *errCon) Read(b []byte) (n int, err error) { + panic("should not be called") +} + +func (e *errCon) Close() error { + panic("should not be called") +} + +func (e *errCon) LocalAddr() net.Addr { + panic("should not be called") +} + +func (e *errCon) RemoteAddr() net.Addr { + panic("should not be called") +} + +func (e *errCon) SetDeadline(t time.Time) error { + panic("should not be called") +} + +func (e *errCon) SetReadDeadline(t time.Time) error { + panic("should not be called") +} diff --git a/pkg/can-go/pkg/socketcan/udp.go b/pkg/can-go/pkg/socketcan/udp.go new file mode 100644 index 0000000..d0d0048 --- /dev/null +++ b/pkg/can-go/pkg/socketcan/udp.go @@ -0,0 +1,151 @@ +package socketcan + +import ( + "fmt" + "net" + "strconv" + "time" + + "golang.org/x/net/ipv4" + "golang.org/x/net/nettest" +) + +// udpTxRx emulates a single `net.Conn` that can be used for both transmitting +// and receiving UDP multicast packets. +type udpTxRx struct { + tx *ipv4.PacketConn + rx *ipv4.PacketConn + groupAddr *net.UDPAddr +} + +func (utr *udpTxRx) Close() error { + if err := utr.tx.Close(); err != nil { + _ = utr.rx.Close() + return err + } + return utr.rx.Close() +} + +func (utr *udpTxRx) LocalAddr() net.Addr { + return utr.rx.LocalAddr() +} + +func (utr *udpTxRx) SetDeadline(t time.Time) error { + if err := utr.rx.SetReadDeadline(t); err != nil { + return err + } + return utr.tx.SetWriteDeadline(t) +} + +func (utr *udpTxRx) SetReadDeadline(t time.Time) error { + return utr.rx.SetReadDeadline(t) +} + +func (utr *udpTxRx) SetWriteDeadline(t time.Time) error { + return utr.tx.SetWriteDeadline(t) +} + +func (utr *udpTxRx) Read(b []byte) (n int, err error) { + n, _, _, err = utr.rx.ReadFrom(b) + return +} + +func (utr *udpTxRx) Write(b []byte) (n int, err error) { + return utr.tx.WriteTo(b, nil, nil) +} + +func (utr *udpTxRx) RemoteAddr() net.Addr { + return utr.groupAddr +} + +func udpTransceiver(network, address string) (*udpTxRx, error) { + if network != udp { + return nil, fmt.Errorf("[%v] is not a udp network", network) + } + ifi, err := getMulticastInterface() + if err != nil { + return nil, fmt.Errorf("new UDP transceiver: %w", err) + } + rx, groupAddr, err := udpReceiver(address, ifi) + if err != nil { + return nil, fmt.Errorf("new UDP transceiver: %w", err) + } + tx, err := udpTransmitter(groupAddr, ifi) + if err != nil { + return nil, fmt.Errorf("new UDP transceiver: %w", err) + } + return &udpTxRx{rx: rx, tx: tx, groupAddr: groupAddr}, nil +} + +func getMulticastInterface() (*net.Interface, error) { + ifi, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback) + if err == nil { + return ifi, nil + } + return nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast) +} + +func hostPortToUDPAddr(hostport string) (*net.UDPAddr, error) { + host, portStr, err := net.SplitHostPort(hostport) + if err != nil { + return nil, fmt.Errorf("convert hostport to udp addr: %w", err) + } + port, err := strconv.Atoi(portStr) + if err != nil { + return nil, fmt.Errorf("convert hostport to udp addr: %w", err) + } + ip := net.ParseIP(host) + return &net.UDPAddr{Port: port, IP: ip}, nil +} + +func setMulticastOpts(p *ipv4.PacketConn, ifi *net.Interface, groupAddr net.Addr) error { + if err := p.JoinGroup(ifi, groupAddr); err != nil { + return err + } + if err := p.SetMulticastInterface(ifi); err != nil { + return err + } + if err := p.SetMulticastLoopback(true); err != nil { + return err + } + if err := p.SetMulticastTTL(0); err != nil { + return err + } + return p.SetTOS(0x0) +} + +func udpReceiver(address string, ifi *net.Interface) (*ipv4.PacketConn, *net.UDPAddr, error) { + c, err := net.ListenPacket("udp4", address) + if err != nil { + return nil, nil, fmt.Errorf("create udp receiver: %w", err) + } + groupAddr, err := hostPortToUDPAddr(address) + if err != nil { + return nil, nil, fmt.Errorf("create udp receiver: %w", err) + } + // If requested port is 0, one is provided when creating the packet listener + if groupAddr.Port == 0 { + localAddr, err := hostPortToUDPAddr(c.LocalAddr().String()) + if err != nil { + return nil, nil, fmt.Errorf("create udp receiver: %w", err) + } + groupAddr.Port = localAddr.Port + } + rx := ipv4.NewPacketConn(c) + if err := setMulticastOpts(rx, ifi, groupAddr); err != nil { + return nil, nil, fmt.Errorf("new UDP transceiver: %w", err) + } + return rx, groupAddr, nil +} + +func udpTransmitter(groupAddr *net.UDPAddr, ifi *net.Interface) (*ipv4.PacketConn, error) { + c, err := net.DialUDP("udp4", nil, groupAddr) + if err != nil { + return nil, fmt.Errorf("new UDP transmitter: %w", err) + } + tx := ipv4.NewPacketConn(c) + if err := tx.SetMulticastInterface(ifi); err != nil { + return nil, fmt.Errorf("new UDP transmitter: %w", err) + } + return tx, nil +} diff --git a/pkg/can-go/scripts/go.mod b/pkg/can-go/scripts/go.mod new file mode 100644 index 0000000..bb5b909 --- /dev/null +++ b/pkg/can-go/scripts/go.mod @@ -0,0 +1,23 @@ +module scripts + +go 1.21 + +require ( + cloud.google.com/go v0.81.0 + golang.org/x/text v0.13.0 + google.golang.org/api v0.46.0 +) + +require ( + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/googleapis/gax-go/v2 v2.0.5 // indirect + go.opencensus.io v0.23.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20210506142907-4a47615972c2 // indirect + google.golang.org/grpc v1.37.0 // indirect + google.golang.org/protobuf v1.26.0 // indirect +) diff --git a/pkg/can-go/scripts/go.sum b/pkg/can-go/scripts/go.sum new file mode 100644 index 0000000..59a5a5f --- /dev/null +++ b/pkg/can-go/scripts/go.sum @@ -0,0 +1,477 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c h1:SgVl/sCtkicsS7psKkje4H9YtjdEl3xsYh7N+5TDHqY= +golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.46.0 h1:jkDWHOBIoNSD0OQpq4rtBVu+Rh325MPjXG1rakAp8JU= +google.golang.org/api v0.46.0/go.mod h1:ceL4oozhkAiTID8XMmJBsIxID/9wMXJVVFXPg4ylg3I= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210429181445-86c259c2b4ab/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210506142907-4a47615972c2 h1:pl8qT5D+48655f14yDURpIZwSPvMWuuekfAP+gxtjvk= +google.golang.org/genproto v0.0.0-20210506142907-4a47615972c2/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/pkg/can-go/scripts/main.go b/pkg/can-go/scripts/main.go new file mode 100644 index 0000000..0e84647 --- /dev/null +++ b/pkg/can-go/scripts/main.go @@ -0,0 +1,136 @@ +package main + +import ( + "bufio" + "context" + "fmt" + "log" + "os" + "regexp" + "unicode" + + "golang.org/x/text/encoding/simplifiedchinese" + "golang.org/x/text/language" + "golang.org/x/text/transform" + + "cloud.google.com/go/translate" + "google.golang.org/api/option" +) + +const dbcReadFilePath = "../orig/121-N60AB_ADASBUS_Matrix_CANFD_V2.3.dbc" +const dbcWriteFilePath = "../dbc/n60.dbc" +const googleAPIProjectKey = "" + +var enc = simplifiedchinese.GB18030 +var lexicon map[string]string + +func main() { + lexicon = make(map[string]string) + reg, err := regexp.Compile("[^a-zA-Z0-9]+") + if err != nil { + log.Fatal(err) + } + + readFile, err := os.Open(dbcReadFilePath) + if err != nil { + log.Fatal(err) + } + defer readFile.Close() + r := transform.NewReader(readFile, enc.NewDecoder()) + + writeFile, err := os.Create(dbcWriteFilePath) + defer writeFile.Close() + w := transform.NewWriter(writeFile, enc.NewEncoder()) + + scanner := bufio.NewScanner(r) + for scanner.Scan() { + line := scanner.Text() + indices := findIndices(line, "\"") + newline := "" + + if len(indices) == 0 { + newline = line + } else { + for i := 0; i < len(indices); i += 2 { + if i-1 >= 0 { + newline += line[indices[i-1]+1 : indices[i]] + } else { + newline += line[:indices[i]] + } + + newline += "\"" + value := line[indices[i]+1 : indices[i+1]] + + fmt.Println(value, containsChinese(value)) + if containsChinese(value) { + translatedValue, err := translateText(value) + if err != nil { + log.Fatal(err) + } + fmt.Println(translatedValue) + value = translatedValue + } + + newline += reg.ReplaceAllString(value, "") + newline += "\"" + } + + newline += line[indices[len(indices)-1]+1:] + } + + fmt.Fprintln(w, newline) + } + + fmt.Printf("strings translated: %v", len(lexicon)) +} + +func findIndices(text string, char string) []int { + var indices []int + + for i, c := range text { + if string(c) == char { + indices = append(indices, i) + } + } + + if len(indices)%2 != 0 { + return make([]int, 0) + } + + return indices +} + +func containsChinese(text string) bool { + for _, runeValue := range text { + if unicode.Is(unicode.Han, runeValue) { + return true + } + } + return false +} + +func translateText(text string) (string, error) { + if translated, ok := lexicon[text]; ok { + return translated, nil + } + + ctx := context.Background() + + client, err := translate.NewClient(ctx, option.WithAPIKey(googleAPIProjectKey)) + if err != nil { + return "", err + } + defer client.Close() + + resp, err := client.Translate(ctx, []string{text}, language.English, nil) + if err != nil { + return "", fmt.Errorf("Translate: %v", err) + } + if len(resp) == 0 { + return "", fmt.Errorf("Translate returned empty response to text: %s", text) + } + + lexicon[text] = resp[0].Text + + return resp[0].Text, nil +} diff --git a/pkg/can-go/testdata/dbc/example/example.dbc.golden b/pkg/can-go/testdata/dbc/example/example.dbc.golden new file mode 100644 index 0000000..e6e18d9 --- /dev/null +++ b/pkg/can-go/testdata/dbc/example/example.dbc.golden @@ -0,0 +1,894 @@ +([]dbc.Def) (len=43) { + (*dbc.VersionDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:1:1, + Version: (string) "" + }), + (*dbc.NewSymbolsDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:3:1, + Symbols: ([]dbc.Keyword) + }), + (*dbc.BitTimingDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:5:1, + BaudRate: (uint64) 0, + BTR1: (uint64) 0, + BTR2: (uint64) 0 + }), + (*dbc.NodesDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:7:1, + NodeNames: ([]dbc.Identifier) (len=5) { + (dbc.Identifier) (len=3) "DBG", + (dbc.Identifier) (len=6) "DRIVER", + (dbc.Identifier) (len=2) "IO", + (dbc.Identifier) (len=5) "MOTOR", + (dbc.Identifier) (len=6) "SENSOR" + } + }), + (*dbc.MessageDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:9:1, + MessageID: (dbc.MessageID) 1, + Name: (dbc.Identifier) (len=12) "EmptyMessage", + Size: (uint64) 0, + Transmitter: (dbc.Identifier) (len=3) "DBG", + Signals: ([]dbc.SignalDef) + }), + (*dbc.MessageDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:11:1, + MessageID: (dbc.MessageID) 100, + Name: (dbc.Identifier) (len=15) "DriverHeartbeat", + Size: (uint64) 1, + Transmitter: (dbc.Identifier) (len=6) "DRIVER", + Signals: ([]dbc.SignalDef) (len=1) { + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:12:2, + Name: (dbc.Identifier) (len=7) "Command", + StartBit: (uint64) 0, + Size: (uint64) 8, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) false, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=2) { + (dbc.Identifier) (len=6) "SENSOR", + (dbc.Identifier) (len=5) "MOTOR" + } + } + } + }), + (*dbc.MessageDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:14:1, + MessageID: (dbc.MessageID) 101, + Name: (dbc.Identifier) (len=12) "MotorCommand", + Size: (uint64) 1, + Transmitter: (dbc.Identifier) (len=6) "DRIVER", + Signals: ([]dbc.SignalDef) (len=2) { + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:15:2, + Name: (dbc.Identifier) (len=5) "Steer", + StartBit: (uint64) 0, + Size: (uint64) 4, + IsBigEndian: (bool) false, + IsSigned: (bool) true, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) false, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) -5, + Factor: (float64) 1, + Minimum: (float64) -5, + Maximum: (float64) 5, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=1) { + (dbc.Identifier) (len=5) "MOTOR" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:16:2, + Name: (dbc.Identifier) (len=5) "Drive", + StartBit: (uint64) 4, + Size: (uint64) 4, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) false, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 1, + Minimum: (float64) 0, + Maximum: (float64) 9, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=1) { + (dbc.Identifier) (len=5) "MOTOR" + } + } + } + }), + (*dbc.MessageDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:18:1, + MessageID: (dbc.MessageID) 400, + Name: (dbc.Identifier) (len=11) "MotorStatus", + Size: (uint64) 3, + Transmitter: (dbc.Identifier) (len=5) "MOTOR", + Signals: ([]dbc.SignalDef) (len=2) { + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:19:2, + Name: (dbc.Identifier) (len=10) "WheelError", + StartBit: (uint64) 0, + Size: (uint64) 1, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) false, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=2) { + (dbc.Identifier) (len=6) "DRIVER", + (dbc.Identifier) (len=2) "IO" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:20:2, + Name: (dbc.Identifier) (len=8) "SpeedKph", + StartBit: (uint64) 8, + Size: (uint64) 16, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) false, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 0.001, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) (len=4) "km/h", + Receivers: ([]dbc.Identifier) (len=2) { + (dbc.Identifier) (len=6) "DRIVER", + (dbc.Identifier) (len=2) "IO" + } + } + } + }), + (*dbc.MessageDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:22:1, + MessageID: (dbc.MessageID) 200, + Name: (dbc.Identifier) (len=12) "SensorSonars", + Size: (uint64) 8, + Transmitter: (dbc.Identifier) (len=6) "SENSOR", + Signals: ([]dbc.SignalDef) (len=10) { + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:23:2, + Name: (dbc.Identifier) (len=3) "Mux", + StartBit: (uint64) 0, + Size: (uint64) 4, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) true, + IsMultiplexed: (bool) false, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=2) { + (dbc.Identifier) (len=6) "DRIVER", + (dbc.Identifier) (len=2) "IO" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:24:2, + Name: (dbc.Identifier) (len=8) "ErrCount", + StartBit: (uint64) 4, + Size: (uint64) 12, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) false, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=2) { + (dbc.Identifier) (len=6) "DRIVER", + (dbc.Identifier) (len=2) "IO" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:25:2, + Name: (dbc.Identifier) (len=4) "Left", + StartBit: (uint64) 16, + Size: (uint64) 12, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) true, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 0.1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=2) { + (dbc.Identifier) (len=6) "DRIVER", + (dbc.Identifier) (len=2) "IO" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:26:2, + Name: (dbc.Identifier) (len=6) "Middle", + StartBit: (uint64) 28, + Size: (uint64) 12, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) true, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 0.1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=2) { + (dbc.Identifier) (len=6) "DRIVER", + (dbc.Identifier) (len=2) "IO" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:27:2, + Name: (dbc.Identifier) (len=5) "Right", + StartBit: (uint64) 40, + Size: (uint64) 12, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) true, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 0.1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=2) { + (dbc.Identifier) (len=6) "DRIVER", + (dbc.Identifier) (len=2) "IO" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:28:2, + Name: (dbc.Identifier) (len=4) "Rear", + StartBit: (uint64) 52, + Size: (uint64) 12, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) true, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 0.1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=2) { + (dbc.Identifier) (len=6) "DRIVER", + (dbc.Identifier) (len=2) "IO" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:29:2, + Name: (dbc.Identifier) (len=10) "NoFiltLeft", + StartBit: (uint64) 16, + Size: (uint64) 12, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) true, + MultiplexerSwitch: (uint64) 1, + Offset: (float64) 0, + Factor: (float64) 0.1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=1) { + (dbc.Identifier) (len=3) "DBG" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:30:2, + Name: (dbc.Identifier) (len=12) "NoFiltMiddle", + StartBit: (uint64) 28, + Size: (uint64) 12, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) true, + MultiplexerSwitch: (uint64) 1, + Offset: (float64) 0, + Factor: (float64) 0.1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=1) { + (dbc.Identifier) (len=3) "DBG" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:31:2, + Name: (dbc.Identifier) (len=11) "NoFiltRight", + StartBit: (uint64) 40, + Size: (uint64) 12, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) true, + MultiplexerSwitch: (uint64) 1, + Offset: (float64) 0, + Factor: (float64) 0.1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=1) { + (dbc.Identifier) (len=3) "DBG" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:32:2, + Name: (dbc.Identifier) (len=10) "NoFiltRear", + StartBit: (uint64) 52, + Size: (uint64) 12, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) true, + MultiplexerSwitch: (uint64) 1, + Offset: (float64) 0, + Factor: (float64) 0.1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=1) { + (dbc.Identifier) (len=3) "DBG" + } + } + } + }), + (*dbc.MessageDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:34:1, + MessageID: (dbc.MessageID) 500, + Name: (dbc.Identifier) (len=7) "IODebug", + Size: (uint64) 6, + Transmitter: (dbc.Identifier) (len=2) "IO", + Signals: ([]dbc.SignalDef) (len=6) { + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:35:2, + Name: (dbc.Identifier) (len=12) "TestUnsigned", + StartBit: (uint64) 0, + Size: (uint64) 8, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) false, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=1) { + (dbc.Identifier) (len=3) "DBG" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:36:2, + Name: (dbc.Identifier) (len=8) "TestEnum", + StartBit: (uint64) 8, + Size: (uint64) 6, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) false, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=1) { + (dbc.Identifier) (len=3) "DBG" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:37:2, + Name: (dbc.Identifier) (len=10) "TestSigned", + StartBit: (uint64) 16, + Size: (uint64) 8, + IsBigEndian: (bool) false, + IsSigned: (bool) true, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) false, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=1) { + (dbc.Identifier) (len=3) "DBG" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:38:2, + Name: (dbc.Identifier) (len=9) "TestFloat", + StartBit: (uint64) 24, + Size: (uint64) 8, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) false, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 0.5, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=1) { + (dbc.Identifier) (len=3) "DBG" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:39:2, + Name: (dbc.Identifier) (len=12) "TestBoolEnum", + StartBit: (uint64) 32, + Size: (uint64) 1, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) false, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 1, + Minimum: (float64) 0, + Maximum: (float64) 0, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=1) { + (dbc.Identifier) (len=3) "DBG" + } + }, + (dbc.SignalDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:40:2, + Name: (dbc.Identifier) (len=14) "TestScaledEnum", + StartBit: (uint64) 40, + Size: (uint64) 2, + IsBigEndian: (bool) false, + IsSigned: (bool) false, + IsMultiplexerSwitch: (bool) false, + IsMultiplexed: (bool) false, + MultiplexerSwitch: (uint64) 0, + Offset: (float64) 0, + Factor: (float64) 2, + Minimum: (float64) 0, + Maximum: (float64) 6, + Unit: (string) "", + Receivers: ([]dbc.Identifier) (len=1) { + (dbc.Identifier) (len=3) "DBG" + } + } + } + }), + (*dbc.EnvironmentVariableDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:42:1, + Name: (dbc.Identifier) (len=12) "BrakeEngaged", + Type: (dbc.EnvironmentVariableType) 0, + Minimum: (float64) 0, + Maximum: (float64) 1, + Unit: (string) "", + InitialValue: (float64) 0, + ID: (uint64) 10, + AccessType: (dbc.AccessType) (len=18) "DUMMY_NODE_VECTOR0", + AccessNodes: ([]dbc.Identifier) (len=1) { + (dbc.Identifier) (len=11) "Vector__XXX" + } + }), + (*dbc.EnvironmentVariableDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:43:1, + Name: (dbc.Identifier) (len=6) "Torque", + Type: (dbc.EnvironmentVariableType) 1, + Minimum: (float64) 0, + Maximum: (float64) 30000, + Unit: (string) (len=3) "mNm", + InitialValue: (float64) 500, + ID: (uint64) 16, + AccessType: (dbc.AccessType) (len=18) "DUMMY_NODE_VECTOR0", + AccessNodes: ([]dbc.Identifier) (len=1) { + (dbc.Identifier) (len=11) "Vector__XXX" + } + }), + (*dbc.CommentDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:45:1, + ObjectType: (dbc.ObjectType) (len=3) "EV_", + NodeName: (dbc.Identifier) "", + MessageID: (dbc.MessageID) 0, + SignalName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) (len=12) "BrakeEngaged", + Comment: (string) (len=19) "Brake fully engaged" + }), + (*dbc.CommentDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:46:1, + ObjectType: (dbc.ObjectType) (len=3) "BU_", + NodeName: (dbc.Identifier) (len=6) "DRIVER", + MessageID: (dbc.MessageID) 0, + SignalName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + Comment: (string) (len=37) "The driver controller driving the car" + }), + (*dbc.CommentDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:47:1, + ObjectType: (dbc.ObjectType) (len=3) "BU_", + NodeName: (dbc.Identifier) (len=5) "MOTOR", + MessageID: (dbc.MessageID) 0, + SignalName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + Comment: (string) (len=31) "The motor controller of the car" + }), + (*dbc.CommentDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:48:1, + ObjectType: (dbc.ObjectType) (len=3) "BU_", + NodeName: (dbc.Identifier) (len=6) "SENSOR", + MessageID: (dbc.MessageID) 0, + SignalName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + Comment: (string) (len=32) "The sensor controller of the car" + }), + (*dbc.CommentDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:49:1, + ObjectType: (dbc.ObjectType) (len=3) "BO_", + NodeName: (dbc.Identifier) "", + MessageID: (dbc.MessageID) 100, + SignalName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + Comment: (string) (len=48) "Sync message used to synchronize the controllers" + }), + (*dbc.AttributeDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:51:1, + ObjectType: (dbc.ObjectType) "", + Name: (dbc.Identifier) (len=7) "BusType", + Type: (dbc.AttributeValueType) (len=6) "STRING", + MinimumInt: (int64) 0, + MaximumInt: (int64) 0, + MinimumFloat: (float64) 0, + MaximumFloat: (float64) 0, + EnumValues: ([]string) + }), + (*dbc.AttributeDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:52:1, + ObjectType: (dbc.ObjectType) (len=3) "BO_", + Name: (dbc.Identifier) (len=14) "GenMsgSendType", + Type: (dbc.AttributeValueType) (len=4) "ENUM", + MinimumInt: (int64) 0, + MaximumInt: (int64) 0, + MinimumFloat: (float64) 0, + MaximumFloat: (float64) 0, + EnumValues: ([]string) (len=3) { + (string) (len=4) "None", + (string) (len=6) "Cyclic", + (string) (len=7) "OnEvent" + } + }), + (*dbc.AttributeDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:53:1, + ObjectType: (dbc.ObjectType) (len=3) "BO_", + Name: (dbc.Identifier) (len=15) "GenMsgCycleTime", + Type: (dbc.AttributeValueType) (len=3) "INT", + MinimumInt: (int64) 0, + MaximumInt: (int64) 0, + MinimumFloat: (float64) 0, + MaximumFloat: (float64) 0, + EnumValues: ([]string) + }), + (*dbc.AttributeDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:54:1, + ObjectType: (dbc.ObjectType) (len=3) "SG_", + Name: (dbc.Identifier) (len=9) "FieldType", + Type: (dbc.AttributeValueType) (len=6) "STRING", + MinimumInt: (int64) 0, + MaximumInt: (int64) 0, + MinimumFloat: (float64) 0, + MaximumFloat: (float64) 0, + EnumValues: ([]string) + }), + (*dbc.AttributeDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:55:1, + ObjectType: (dbc.ObjectType) (len=3) "SG_", + Name: (dbc.Identifier) (len=16) "GenSigStartValue", + Type: (dbc.AttributeValueType) (len=3) "INT", + MinimumInt: (int64) 0, + MaximumInt: (int64) 10000, + MinimumFloat: (float64) 0, + MaximumFloat: (float64) 0, + EnumValues: ([]string) + }), + (*dbc.AttributeDefaultValueDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:56:1, + AttributeName: (dbc.Identifier) (len=7) "BusType", + DefaultIntValue: (int64) 0, + DefaultFloatValue: (float64) 0, + DefaultStringValue: (string) (len=3) "CAN" + }), + (*dbc.AttributeDefaultValueDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:57:1, + AttributeName: (dbc.Identifier) (len=9) "FieldType", + DefaultIntValue: (int64) 0, + DefaultFloatValue: (float64) 0, + DefaultStringValue: (string) "" + }), + (*dbc.AttributeDefaultValueDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:58:1, + AttributeName: (dbc.Identifier) (len=15) "GenMsgCycleTime", + DefaultIntValue: (int64) 0, + DefaultFloatValue: (float64) 0, + DefaultStringValue: (string) "" + }), + (*dbc.AttributeDefaultValueDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:59:1, + AttributeName: (dbc.Identifier) (len=16) "GenSigStartValue", + DefaultIntValue: (int64) 0, + DefaultFloatValue: (float64) 0, + DefaultStringValue: (string) "" + }), + (*dbc.AttributeValueForObjectDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:61:1, + AttributeName: (dbc.Identifier) (len=14) "GenMsgSendType", + ObjectType: (dbc.ObjectType) (len=3) "BO_", + MessageID: (dbc.MessageID) 1, + SignalName: (dbc.Identifier) "", + NodeName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + IntValue: (int64) 0, + FloatValue: (float64) 0, + StringValue: (string) (len=4) "None" + }), + (*dbc.AttributeValueForObjectDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:62:1, + AttributeName: (dbc.Identifier) (len=14) "GenMsgSendType", + ObjectType: (dbc.ObjectType) (len=3) "BO_", + MessageID: (dbc.MessageID) 100, + SignalName: (dbc.Identifier) "", + NodeName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + IntValue: (int64) 0, + FloatValue: (float64) 0, + StringValue: (string) (len=6) "Cyclic" + }), + (*dbc.AttributeValueForObjectDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:63:1, + AttributeName: (dbc.Identifier) (len=15) "GenMsgCycleTime", + ObjectType: (dbc.ObjectType) (len=3) "BO_", + MessageID: (dbc.MessageID) 100, + SignalName: (dbc.Identifier) "", + NodeName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + IntValue: (int64) 1000, + FloatValue: (float64) 0, + StringValue: (string) "" + }), + (*dbc.AttributeValueForObjectDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:64:1, + AttributeName: (dbc.Identifier) (len=14) "GenMsgSendType", + ObjectType: (dbc.ObjectType) (len=3) "BO_", + MessageID: (dbc.MessageID) 101, + SignalName: (dbc.Identifier) "", + NodeName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + IntValue: (int64) 0, + FloatValue: (float64) 0, + StringValue: (string) (len=6) "Cyclic" + }), + (*dbc.AttributeValueForObjectDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:65:1, + AttributeName: (dbc.Identifier) (len=15) "GenMsgCycleTime", + ObjectType: (dbc.ObjectType) (len=3) "BO_", + MessageID: (dbc.MessageID) 101, + SignalName: (dbc.Identifier) "", + NodeName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + IntValue: (int64) 100, + FloatValue: (float64) 0, + StringValue: (string) "" + }), + (*dbc.AttributeValueForObjectDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:66:1, + AttributeName: (dbc.Identifier) (len=14) "GenMsgSendType", + ObjectType: (dbc.ObjectType) (len=3) "BO_", + MessageID: (dbc.MessageID) 200, + SignalName: (dbc.Identifier) "", + NodeName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + IntValue: (int64) 0, + FloatValue: (float64) 0, + StringValue: (string) (len=6) "Cyclic" + }), + (*dbc.AttributeValueForObjectDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:67:1, + AttributeName: (dbc.Identifier) (len=15) "GenMsgCycleTime", + ObjectType: (dbc.ObjectType) (len=3) "BO_", + MessageID: (dbc.MessageID) 200, + SignalName: (dbc.Identifier) "", + NodeName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + IntValue: (int64) 100, + FloatValue: (float64) 0, + StringValue: (string) "" + }), + (*dbc.AttributeValueForObjectDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:68:1, + AttributeName: (dbc.Identifier) (len=14) "GenMsgSendType", + ObjectType: (dbc.ObjectType) (len=3) "BO_", + MessageID: (dbc.MessageID) 400, + SignalName: (dbc.Identifier) "", + NodeName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + IntValue: (int64) 0, + FloatValue: (float64) 0, + StringValue: (string) (len=6) "Cyclic" + }), + (*dbc.AttributeValueForObjectDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:69:1, + AttributeName: (dbc.Identifier) (len=15) "GenMsgCycleTime", + ObjectType: (dbc.ObjectType) (len=3) "BO_", + MessageID: (dbc.MessageID) 400, + SignalName: (dbc.Identifier) "", + NodeName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + IntValue: (int64) 100, + FloatValue: (float64) 0, + StringValue: (string) "" + }), + (*dbc.AttributeValueForObjectDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:70:1, + AttributeName: (dbc.Identifier) (len=14) "GenMsgSendType", + ObjectType: (dbc.ObjectType) (len=3) "BO_", + MessageID: (dbc.MessageID) 500, + SignalName: (dbc.Identifier) "", + NodeName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + IntValue: (int64) 0, + FloatValue: (float64) 0, + StringValue: (string) (len=7) "OnEvent" + }), + (*dbc.AttributeValueForObjectDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:71:1, + AttributeName: (dbc.Identifier) (len=9) "FieldType", + ObjectType: (dbc.ObjectType) (len=3) "SG_", + MessageID: (dbc.MessageID) 100, + SignalName: (dbc.Identifier) (len=7) "Command", + NodeName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + IntValue: (int64) 0, + FloatValue: (float64) 0, + StringValue: (string) (len=7) "Command" + }), + (*dbc.AttributeValueForObjectDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:72:1, + AttributeName: (dbc.Identifier) (len=9) "FieldType", + ObjectType: (dbc.ObjectType) (len=3) "SG_", + MessageID: (dbc.MessageID) 500, + SignalName: (dbc.Identifier) (len=8) "TestEnum", + NodeName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + IntValue: (int64) 0, + FloatValue: (float64) 0, + StringValue: (string) (len=8) "TestEnum" + }), + (*dbc.AttributeValueForObjectDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:73:1, + AttributeName: (dbc.Identifier) (len=16) "GenSigStartValue", + ObjectType: (dbc.ObjectType) (len=3) "SG_", + MessageID: (dbc.MessageID) 500, + SignalName: (dbc.Identifier) (len=8) "TestEnum", + NodeName: (dbc.Identifier) "", + EnvironmentVariableName: (dbc.Identifier) "", + IntValue: (int64) 2, + FloatValue: (float64) 0, + StringValue: (string) "" + }), + (*dbc.ValueDescriptionsDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:75:1, + ObjectType: (dbc.ObjectType) (len=3) "SG_", + MessageID: (dbc.MessageID) 100, + SignalName: (dbc.Identifier) (len=7) "Command", + EnvironmentVariableName: (dbc.Identifier) "", + ValueDescriptions: ([]dbc.ValueDescriptionDef) (len=3) { + (dbc.ValueDescriptionDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:75:18, + Value: (float64) 2, + Description: (string) (len=6) "Reboot" + }, + (dbc.ValueDescriptionDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:75:29, + Value: (float64) 1, + Description: (string) (len=4) "Sync" + }, + (dbc.ValueDescriptionDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:75:38, + Value: (float64) 0, + Description: (string) (len=4) "None" + } + } + }), + (*dbc.ValueDescriptionsDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:76:1, + ObjectType: (dbc.ObjectType) (len=3) "SG_", + MessageID: (dbc.MessageID) 500, + SignalName: (dbc.Identifier) (len=8) "TestEnum", + EnvironmentVariableName: (dbc.Identifier) "", + ValueDescriptions: ([]dbc.ValueDescriptionDef) (len=2) { + (dbc.ValueDescriptionDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:76:19, + Value: (float64) 2, + Description: (string) (len=3) "Two" + }, + (dbc.ValueDescriptionDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:76:27, + Value: (float64) 1, + Description: (string) (len=3) "One" + } + } + }), + (*dbc.ValueDescriptionsDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:77:1, + ObjectType: (dbc.ObjectType) (len=3) "SG_", + MessageID: (dbc.MessageID) 500, + SignalName: (dbc.Identifier) (len=14) "TestScaledEnum", + EnvironmentVariableName: (dbc.Identifier) "", + ValueDescriptions: ([]dbc.ValueDescriptionDef) (len=4) { + (dbc.ValueDescriptionDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:77:25, + Value: (float64) 3, + Description: (string) (len=3) "Six" + }, + (dbc.ValueDescriptionDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:77:33, + Value: (float64) 2, + Description: (string) (len=4) "Four" + }, + (dbc.ValueDescriptionDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:77:42, + Value: (float64) 1, + Description: (string) (len=3) "Two" + }, + (dbc.ValueDescriptionDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:77:50, + Value: (float64) 0, + Description: (string) (len=4) "Zero" + } + } + }), + (*dbc.ValueDescriptionsDef)({ + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:78:1, + ObjectType: (dbc.ObjectType) (len=3) "SG_", + MessageID: (dbc.MessageID) 500, + SignalName: (dbc.Identifier) (len=12) "TestBoolEnum", + EnvironmentVariableName: (dbc.Identifier) "", + ValueDescriptions: ([]dbc.ValueDescriptionDef) (len=2) { + (dbc.ValueDescriptionDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:78:23, + Value: (float64) 1, + Description: (string) (len=3) "One" + }, + (dbc.ValueDescriptionDef) { + Pos: (scanner.Position) ../../testdata/dbc/example/example.dbc:78:31, + Value: (float64) 0, + Description: (string) (len=4) "Zero" + } + } + }) +} diff --git a/pkg/can-go/tests/decode_test.go b/pkg/can-go/tests/decode_test.go new file mode 100644 index 0000000..9dd953e --- /dev/null +++ b/pkg/can-go/tests/decode_test.go @@ -0,0 +1,415 @@ +package decode_test + +import ( + "fmt" + "testing" + + can "github.com/fiskerinc/cloud-services/pkg/can-go" + "github.com/fiskerinc/cloud-services/pkg/can-go/internal/generate" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" +) + +type signal struct { + name string + value float64 + description string + unit string +} + +func getTestDatabase() descriptor.Database { + dbc := []byte(` +VERSION "" +NS_ : +BS_: +BU_: DBG DRIVER IO MOTOR SENSOR + +BO_ 1 EmptyMessage: 0 DBG +BO_ 100 DriverHeartbeat: 1 DRIVER + SG_ Command : 0|8@1+ (1,0) [0|0] "" SENSOR,MOTOR +BO_ 101 MotorCommand: 1 DRIVER + SG_ Steer : 0|4@1- (1,-5) [-5|5] "" MOTOR + SG_ Drive : 4|4@1+ (1,0) [0|9] "" MOTOR +BO_ 400 MotorStatus: 3 MOTOR + SG_ WheelError : 0|1@1+ (1,0) [0|0] "" DRIVER,IO + SG_ SpeedKph : 8|16@1+ (0.001,0) [0|0] "km/h" DRIVER,IO + +BO_ 200 SensorSonars: 8 SENSOR + SG_ Mux M : 0|4@1+ (1,0) [0|0] "" DRIVER,IO + SG_ ErrCount : 4|12@1+ (1,0) [0|0] "" DRIVER,IO + SG_ Left m0 : 16|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ Middle m0 : 28|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ Right m0 : 40|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ Rear m0 : 52|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ NoFiltLeft m1 : 16|12@1+ (0.1,0) [0|0] "" DBG + SG_ NoFiltMiddle m1 : 28|12@1+ (0.1,0) [0|0] "" DBG + SG_ NoFiltRight m1 : 40|12@1+ (0.1,0) [0|0] "" DBG + SG_ NoFiltRear m1 : 52|12@1+ (0.1,0) [0|0] "" DBG + +BO_ 500 IODebug: 6 IO + SG_ TestUnsigned : 0|8@1+ (1,0) [0|0] "" DBG + SG_ TestEnum : 8|6@1+ (1,0) [0|0] "" DBG + SG_ TestSigned : 16|8@1- (1,0) [0|0] "" DBG + SG_ TestFloat : 24|8@1+ (0.5,0) [0|0] "" DBG + SG_ TestBoolEnum : 32|1@1+ (1,0) [0|0] "" DBG + SG_ TestScaledEnum : 40|2@1+ (2,0) [0|6] "" DBG + +BO_ 1530 DisconnectState: 14 MOTOR + SG_ LockCountRearRight : 91|20@0+ (1,0) [0|1048575] "" IO + SG_ DisconnectStateRearRight : 95|4@0+ (1,0) [0|5] "" IO + SG_ CurrentRearRight : 79|16@0+ (1,0) [0|65535] "" IO + SG_ DisconnectStateRearRightTarget : 64|1@0+ (1,0) [0|1] "" IO + SG_ TargetSpeedRearRight : 63|15@0+ (0.125,-2048) [-2048|2047.875] "rad/s" IO + SG_ LockCountRearLeft : 35|20@0+ (1,0) [0|1048575] "" IO + SG_ DisconnectStateRearLeft : 39|4@0+ (1,0) [0|5] "" IO + SG_ CurrentRearLeft : 23|16@0+ (1,0) [0|65535] "" IO + SG_ DisconnectStateRearLeftTarget : 8|1@0+ (1,0) [0|1] "" IO + SG_ TargetSpeedRearLeft : 7|15@0+ (0.125,-2048) [-2048|2047.875] "rad/s" IO + +BO_ 1927 EvaporatorVariables: 14 SENSOR + SG_ EvaporatorAirTempIn : 96|8@0+ (0.4,-20) [-19.6|82.4] "degC" IO + SG_ CompressorSpeedActual : 80|16@0+ (1,-1) [0|8600] "RPM" IO + SG_ FBTargetCompressorSpeed : 64|16@0+ (1,-8600) [-8599|8600] "RPM" IO + SG_ FFTargetCompressorSpeed : 48|16@0+ (1,-1) [0|8600] "RPM" IO + SG_ FBTargetSuctionPressure : 32|16@0+ (0.05,0) [0.05|3276.8] "kPa" IO + SG_ FFTargetSuctionPressure : 16|16@0+ (0.05,0) [0.05|3276.8] "kPa" IO + SG_ ChillerCoolantOutTempTarget : 8|8@0+ (0.4,-20) [-19.6|82.4] "degC" IO + SG_ EvaporatorAirOutTempTarget : 0|8@0+ (0.4,-20) [-19.6|82.4] "degC" IO + +EV_ BrakeEngaged: 0 [0|1] "" 0 10 DUMMY_NODE_VECTOR0 Vector__XXX; +EV_ Torque: 1 [0|30000] "mNm" 500 16 DUMMY_NODE_VECTOR0 Vector__XXX; +CM_ EV_ BrakeEngaged "Brake fully engaged"; +CM_ BU_ DRIVER "The driver controller driving the car"; +CM_ BU_ MOTOR "The motor controller of the car"; +CM_ BU_ SENSOR "The sensor controller of the car"; +CM_ BO_ 100 "Sync message used to synchronize the controllers"; + +BA_DEF_ "BusType" STRING ; +BA_DEF_ BO_ "GenMsgSendType" ENUM "None","Cyclic","OnEvent"; +BA_DEF_ BO_ "GenMsgCycleTime" INT 0 0; +BA_DEF_ SG_ "FieldType" STRING ; +BA_DEF_ SG_ "GenSigStartValue" INT 0 10000; +BA_DEF_DEF_ "BusType" "CAN"; +BA_DEF_DEF_ "FieldType" ""; +BA_DEF_DEF_ "GenMsgCycleTime" 0; +BA_DEF_DEF_ "GenSigStartValue" 0; + +BA_ "GenMsgSendType" BO_ 1 0; +BA_ "GenMsgSendType" BO_ 100 1; +BA_ "GenMsgCycleTime" BO_ 100 1000; +BA_ "GenMsgSendType" BO_ 101 1; +BA_ "GenMsgCycleTime" BO_ 101 100; +BA_ "GenMsgSendType" BO_ 200 1; +BA_ "GenMsgCycleTime" BO_ 200 100; +BA_ "GenMsgSendType" BO_ 400 1; +BA_ "GenMsgCycleTime" BO_ 400 100; +BA_ "GenMsgSendType" BO_ 500 2; +BA_ "FieldType" SG_ 100 Command "Command"; +BA_ "FieldType" SG_ 500 TestEnum "TestEnum"; +BA_ "GenSigStartValue" SG_ 500 TestEnum 2; + +VAL_ 100 Command 2 "Reboot" 1 "Sync" 0 "None" ; +VAL_ 500 TestEnum 2 "Two" 1 "One" ; +VAL_ 500 TestScaledEnum 3 "Six" 2 "Four" 1 "Two" 0 "Zero" ; +VAL_ 500 TestBoolEnum 1 "One" 0 "Zero" ; +VAL_ 1530 DisconnectStateRearRight 0 "Undefined" 1 "Locked" 2 "Unlocked" 3 "Locking" 4 "Unlocking" 5 "Faulted" ; +VAL_ 1530 DisconnectStateRearLeft 0 "Undefined" 1 "Locked" 2 "Unlocked" 3 "Locking" 4 "Unlocking" 5 "Faulted" ; + +`) + c, _ := generate.Compile("test.dbc", dbc) + db := *c.Database + return db +} + +func TestDecodeEvaporatorVariables(t *testing.T) { + db := getTestDatabase() + message, _ := db.Message(uint32(1927)) + + canDataHexString := "008232204e027600ca4b0007d296" + + payload, err := can.PayloadFromHex(canDataHexString) + if err != nil { + t.Errorf("err = %v; want nil", err) + } + + expected := []signal{ + { + name: "EvaporatorAirOutTempTarget", + value: 6.0, + description: "", + unit: "degC", + }, + { + name: "ChillerCoolantOutTempTarget", + value: -10.0, + description: "", + unit: "degC", + }, + { + name: "FFTargetSuctionPressure", + value: 206.75, + description: "", + unit: "kPa", + }, + { + name: "FBTargetSuctionPressure", + value: 15.75, + description: "", + unit: "kPa", + }, + { + name: "FFTargetCompressorSpeed", + value: 100, + description: "", + unit: "RPM", + }, + { + name: "FBTargetCompressorSpeed", + value: 1000, + description: "", + unit: "RPM", + }, + { + name: "CompressorSpeedActual", + value: 1000, + description: "", + unit: "RPM", + }, + { + name: "EvaporatorAirTempIn", + value: 10.0, + description: "", + unit: "degC", + }, + } + + expectedMap := make(map[string]signal) + for _, s := range expected { + expectedMap[s.name] = s + } + + for _, signal := range message.Signals { + value := signal.UnmarshalPhysicalPayload(&payload) + valueDesc, _ := signal.UnmarshalValueDescriptionPayload(&payload) + unit := signal.Unit + name := signal.Name + + if value != expectedMap[name].value { + t.Errorf("signal[%s] value = %f ; expected: %f", name, value, expectedMap[name].value) + } + + if valueDesc != expectedMap[name].description { + t.Errorf("signal[%s] value = %s ; expected: %s", name, valueDesc, expectedMap[name].description) + } + + if unit != expectedMap[name].unit { + t.Errorf("signal[%s] value = %s ; expected: %s", name, unit, expectedMap[name].unit) + } + } + + for _, signal := range message.Decode(&payload) { + value := signal.Value + valueDesc := signal.Description + name := signal.Signal.Name + + if value != expectedMap[name].value { + t.Errorf("signal[%s] value = %f ; expected: %f", name, value, expectedMap[name].value) + } + + if valueDesc != expectedMap[name].description { + t.Errorf("signal[%s] value = %s ; expected: %s", name, valueDesc, expectedMap[name].description) + } + } +} + +func TestDecodeDisconnectState(t *testing.T) { + byteStringHex := "8000000420061880000005200600" + + p, err := can.PayloadFromHex(byteStringHex) + if err != nil { + fmt.Println(err) + } + + expected := []signal{ + { + + name: "TargetSpeedRearLeft", + value: 0.0, + description: "", + unit: "rad/s", + }, + { + + name: "DisconnectStateRearLeftTarget", + value: 0, + description: "", + unit: "", + }, + { + + name: "CurrentRearLeft", + value: 4, + description: "", + unit: "", + }, + { + + name: "DisconnectStateRearLeft", + value: 2, + description: "Unlocked", + unit: "", + }, + { + + name: "LockCountRearLeft", + value: 1560, + description: "", + unit: "", + }, + { + + name: "TargetSpeedRearRight", + value: 0, + description: "", + unit: "rad/s", + }, + { + + name: "DisconnectStateRearRightTarget", + value: 0, + description: "", + unit: "", + }, + { + + name: "CurrentRearRight", + value: 5, + description: "", + unit: "", + }, + { + + name: "DisconnectStateRearRight", + value: 2, + description: "Unlocked", + unit: "", + }, + { + + name: "LockCountRearRight", + value: 1536, + description: "", + unit: "", + }, + } + + expectedMap := make(map[string]signal) + for _, s := range expected { + expectedMap[s.name] = s + } + + db := getTestDatabase() + message, _ := db.Message(uint32(1530)) + for _, signal := range message.Signals { + value := signal.UnmarshalPhysicalPayload(&p) + valueDesc, _ := signal.UnmarshalValueDescriptionPayload(&p) + unit := signal.Unit + name := signal.Name + + if value != expectedMap[name].value { + t.Errorf("signal[%s] value = %f ; expected: %f", name, value, expectedMap[name].value) + } + + if valueDesc != expectedMap[name].description { + t.Errorf("signal[%s] value = %s ; expected: %s", name, valueDesc, expectedMap[name].description) + } + + if unit != expectedMap[name].unit { + t.Errorf("signal[%s] value = %s ; expected: %s", name, unit, expectedMap[name].unit) + } + } +} + +func TestDecodeSensorSonarsData(t *testing.T) { + data := can.Data{0x01, 0x01, 0x01, 0x02, 0x01, 0x00} + payload := can.Payload{Data: data[:]} + + db := getTestDatabase() + message, _ := db.Message(uint32(500)) + fmt.Println(message.Length) + + for _, signal := range message.Signals { + value := signal.UnmarshalPhysicalPayload(&payload) + valueDesc, _ := signal.UnmarshalValueDescriptionPayload(&payload) + + valueFromData := signal.UnmarshalPhysical(data) + descFromData, _ := signal.UnmarshalValueDescription(data) + name := signal.Name + + if value != valueFromData { + t.Errorf("signal[%s] value = %f ; expected: %f", name, value, valueFromData) + } + + if valueDesc != descFromData { + t.Errorf("signal[%s] value = %s ; expected: %s", name, valueDesc, descFromData) + } + } +} + +func TestMessageDecodeSensorSonarsData(t *testing.T) { + data := can.Data{0x01, 0x01, 0x01, 0x02, 0x01, 0x00} + payload := can.Payload{Data: data[:]} + + db := getTestDatabase() + message, _ := db.Message(uint32(500)) + fmt.Println(message.Length) + + decodedSignals := message.Decode(&payload) + for _, signal := range decodedSignals { + value := signal.Value + valueDesc := signal.Description + name := signal.Signal.Name + + valueFromData := signal.Signal.UnmarshalPhysical(data) + descFromData, _ := signal.Signal.UnmarshalValueDescription(data) + + if value != valueFromData { + t.Errorf("signal[%s] value = %f ; expected: %f", name, value, valueFromData) + } + + if valueDesc != descFromData { + t.Errorf("signal[%s] value = %s ; expected: %s", name, valueDesc, descFromData) + } + } +} + +func BenchmarkDecodeData(b *testing.B) { + db := getTestDatabase() + message, _ := db.Message(uint32(500)) + decodeSignal := func() { + data := can.Data{0x01, 0x01, 0x01, 0x02, 0x01, 0x00} + for _, signal := range message.Signals { + _ = signal.UnmarshalPhysical(data) + _, _ = signal.UnmarshalValueDescription(data) + } + } + for i := 0; i < b.N; i++ { + decodeSignal() + } +} + +func BenchmarkDecodePayload(b *testing.B) { + // {0x01, 0x01, 0x01, 0x02, 0x01, 0x00} + + db := getTestDatabase() + message, _ := db.Message(uint32(500)) + decodeSignal := func() { + data := can.Payload{Data: []byte{0x01, 0x01, 0x01, 0x02, 0x01, 0x00}} + for _, signal := range message.Signals { + _ = signal.UnmarshalPhysicalPayload(&data) + _, _ = signal.UnmarshalValueDescriptionPayload(&data) + } + } + for i := 0; i < b.N; i++ { + decodeSignal() + } +} diff --git a/pkg/can-go/tools.go b/pkg/can-go/tools.go new file mode 100644 index 0000000..3c324cb --- /dev/null +++ b/pkg/can-go/tools.go @@ -0,0 +1,8 @@ +// +build tools + +package tools + +import ( + _ "github.com/golang/mock/mockgen" + _ "golang.org/x/tools/cmd/stringer" +) diff --git a/pkg/can-go/tools/commitlint/.commitlintrc.js b/pkg/can-go/tools/commitlint/.commitlintrc.js new file mode 100644 index 0000000..92e3aec --- /dev/null +++ b/pkg/can-go/tools/commitlint/.commitlintrc.js @@ -0,0 +1,8 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: { + // Treat as warning until Dependabot supports commitlint. + // https://github.com/dependabot/dependabot-core/issues/2445 + "body-max-line-length": [1, "always", 100], + } +}; diff --git a/pkg/can-go/tools/commitlint/package.json b/pkg/can-go/tools/commitlint/package.json new file mode 100644 index 0000000..2f66822 --- /dev/null +++ b/pkg/can-go/tools/commitlint/package.json @@ -0,0 +1,6 @@ +{ + "devDependencies": { + "@commitlint/cli": "^11.0.0", + "@commitlint/config-conventional": "^11.0.0" + } +} diff --git a/pkg/can-go/tools/commitlint/rules.mk b/pkg/can-go/tools/commitlint/rules.mk new file mode 100644 index 0000000..0d3eb3b --- /dev/null +++ b/pkg/can-go/tools/commitlint/rules.mk @@ -0,0 +1,16 @@ +commitlint_cwd := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +commitlint := $(commitlint_cwd)/node_modules/.bin/commitlint + +$(commitlint): $(commitlint_cwd)/package.json + $(info [commitlint] installing package...) + @cd $(commitlint_cwd) && npm install --no-save --no-audit &> /dev/null + @touch $@ + +.PHONY: commitlint +commitlint: $(commitlint_cwd)/.commitlintrc.js $(commitlint) + $(info [$@] linting commit messages...) + @git fetch --tags + @NODE_PATH=$(commitlint_cwd)/node_modules $(commitlint) \ + --config $< \ + --from origin/master \ + --to HEAD diff --git a/pkg/can-go/tools/git-verify-nodiff/git-verify-nodiff.bash b/pkg/can-go/tools/git-verify-nodiff/git-verify-nodiff.bash new file mode 100755 index 0000000..0dd1f0c --- /dev/null +++ b/pkg/can-go/tools/git-verify-nodiff/git-verify-nodiff.bash @@ -0,0 +1,9 @@ +#!/bin/bash + +set -euo pipefail + +if [[ -n $(git status --porcelain) ]]; then + echo "Staging area is dirty, please add all files created by the build to .gitignore" + git diff --patch + exit 1 +fi diff --git a/pkg/can-go/tools/git-verify-nodiff/rules.mk b/pkg/can-go/tools/git-verify-nodiff/rules.mk new file mode 100644 index 0000000..9e3cf98 --- /dev/null +++ b/pkg/can-go/tools/git-verify-nodiff/rules.mk @@ -0,0 +1,7 @@ +git_verify_nodiff_cwd := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +git_verify_nodiff := $(git_verify_nodiff_cwd)/git-verify-nodiff.bash + +.PHONY: git-verify-nodiff +git-verify-nodiff: + @echo verifying that git has no diff... + @$(git_verify_nodiff) diff --git a/pkg/can-go/tools/golangci-lint/rules.mk b/pkg/can-go/tools/golangci-lint/rules.mk new file mode 100644 index 0000000..dd539a3 --- /dev/null +++ b/pkg/can-go/tools/golangci-lint/rules.mk @@ -0,0 +1,24 @@ +golangci_lint_cwd := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +golangci_lint_version := 1.37.0 +golangci_lint := $(golangci_lint_cwd)/$(golangci_lint_version)/golangci-lint + +ifeq ($(shell uname),Linux) +golangci_lint_archive_url := https://github.com/golangci/golangci-lint/releases/download/v${golangci_lint_version}/golangci-lint-${golangci_lint_version}-linux-amd64.tar.gz +else ifeq ($(shell uname),Darwin) +golangci_lint_archive_url := https://github.com/golangci/golangci-lint/releases/download/v${golangci_lint_version}/golangci-lint-${golangci_lint_version}-darwin-amd64.tar.gz +else +$(error unsupported OS: $(shell uname)) +endif + +$(golangci_lint): + $(info building golangci-lint...) + @mkdir -p $(dir $@) + @curl -sSL $(golangci_lint_archive_url) -o - | \ + tar -xz --directory $(dir $@) --strip-components 1 + @chmod +x $@ + @touch $@ + +.PHONY: go-lint +go-lint: $(golangci_lint) + $(info linting Go code with golangci-lint...) + @$(golangci_lint) run diff --git a/pkg/can-go/tools/goreview/rules.mk b/pkg/can-go/tools/goreview/rules.mk new file mode 100644 index 0000000..426850e --- /dev/null +++ b/pkg/can-go/tools/goreview/rules.mk @@ -0,0 +1,18 @@ +goreview_cwd := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +goreview_version := 0.15.0 +goreview := $(goreview_cwd)/$(goreview_version)/goreview + +goreview_archive_url := https://github.com/einride/goreview/releases/download/v$(goreview_version)/goreview_$(goreview_version)_$(shell uname)_$(shell uname -m).tar.gz + +$(goreview): $(goreview_cwd)/rules.mk + $(info [goreview] fetching $(goreview_version) binary...) + @mkdir -p $(dir $@) + @curl -sSL $(goreview_archive_url) -o - | tar -xz --directory $(dir $@) + @chmod +x $@ + @touch $@ + +# go-review: review Go code for Einride-specific conventions +.PHONY: go-review +go-review: $(goreview) + $(info [$@] reviewing Go code for Einride-specific conventions...) + @$(goreview) -c 1 ./... diff --git a/pkg/can-go/tools/semantic-release/.releaserc.yaml b/pkg/can-go/tools/semantic-release/.releaserc.yaml new file mode 100644 index 0000000..1a68877 --- /dev/null +++ b/pkg/can-go/tools/semantic-release/.releaserc.yaml @@ -0,0 +1,19 @@ +plugins: + - - "@semantic-release/commit-analyzer" + - preset: "conventionalcommits" + releaseRules: + # Given Go v2+ conventions we disable major releases on + # breaking changes and leave it up to the developer + # to make major releases + - breaking: true + release: "minor" + - "@semantic-release/release-notes-generator" + - "@semantic-release/github" + +branches: ["master"] +# github plugin is the only one running this step and we're not interested +# in its updates to PR and issues +success: false +# github plugin is the only one running this step and we're not interested +# the issues it creates due to failed releases +fail: false diff --git a/pkg/can-go/tools/semantic-release/package.json b/pkg/can-go/tools/semantic-release/package.json new file mode 100644 index 0000000..3069fdc --- /dev/null +++ b/pkg/can-go/tools/semantic-release/package.json @@ -0,0 +1,8 @@ +{ + "devDependencies": { + "semantic-release": "^17.3.0", + "@semantic-release/github": "^7.2.0", + "@semantic-release/release-notes-generator": "^9.0.1", + "conventional-changelog-conventionalcommits": "^4.5.0" + } +} diff --git a/pkg/can-go/tools/semantic-release/rules.mk b/pkg/can-go/tools/semantic-release/rules.mk new file mode 100644 index 0000000..2992c1a --- /dev/null +++ b/pkg/can-go/tools/semantic-release/rules.mk @@ -0,0 +1,12 @@ +semantic_release_cwd := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +semantic_release := $(semantic_release_cwd)/node_modules/.bin/semantic-release + +$(semantic_release): $(semantic_release_cwd)/package.json + $(info [semantic-release] installing packages...) + @cd $(semantic_release_cwd) && npm install --no-save --no-audit --ignore-scripts &> /dev/null + @touch $@ + +.PHONY: semantic-release +semantic-release: $(semantic_release_cwd)/.releaserc.yaml $(semantic_release) + $(info [$@] creating release...) + @cd $(semantic_release_cwd) && $(semantic_release) diff --git a/pkg/can-go/tools/stringer/go.mod b/pkg/can-go/tools/stringer/go.mod new file mode 100644 index 0000000..ecf3e25 --- /dev/null +++ b/pkg/can-go/tools/stringer/go.mod @@ -0,0 +1,5 @@ +module tools/stringer + +go 1.13 + +require golang.org/x/tools v0.0.0-20200609164405-eb789aa7ce50 diff --git a/pkg/can-go/tools/stringer/go.sum b/pkg/can-go/tools/stringer/go.sum new file mode 100644 index 0000000..070ed99 --- /dev/null +++ b/pkg/can-go/tools/stringer/go.sum @@ -0,0 +1,20 @@ +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200609164405-eb789aa7ce50 h1:59syOWj4+Fl+op4LL8fX1kO7HmbdEWfxlw4tcGvH+y0= +golang.org/x/tools v0.0.0-20200609164405-eb789aa7ce50/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/can-go/tools/stringer/rules.mk b/pkg/can-go/tools/stringer/rules.mk new file mode 100644 index 0000000..8a75030 --- /dev/null +++ b/pkg/can-go/tools/stringer/rules.mk @@ -0,0 +1,8 @@ +stringer_cwd := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +stringer := $(stringer_cwd)/bin/stringer +PATH := $(PATH):$(dir $(stringer)) + +$(stringer): $(stringer_cwd)/go.mod + @echo building stringer... + @cd $(stringer_cwd) && go build -o $@ golang.org/x/tools/cmd/stringer + @cd $(stringer_cwd) && go mod tidy diff --git a/pkg/can-go/tools/stringer/tool.go b/pkg/can-go/tools/stringer/tool.go new file mode 100644 index 0000000..f836ab8 --- /dev/null +++ b/pkg/can-go/tools/stringer/tool.go @@ -0,0 +1,5 @@ +//+build tool + +package tool + +import _ "golang.org/x/tools/cmd/stringer" diff --git a/pkg/carcommand/wake_up.go b/pkg/carcommand/wake_up.go index 1ea1242..cadca0f 100644 --- a/pkg/carcommand/wake_up.go +++ b/pkg/carcommand/wake_up.go @@ -3,13 +3,13 @@ package carcommand import ( "context" - "fiskerinc.com/modules/cache" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/grpc/sms" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/utils/envtool" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/cache" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/grpc/sms" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/pkg/errors" ) diff --git a/pkg/clickhouse/clickhouse.go b/pkg/clickhouse/clickhouse.go index c6652d8..090d163 100644 --- a/pkg/clickhouse/clickhouse.go +++ b/pkg/clickhouse/clickhouse.go @@ -6,8 +6,8 @@ import ( "strings" "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/ClickHouse/clickhouse-go/v2" "github.com/ClickHouse/clickhouse-go/v2/lib/driver" diff --git a/pkg/clickhouse/clickhouse_test.go b/pkg/clickhouse/clickhouse_test.go index 48b42fa..2b6212f 100644 --- a/pkg/clickhouse/clickhouse_test.go +++ b/pkg/clickhouse/clickhouse_test.go @@ -3,8 +3,8 @@ package clickhouse_test import ( "testing" - "fiskerinc.com/modules/clickhouse" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/clickhouse" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestRetrieveDefaultFilters(t *testing.T) { diff --git a/pkg/clickhouse/options.go b/pkg/clickhouse/options.go index e0c35c4..6688fba 100644 --- a/pkg/clickhouse/options.go +++ b/pkg/clickhouse/options.go @@ -3,7 +3,7 @@ package clickhouse import ( "net/http" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/gorilla/schema" ) diff --git a/pkg/common/actionlogger/actionlogger.go b/pkg/common/actionlogger/actionlogger.go index 5213b6d..d3a6982 100644 --- a/pkg/common/actionlogger/actionlogger.go +++ b/pkg/common/actionlogger/actionlogger.go @@ -3,7 +3,7 @@ package actionlogger import ( "time" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" "github.com/google/uuid" ) diff --git a/pkg/common/apitoken.go b/pkg/common/apitoken.go index f7758c0..0fb900b 100644 --- a/pkg/common/apitoken.go +++ b/pkg/common/apitoken.go @@ -3,7 +3,7 @@ package common import ( "fmt" "time" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type APIToken struct { diff --git a/pkg/common/authproviders/authproviders.go b/pkg/common/authproviders/authproviders.go index e533c1c..b562748 100644 --- a/pkg/common/authproviders/authproviders.go +++ b/pkg/common/authproviders/authproviders.go @@ -1,6 +1,6 @@ package authproviders -import "fiskerinc.com/modules/utils/envtool" +import "github.com/fiskerinc/cloud-services/pkg/utils/envtool" const ( Default = "Default" // This is for any provider diff --git a/pkg/common/binary_hex._test.go b/pkg/common/binary_hex._test.go index 23c083f..9cbcf14 100644 --- a/pkg/common/binary_hex._test.go +++ b/pkg/common/binary_hex._test.go @@ -4,8 +4,8 @@ import ( "encoding/json" "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) type testHexBinary struct { diff --git a/pkg/common/can.go b/pkg/common/can.go index c9daadd..8753c76 100644 --- a/pkg/common/can.go +++ b/pkg/common/can.go @@ -3,7 +3,7 @@ package common import ( "time" - "fiskerinc.com/modules/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" ) // CANFrame provides struct for can bus messages diff --git a/pkg/common/car.go b/pkg/common/car.go index d7b10a3..976ea5e 100644 --- a/pkg/common/car.go +++ b/pkg/common/car.go @@ -3,7 +3,7 @@ package common import ( "fmt" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) const ( diff --git a/pkg/common/car_data.go b/pkg/common/car_data.go index 2ed2f34..c803456 100644 --- a/pkg/common/car_data.go +++ b/pkg/common/car_data.go @@ -3,7 +3,7 @@ package common import ( "encoding/json" - "fiskerinc.com/modules/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" "github.com/pkg/errors" ) diff --git a/pkg/common/car_ecu.go b/pkg/common/car_ecu.go index 3376762..c436a71 100644 --- a/pkg/common/car_ecu.go +++ b/pkg/common/car_ecu.go @@ -5,7 +5,7 @@ import ( "encoding/hex" "fmt" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type CarECU struct { diff --git a/pkg/common/car_setting.go b/pkg/common/car_setting.go index d939566..127b5cb 100644 --- a/pkg/common/car_setting.go +++ b/pkg/common/car_setting.go @@ -1,7 +1,7 @@ package common import ( - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type CarSetting struct { diff --git a/pkg/common/car_update.go b/pkg/common/car_update.go index 0e97c8c..2e28ad8 100644 --- a/pkg/common/car_update.go +++ b/pkg/common/car_update.go @@ -2,7 +2,7 @@ package common import ( "fmt" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) // CarUpdate schema diff --git a/pkg/common/car_update_progress_test.go b/pkg/common/car_update_progress_test.go index 1d2ef81..ef26682 100644 --- a/pkg/common/car_update_progress_test.go +++ b/pkg/common/car_update_progress_test.go @@ -3,8 +3,8 @@ package common_test import ( "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestCarUpdateProgressCombine(t *testing.T) { diff --git a/pkg/common/car_update_status.go b/pkg/common/car_update_status.go index 2ba4670..a355a9f 100644 --- a/pkg/common/car_update_status.go +++ b/pkg/common/car_update_status.go @@ -1,7 +1,7 @@ package common import ( - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) // CarUpdateStatus database model for logging history of car updates diff --git a/pkg/common/certificate.go b/pkg/common/certificate.go index ff437af..b676cc8 100644 --- a/pkg/common/certificate.go +++ b/pkg/common/certificate.go @@ -6,7 +6,7 @@ import ( "fmt" "time" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) // CertificateRequest schema diff --git a/pkg/common/certificate_test.go b/pkg/common/certificate_test.go index 77d9a69..2a4d9a9 100644 --- a/pkg/common/certificate_test.go +++ b/pkg/common/certificate_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/stretchr/testify/assert" ) diff --git a/pkg/common/configuration.go b/pkg/common/configuration.go index 7e52698..6033e39 100644 --- a/pkg/common/configuration.go +++ b/pkg/common/configuration.go @@ -1,7 +1,7 @@ package common import ( - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type Configuration struct { diff --git a/pkg/common/consumer_payload.go b/pkg/common/consumer_payload.go index 4dd0e4c..e147938 100644 --- a/pkg/common/consumer_payload.go +++ b/pkg/common/consumer_payload.go @@ -5,8 +5,8 @@ import ( "log" "time" - "fiskerinc.com/modules/grpc/kafka_grpc" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/google/uuid" ) diff --git a/pkg/common/consumer_payload_input.go b/pkg/common/consumer_payload_input.go index d819758..ee57008 100644 --- a/pkg/common/consumer_payload_input.go +++ b/pkg/common/consumer_payload_input.go @@ -3,8 +3,8 @@ package common import ( "encoding/json" - "fiskerinc.com/modules/grpc/kafka_grpc" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/logger" ) func DepotRouteTRexToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_DepotPayload { diff --git a/pkg/common/consumer_payload_test.go b/pkg/common/consumer_payload_test.go index 5e92ffb..9856869 100644 --- a/pkg/common/consumer_payload_test.go +++ b/pkg/common/consumer_payload_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - "fiskerinc.com/modules/grpc/kafka_grpc" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" ) func TestDepotRouteHMIPayload(t *testing.T) { diff --git a/pkg/common/dbc_desc.go b/pkg/common/dbc_desc.go index 3b78f73..792a0af 100644 --- a/pkg/common/dbc_desc.go +++ b/pkg/common/dbc_desc.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - can "github.com/Fisker-Inc/project-ai-can-go/pkg/descriptor" + can "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" ) // SignalDesc is copied from project-ai-can-go and modified for local needs. diff --git a/pkg/common/dbc_desc_test.go b/pkg/common/dbc_desc_test.go index c19c0c7..6d5a802 100644 --- a/pkg/common/dbc_desc_test.go +++ b/pkg/common/dbc_desc_test.go @@ -3,8 +3,8 @@ package common_test import ( "testing" - "fiskerinc.com/modules/common" - "github.com/Fisker-Inc/project-ai-can-go/pkg/descriptor" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" "github.com/stretchr/testify/assert" ) diff --git a/pkg/common/driver.go b/pkg/common/driver.go index e329250..97fbc05 100644 --- a/pkg/common/driver.go +++ b/pkg/common/driver.go @@ -3,7 +3,7 @@ package common import ( "fmt" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) // DriverAccount model diff --git a/pkg/common/ecc_keys.go b/pkg/common/ecc_keys.go index 4a442b9..617c3c0 100644 --- a/pkg/common/ecc_keys.go +++ b/pkg/common/ecc_keys.go @@ -1,7 +1,7 @@ package common import ( - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type ECCKeys struct { diff --git a/pkg/common/ecu_dtc.go b/pkg/common/ecu_dtc.go index e669f42..f3df4b4 100644 --- a/pkg/common/ecu_dtc.go +++ b/pkg/common/ecu_dtc.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type AddDTCRequest struct { diff --git a/pkg/common/ecu_list.go b/pkg/common/ecu_list.go index 6928ef6..7d06a33 100644 --- a/pkg/common/ecu_list.go +++ b/pkg/common/ecu_list.go @@ -3,8 +3,8 @@ package common import ( "strings" - "fiskerinc.com/modules/utils/envtool" - "fiskerinc.com/modules/utils/whereami" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/utils/whereami" ) var EcuMap = map[string]string{ diff --git a/pkg/common/file_key.go b/pkg/common/file_key.go index 432e993..e634205 100644 --- a/pkg/common/file_key.go +++ b/pkg/common/file_key.go @@ -3,7 +3,7 @@ package common import ( b64 "encoding/base64" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type FileKey struct { diff --git a/pkg/common/hmi_rx_test.go b/pkg/common/hmi_rx_test.go index 59cc91a..e2b2cfe 100644 --- a/pkg/common/hmi_rx_test.go +++ b/pkg/common/hmi_rx_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/common/dbbasemodel" - "fiskerinc.com/modules/common/handlers" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/handlers" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestHMIRXMessages(t *testing.T) { diff --git a/pkg/common/hmi_tx_test.go b/pkg/common/hmi_tx_test.go index 377913b..457403b 100644 --- a/pkg/common/hmi_tx_test.go +++ b/pkg/common/hmi_tx_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/common/dbbasemodel" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestHMITXMessages(t *testing.T) { diff --git a/pkg/common/manifestfingerprintparams/manifestfingerprintparams.go b/pkg/common/manifestfingerprintparams/manifestfingerprintparams.go index 223651a..9ca6207 100644 --- a/pkg/common/manifestfingerprintparams/manifestfingerprintparams.go +++ b/pkg/common/manifestfingerprintparams/manifestfingerprintparams.go @@ -4,7 +4,7 @@ import ( "sync" "time" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" ) var fpParams FingerprintParamer diff --git a/pkg/common/mobile_rx_test.go b/pkg/common/mobile_rx_test.go index 6f7fc3d..9808a5b 100644 --- a/pkg/common/mobile_rx_test.go +++ b/pkg/common/mobile_rx_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/common/dbbasemodel" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/google/uuid" ) diff --git a/pkg/common/mobile_tx_test.go b/pkg/common/mobile_tx_test.go index 0cac9f4..70c90e8 100644 --- a/pkg/common/mobile_tx_test.go +++ b/pkg/common/mobile_tx_test.go @@ -4,11 +4,11 @@ import ( "testing" "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/common/dbbasemodel" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/google/uuid" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" ) func TestMobileTXMessages(t *testing.T) { diff --git a/pkg/common/order_updated.go b/pkg/common/order_updated.go index 9cf2a85..f1a631c 100644 --- a/pkg/common/order_updated.go +++ b/pkg/common/order_updated.go @@ -5,7 +5,7 @@ import ( "encoding/xml" "time" - "fiskerinc.com/modules/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" ) type OrderUpdated struct { diff --git a/pkg/common/product.go b/pkg/common/product.go index d9390d6..eb65db9 100644 --- a/pkg/common/product.go +++ b/pkg/common/product.go @@ -1,7 +1,7 @@ package common import ( - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type Product struct { diff --git a/pkg/common/rate_plan_mobile.go b/pkg/common/rate_plan_mobile.go index 3967448..130d40b 100644 --- a/pkg/common/rate_plan_mobile.go +++ b/pkg/common/rate_plan_mobile.go @@ -1,7 +1,7 @@ package common import ( - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type RatePlanTMobile struct { diff --git a/pkg/common/signed_image.go b/pkg/common/signed_image.go index 4a75bd7..7e1a835 100644 --- a/pkg/common/signed_image.go +++ b/pkg/common/signed_image.go @@ -1,7 +1,7 @@ package common import ( - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type SignedImage struct { diff --git a/pkg/common/subscription.go b/pkg/common/subscription.go index 68566db..02dcda6 100644 --- a/pkg/common/subscription.go +++ b/pkg/common/subscription.go @@ -3,7 +3,7 @@ package common import ( "time" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" "github.com/google/uuid" ) diff --git a/pkg/common/subscription_configuration.go b/pkg/common/subscription_configuration.go index 3a39407..1da67f8 100644 --- a/pkg/common/subscription_configuration.go +++ b/pkg/common/subscription_configuration.go @@ -3,7 +3,7 @@ package common import ( "fmt" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" "github.com/google/uuid" ) diff --git a/pkg/common/subscription_feature.go b/pkg/common/subscription_feature.go index fbf9a0f..2140038 100644 --- a/pkg/common/subscription_feature.go +++ b/pkg/common/subscription_feature.go @@ -3,7 +3,7 @@ package common import ( "fmt" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" "github.com/google/uuid" ) diff --git a/pkg/common/subscription_package.go b/pkg/common/subscription_package.go index 2aab6e0..4d7e62d 100644 --- a/pkg/common/subscription_package.go +++ b/pkg/common/subscription_package.go @@ -3,7 +3,7 @@ package common import ( "fmt" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" "github.com/google/uuid" ) diff --git a/pkg/common/subscription_type.go b/pkg/common/subscription_type.go index db2fabd..3c98bce 100644 --- a/pkg/common/subscription_type.go +++ b/pkg/common/subscription_type.go @@ -3,7 +3,7 @@ package common import ( "time" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" "github.com/google/uuid" ) diff --git a/pkg/common/supplier_account.go b/pkg/common/supplier_account.go index 08aa0f7..55c80b5 100644 --- a/pkg/common/supplier_account.go +++ b/pkg/common/supplier_account.go @@ -5,7 +5,7 @@ import ( "strings" "time" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type SupplierAccount struct { diff --git a/pkg/common/supplier_account_test.go b/pkg/common/supplier_account_test.go index eb39d94..b10b354 100644 --- a/pkg/common/supplier_account_test.go +++ b/pkg/common/supplier_account_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/common/dbbasemodel" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestSupplierAccount(t *testing.T) { diff --git a/pkg/common/supplier_organizations.go b/pkg/common/supplier_organizations.go index 2d6fd88..3742821 100644 --- a/pkg/common/supplier_organizations.go +++ b/pkg/common/supplier_organizations.go @@ -3,7 +3,7 @@ package common import ( "fmt" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type SupplierOrganization struct { diff --git a/pkg/common/supplier_signing_certs.go b/pkg/common/supplier_signing_certs.go index dd61795..79c09a5 100644 --- a/pkg/common/supplier_signing_certs.go +++ b/pkg/common/supplier_signing_certs.go @@ -1,7 +1,7 @@ package common import ( - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type SupplierSigningCert struct { diff --git a/pkg/common/swversion_rxswin.go b/pkg/common/swversion_rxswin.go index fbeeb79..1fc1c7d 100644 --- a/pkg/common/swversion_rxswin.go +++ b/pkg/common/swversion_rxswin.go @@ -1,7 +1,7 @@ package common import ( - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type SwVersionRxSwin struct { diff --git a/pkg/common/sym_keys.go b/pkg/common/sym_keys.go index a02f774..61c9143 100644 --- a/pkg/common/sym_keys.go +++ b/pkg/common/sym_keys.go @@ -1,7 +1,7 @@ package common import ( - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type SymKeys struct { diff --git a/pkg/common/tests/trex_config_test.go b/pkg/common/tests/trex_config_test.go index 9d2c2d7..7244f3b 100644 --- a/pkg/common/tests/trex_config_test.go +++ b/pkg/common/tests/trex_config_test.go @@ -3,8 +3,8 @@ package tests import ( "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/validator" ) func TestCANBusValidator(t *testing.T) { diff --git a/pkg/common/trex_config.go b/pkg/common/trex_config.go index 4a14195..2888142 100644 --- a/pkg/common/trex_config.go +++ b/pkg/common/trex_config.go @@ -4,7 +4,7 @@ import ( "encoding/json" "strings" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/go-playground/validator/v10" "github.com/pkg/errors" "go.mongodb.org/mongo-driver/bson" diff --git a/pkg/common/trex_config_test.go b/pkg/common/trex_config_test.go index b6dc4e9..e1768cd 100644 --- a/pkg/common/trex_config_test.go +++ b/pkg/common/trex_config_test.go @@ -3,7 +3,7 @@ package common import ( "testing" "reflect" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" ) func TestStringToTrexLogLevel(t *testing.T){ diff --git a/pkg/common/trex_error.go b/pkg/common/trex_error.go index 31ed078..ac2ed96 100644 --- a/pkg/common/trex_error.go +++ b/pkg/common/trex_error.go @@ -3,7 +3,7 @@ package common import ( "encoding/json" - "fiskerinc.com/modules/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" "github.com/pkg/errors" ) diff --git a/pkg/common/trex_log.go b/pkg/common/trex_log.go index 4491791..98f13c8 100644 --- a/pkg/common/trex_log.go +++ b/pkg/common/trex_log.go @@ -3,7 +3,7 @@ package common import ( "encoding/json" - "fiskerinc.com/modules/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" "github.com/pkg/errors" ) diff --git a/pkg/common/trex_rx_test.go b/pkg/common/trex_rx_test.go index e30f771..1d6bd28 100644 --- a/pkg/common/trex_rx_test.go +++ b/pkg/common/trex_rx_test.go @@ -3,10 +3,10 @@ package common_test import ( "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/common/handlers" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/common/handlers" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" ) func TestTRexRXMessages(t *testing.T) { diff --git a/pkg/common/trex_setting.go b/pkg/common/trex_setting.go index 438c282..4cf76c9 100644 --- a/pkg/common/trex_setting.go +++ b/pkg/common/trex_setting.go @@ -1,7 +1,7 @@ package common import ( - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type TRexSetting struct { diff --git a/pkg/common/trex_tx_test.go b/pkg/common/trex_tx_test.go index 5665940..76e5f40 100644 --- a/pkg/common/trex_tx_test.go +++ b/pkg/common/trex_tx_test.go @@ -3,9 +3,9 @@ package common_test import ( "testing" - "fiskerinc.com/modules/common" - s "fiskerinc.com/modules/common/carupdatestatus" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + s "github.com/fiskerinc/cloud-services/pkg/common/carupdatestatus" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestTRexTXMessages(t *testing.T) { diff --git a/pkg/common/update_manifest_versions.go b/pkg/common/update_manifest_versions.go index 2a84803..5185552 100644 --- a/pkg/common/update_manifest_versions.go +++ b/pkg/common/update_manifest_versions.go @@ -3,7 +3,7 @@ package common import ( "fmt" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type SUMSVersion struct { diff --git a/pkg/common/updatemanifest.go b/pkg/common/updatemanifest.go index 298869f..0618dc9 100644 --- a/pkg/common/updatemanifest.go +++ b/pkg/common/updatemanifest.go @@ -9,12 +9,12 @@ import ( "strings" "time" - v "fiskerinc.com/modules/utils/vod" - "fiskerinc.com/modules/vindecoder" - "fiskerinc.com/modules/vod_decoder" + v "github.com/fiskerinc/cloud-services/pkg/utils/vod" + "github.com/fiskerinc/cloud-services/pkg/vindecoder" + "github.com/fiskerinc/cloud-services/pkg/vod_decoder" - "fiskerinc.com/modules/common/dbbasemodel" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/albenik/bcd" "github.com/jinzhu/copier" "github.com/pkg/errors" diff --git a/pkg/common/updatemanifest_ecu.go b/pkg/common/updatemanifest_ecu.go index 85886ca..4f245b4 100644 --- a/pkg/common/updatemanifest_ecu.go +++ b/pkg/common/updatemanifest_ecu.go @@ -7,8 +7,8 @@ import ( "strconv" "github.com/pkg/errors" - "fiskerinc.com/modules/common/dbbasemodel" - "fiskerinc.com/modules/vindecoder" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/vindecoder" ) // Any changes need to be made to modules_go/db/queries/updatemanifests.go ECURollback() on getRollbacksOld diff --git a/pkg/common/updatemanifest_ecu_test.go b/pkg/common/updatemanifest_ecu_test.go index d5d6ca4..c20bd0b 100644 --- a/pkg/common/updatemanifest_ecu_test.go +++ b/pkg/common/updatemanifest_ecu_test.go @@ -3,7 +3,7 @@ package common import ( "testing" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" ) func TestRemoveParsedS19HexFiles(t *testing.T) { diff --git a/pkg/common/updatemanifest_file.go b/pkg/common/updatemanifest_file.go index 7418c9a..db74901 100644 --- a/pkg/common/updatemanifest_file.go +++ b/pkg/common/updatemanifest_file.go @@ -1,7 +1,7 @@ package common import ( - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" ) type ManifestFileType string diff --git a/pkg/common/updatemanifest_test.go b/pkg/common/updatemanifest_test.go index a8139b7..547fa94 100644 --- a/pkg/common/updatemanifest_test.go +++ b/pkg/common/updatemanifest_test.go @@ -8,11 +8,11 @@ import ( "testing" "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/common/dbbasemodel" - th "fiskerinc.com/modules/testhelper" - v "fiskerinc.com/modules/utils/vod" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" + th "github.com/fiskerinc/cloud-services/pkg/testhelper" + v "github.com/fiskerinc/cloud-services/pkg/utils/vod" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" ) func TestUpdatePackageManifest(t *testing.T) { diff --git a/pkg/db/db.go b/pkg/db/db.go index 82fa721..c3f3ef3 100644 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -4,8 +4,8 @@ import ( "fmt" "sync" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/go-pg/pg/v10" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" diff --git a/pkg/db/db_test.go b/pkg/db/db_test.go index 871b65e..ca96032 100644 --- a/pkg/db/db_test.go +++ b/pkg/db/db_test.go @@ -5,9 +5,9 @@ import ( "github.com/go-pg/pg/v10" - m "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/testhelper" + m "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestDBClient(t *testing.T) { diff --git a/pkg/db/migrator.go b/pkg/db/migrator.go index 110fd4f..f5bd6ee 100644 --- a/pkg/db/migrator.go +++ b/pkg/db/migrator.go @@ -12,7 +12,7 @@ import ( "github.com/pkg/errors" "github.com/vmihailenco/tagparser" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" ) type Migrator struct { diff --git a/pkg/db/migrator_test.go b/pkg/db/migrator_test.go index 2c87480..070fea4 100644 --- a/pkg/db/migrator_test.go +++ b/pkg/db/migrator_test.go @@ -4,9 +4,9 @@ import ( "reflect" "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/common/dbbasemodel" - "fiskerinc.com/modules/db" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/db" ) var instance db.Migrator diff --git a/pkg/db/queries/action_logs.go b/pkg/db/queries/action_logs.go index 09adab4..15b5f63 100644 --- a/pkg/db/queries/action_logs.go +++ b/pkg/db/queries/action_logs.go @@ -1,7 +1,7 @@ package queries import ( - "fiskerinc.com/modules/common/actionlogger" + "github.com/fiskerinc/cloud-services/pkg/common/actionlogger" "github.com/pkg/errors" ) diff --git a/pkg/db/queries/apicalls.go b/pkg/db/queries/apicalls.go index bdc22bb..359d05b 100644 --- a/pkg/db/queries/apicalls.go +++ b/pkg/db/queries/apicalls.go @@ -3,8 +3,8 @@ package queries import ( "strings" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" diff --git a/pkg/db/queries/apitokens.go b/pkg/db/queries/apitokens.go index 7ce2132..9016390 100644 --- a/pkg/db/queries/apitokens.go +++ b/pkg/db/queries/apitokens.go @@ -4,8 +4,8 @@ import ( "fmt" "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" ) diff --git a/pkg/db/queries/car_config_data.go b/pkg/db/queries/car_config_data.go index 389d88c..469f071 100644 --- a/pkg/db/queries/car_config_data.go +++ b/pkg/db/queries/car_config_data.go @@ -1,7 +1,7 @@ package queries import ( - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/pkg/errors" ) diff --git a/pkg/db/queries/car_versions_log.go b/pkg/db/queries/car_versions_log.go index 785ac82..69d37d0 100644 --- a/pkg/db/queries/car_versions_log.go +++ b/pkg/db/queries/car_versions_log.go @@ -3,7 +3,7 @@ package queries import ( "time" - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" diff --git a/pkg/db/queries/cars.go b/pkg/db/queries/cars.go index 9d198fa..fd6e817 100644 --- a/pkg/db/queries/cars.go +++ b/pkg/db/queries/cars.go @@ -7,10 +7,10 @@ import ( "strings" "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/validator" - "fiskerinc.com/modules/vindecoder" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/validator" + "github.com/fiskerinc/cloud-services/pkg/vindecoder" "github.com/go-pg/pg/v10" "github.com/go-pg/pg/v10/orm" diff --git a/pkg/db/queries/cars_test.go b/pkg/db/queries/cars_test.go index 9366486..2b68af4 100644 --- a/pkg/db/queries/cars_test.go +++ b/pkg/db/queries/cars_test.go @@ -7,14 +7,14 @@ import ( "github.com/go-pg/pg/v10/orm" - m "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/testhelper" + m "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/go-pg/pg/v10" "github.com/google/uuid" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" ) const testCarVIN = "1GNGC26R1XJ407649" diff --git a/pkg/db/queries/carupdates.go b/pkg/db/queries/carupdates.go index 3b95697..d46e4fd 100644 --- a/pkg/db/queries/carupdates.go +++ b/pkg/db/queries/carupdates.go @@ -3,10 +3,10 @@ package queries import ( "fmt" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/common/carupdatestatus" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/common/carupdatestatus" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/go-pg/pg/v10" "github.com/go-pg/pg/v10/orm" diff --git a/pkg/db/queries/carupdates_test.go b/pkg/db/queries/carupdates_test.go index ab29d8e..39d3ed0 100644 --- a/pkg/db/queries/carupdates_test.go +++ b/pkg/db/queries/carupdates_test.go @@ -6,10 +6,10 @@ import ( "github.com/go-pg/pg/v10/orm" - m "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/testhelper" + m "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) const testCarUpdateVIN = "FISKER1234" diff --git a/pkg/db/queries/certificates.go b/pkg/db/queries/certificates.go index 3b49b01..c6c53b7 100644 --- a/pkg/db/queries/certificates.go +++ b/pkg/db/queries/certificates.go @@ -1,9 +1,9 @@ package queries import ( - "fiskerinc.com/modules/common" - s "fiskerinc.com/modules/security" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + s "github.com/fiskerinc/cloud-services/pkg/security" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/go-pg/pg/v10" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" diff --git a/pkg/db/queries/certificates_test.go b/pkg/db/queries/certificates_test.go index 55dd83d..1a28f21 100644 --- a/pkg/db/queries/certificates_test.go +++ b/pkg/db/queries/certificates_test.go @@ -3,8 +3,8 @@ package queries_test import ( "testing" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" ) func TestSelectMostRecents(t *testing.T) { diff --git a/pkg/db/queries/driver_emails.go b/pkg/db/queries/driver_emails.go index bd12796..e40dfa0 100644 --- a/pkg/db/queries/driver_emails.go +++ b/pkg/db/queries/driver_emails.go @@ -1,7 +1,7 @@ package queries import ( - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/pkg/errors" ) diff --git a/pkg/db/queries/drivers.go b/pkg/db/queries/drivers.go index 9b99f94..6a84725 100644 --- a/pkg/db/queries/drivers.go +++ b/pkg/db/queries/drivers.go @@ -1,8 +1,8 @@ package queries import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" diff --git a/pkg/db/queries/ecckeys.go b/pkg/db/queries/ecckeys.go index cf02d6f..2a4518c 100644 --- a/pkg/db/queries/ecckeys.go +++ b/pkg/db/queries/ecckeys.go @@ -1,9 +1,9 @@ package queries import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/logger" - s "fiskerinc.com/modules/security" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/logger" + s "github.com/fiskerinc/cloud-services/pkg/security" "github.com/go-pg/pg/v10" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" diff --git a/pkg/db/queries/ecckeys_test.go b/pkg/db/queries/ecckeys_test.go index 980b054..6b854fe 100644 --- a/pkg/db/queries/ecckeys_test.go +++ b/pkg/db/queries/ecckeys_test.go @@ -4,10 +4,10 @@ import ( "encoding/json" "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestSelectAllPrivateKeysByVIN(t *testing.T) { diff --git a/pkg/db/queries/ecu_dtc.go b/pkg/db/queries/ecu_dtc.go index d98068e..a3b98fa 100644 --- a/pkg/db/queries/ecu_dtc.go +++ b/pkg/db/queries/ecu_dtc.go @@ -2,7 +2,7 @@ package queries import ( "time" - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" diff --git a/pkg/db/queries/ecu_dtc_test.go b/pkg/db/queries/ecu_dtc_test.go index 1d5910f..d400659 100644 --- a/pkg/db/queries/ecu_dtc_test.go +++ b/pkg/db/queries/ecu_dtc_test.go @@ -3,10 +3,10 @@ package queries_test import ( "testing" - m "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/testhelper" + m "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestDTCIntegration(t *testing.T) { diff --git a/pkg/db/queries/filekeys.go b/pkg/db/queries/filekeys.go index d067eca..89b654b 100644 --- a/pkg/db/queries/filekeys.go +++ b/pkg/db/queries/filekeys.go @@ -3,9 +3,9 @@ package queries import ( "sync" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/security" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/security" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/go-pg/pg/v10" "github.com/go-pg/pg/v10/orm" diff --git a/pkg/db/queries/filekeys_test.go b/pkg/db/queries/filekeys_test.go index 7a63522..766b1f9 100644 --- a/pkg/db/queries/filekeys_test.go +++ b/pkg/db/queries/filekeys_test.go @@ -3,11 +3,11 @@ package queries_test import ( "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/testhelper" - th "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + th "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/validator" ) const testFileID string = "07a4ed515543d4d8" diff --git a/pkg/db/queries/helper.go b/pkg/db/queries/helper.go index f58febc..73ed943 100644 --- a/pkg/db/queries/helper.go +++ b/pkg/db/queries/helper.go @@ -4,7 +4,7 @@ import ( "fmt" "net/http" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/gorilla/schema" "github.com/pkg/errors" diff --git a/pkg/db/queries/issue_images.go b/pkg/db/queries/issue_images.go index e869c20..e21020d 100644 --- a/pkg/db/queries/issue_images.go +++ b/pkg/db/queries/issue_images.go @@ -1,7 +1,7 @@ package queries import ( - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" diff --git a/pkg/db/queries/issue_images_test.go b/pkg/db/queries/issue_images_test.go index d1574ed..f68c82d 100644 --- a/pkg/db/queries/issue_images_test.go +++ b/pkg/db/queries/issue_images_test.go @@ -3,11 +3,11 @@ package queries_test import ( "testing" - "fiskerinc.com/modules/common" - m "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + m "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestIssueImageIntegration(t *testing.T) { diff --git a/pkg/db/queries/issues.go b/pkg/db/queries/issues.go index cecac97..a4e0a31 100644 --- a/pkg/db/queries/issues.go +++ b/pkg/db/queries/issues.go @@ -3,8 +3,8 @@ package queries import ( "fmt" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" diff --git a/pkg/db/queries/issues_test.go b/pkg/db/queries/issues_test.go index 01b51c5..935db44 100644 --- a/pkg/db/queries/issues_test.go +++ b/pkg/db/queries/issues_test.go @@ -4,11 +4,11 @@ import ( "testing" "time" - "fiskerinc.com/modules/common" - m "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + m "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestIssueIntegration(t *testing.T) { diff --git a/pkg/db/queries/mocks/apicalls.go b/pkg/db/queries/mocks/apicalls.go index d6ce7f3..b386e4f 100644 --- a/pkg/db/queries/mocks/apicalls.go +++ b/pkg/db/queries/mocks/apicalls.go @@ -1,8 +1,8 @@ package mocks import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/mocks/apitokens.go b/pkg/db/queries/mocks/apitokens.go index f25e08d..1ce5991 100644 --- a/pkg/db/queries/mocks/apitokens.go +++ b/pkg/db/queries/mocks/apitokens.go @@ -1,8 +1,8 @@ package mocks import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/mocks/car_config_data.go b/pkg/db/queries/mocks/car_config_data.go index 19dc095..d30f8ba 100644 --- a/pkg/db/queries/mocks/car_config_data.go +++ b/pkg/db/queries/mocks/car_config_data.go @@ -1,7 +1,7 @@ package mocks import ( - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" ) type MockCarConfigData struct { diff --git a/pkg/db/queries/mocks/car_versions_log.go b/pkg/db/queries/mocks/car_versions_log.go index ab6d1c4..6d3de43 100644 --- a/pkg/db/queries/mocks/car_versions_log.go +++ b/pkg/db/queries/mocks/car_versions_log.go @@ -3,8 +3,8 @@ package mocks import ( "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/mocks/cars.go b/pkg/db/queries/mocks/cars.go index 71c3387..4808bd8 100644 --- a/pkg/db/queries/mocks/cars.go +++ b/pkg/db/queries/mocks/cars.go @@ -3,9 +3,9 @@ package mocks import ( "github.com/go-pg/pg/v10/orm" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/jinzhu/copier" ) diff --git a/pkg/db/queries/mocks/carupdates.go b/pkg/db/queries/mocks/carupdates.go index 6d81dcb..8da88d4 100644 --- a/pkg/db/queries/mocks/carupdates.go +++ b/pkg/db/queries/mocks/carupdates.go @@ -5,9 +5,9 @@ import ( "github.com/go-pg/pg/v10/orm" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/validator" ) type MockCarUpdates struct { diff --git a/pkg/db/queries/mocks/certificates.go b/pkg/db/queries/mocks/certificates.go index 0a74a02..cafffaf 100644 --- a/pkg/db/queries/mocks/certificates.go +++ b/pkg/db/queries/mocks/certificates.go @@ -3,8 +3,8 @@ package mocks import ( "github.com/go-pg/pg/v10/orm" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/validator" ) // CarUpdate query methods diff --git a/pkg/db/queries/mocks/dbhttptest.go b/pkg/db/queries/mocks/dbhttptest.go index c3824af..e589c6b 100644 --- a/pkg/db/queries/mocks/dbhttptest.go +++ b/pkg/db/queries/mocks/dbhttptest.go @@ -7,8 +7,8 @@ import ( "regexp" "testing" - th "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/validator" + th "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/julienschmidt/httprouter" ) diff --git a/pkg/db/queries/mocks/dbmockhelper.go b/pkg/db/queries/mocks/dbmockhelper.go index d44dd9e..72b00ea 100644 --- a/pkg/db/queries/mocks/dbmockhelper.go +++ b/pkg/db/queries/mocks/dbmockhelper.go @@ -3,7 +3,7 @@ package mocks import ( "fmt" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/mocks/dbtestcase.go b/pkg/db/queries/mocks/dbtestcase.go index 380c9d2..eafef9e 100644 --- a/pkg/db/queries/mocks/dbtestcase.go +++ b/pkg/db/queries/mocks/dbtestcase.go @@ -4,8 +4,8 @@ import ( "fmt" "testing" - "fiskerinc.com/modules/db/queries" - th "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + th "github.com/fiskerinc/cloud-services/pkg/testhelper" ) type DBTestCase struct { diff --git a/pkg/db/queries/mocks/drivers.go b/pkg/db/queries/mocks/drivers.go index ad336da..bb20a3d 100644 --- a/pkg/db/queries/mocks/drivers.go +++ b/pkg/db/queries/mocks/drivers.go @@ -3,8 +3,8 @@ package mocks import ( "github.com/go-pg/pg/v10/orm" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/validator" ) // MockDrivers diff --git a/pkg/db/queries/mocks/ecckeys.go b/pkg/db/queries/mocks/ecckeys.go index 604eb2c..6554808 100644 --- a/pkg/db/queries/mocks/ecckeys.go +++ b/pkg/db/queries/mocks/ecckeys.go @@ -1,7 +1,7 @@ package mocks import ( - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/go-pg/pg/v10/orm" "github.com/jinzhu/copier" diff --git a/pkg/db/queries/mocks/ecu_dtc.go b/pkg/db/queries/mocks/ecu_dtc.go index d602be8..a52d12f 100644 --- a/pkg/db/queries/mocks/ecu_dtc.go +++ b/pkg/db/queries/mocks/ecu_dtc.go @@ -1,8 +1,8 @@ package mocks import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/mocks/filekeys.go b/pkg/db/queries/mocks/filekeys.go index 538dad6..df38678 100644 --- a/pkg/db/queries/mocks/filekeys.go +++ b/pkg/db/queries/mocks/filekeys.go @@ -1,8 +1,8 @@ package mocks import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/mocks/issues.go b/pkg/db/queries/mocks/issues.go index 4e170fa..6bfd266 100644 --- a/pkg/db/queries/mocks/issues.go +++ b/pkg/db/queries/mocks/issues.go @@ -6,9 +6,9 @@ import ( "github.com/go-pg/pg/v10/orm" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/validator" ) type MockIssue struct { diff --git a/pkg/db/queries/mocks/rate_plan.go b/pkg/db/queries/mocks/rate_plan.go index 46beed3..197a67d 100644 --- a/pkg/db/queries/mocks/rate_plan.go +++ b/pkg/db/queries/mocks/rate_plan.go @@ -1,8 +1,8 @@ package mocks import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" ) type MockRatePlan struct { diff --git a/pkg/db/queries/mocks/signed_images.go b/pkg/db/queries/mocks/signed_images.go index 956691b..06c5124 100644 --- a/pkg/db/queries/mocks/signed_images.go +++ b/pkg/db/queries/mocks/signed_images.go @@ -3,7 +3,7 @@ package mocks import ( "github.com/go-pg/pg/v10/orm" - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" ) // EccKey query methods diff --git a/pkg/db/queries/mocks/subscription_configurations.go b/pkg/db/queries/mocks/subscription_configurations.go index 5a9fb1d..7096fbe 100644 --- a/pkg/db/queries/mocks/subscription_configurations.go +++ b/pkg/db/queries/mocks/subscription_configurations.go @@ -1,8 +1,8 @@ package mocks import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/mocks/subscription_features.go b/pkg/db/queries/mocks/subscription_features.go index 1f398d1..cd3006b 100644 --- a/pkg/db/queries/mocks/subscription_features.go +++ b/pkg/db/queries/mocks/subscription_features.go @@ -1,8 +1,8 @@ package mocks import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/go-pg/pg/v10/orm" "github.com/google/uuid" diff --git a/pkg/db/queries/mocks/subscription_packages.go b/pkg/db/queries/mocks/subscription_packages.go index e9dd599..db94911 100644 --- a/pkg/db/queries/mocks/subscription_packages.go +++ b/pkg/db/queries/mocks/subscription_packages.go @@ -1,8 +1,8 @@ package mocks import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/go-pg/pg/v10/orm" "github.com/google/uuid" diff --git a/pkg/db/queries/mocks/subscriptions.go b/pkg/db/queries/mocks/subscriptions.go index 279db50..4fb43b5 100644 --- a/pkg/db/queries/mocks/subscriptions.go +++ b/pkg/db/queries/mocks/subscriptions.go @@ -3,8 +3,8 @@ package mocks import ( "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/mocks/subscriptiontypes.go b/pkg/db/queries/mocks/subscriptiontypes.go index 12a0343..e073e18 100644 --- a/pkg/db/queries/mocks/subscriptiontypes.go +++ b/pkg/db/queries/mocks/subscriptiontypes.go @@ -1,7 +1,7 @@ package mocks import ( - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/mocks/sums_versions.go b/pkg/db/queries/mocks/sums_versions.go index 10823eb..88ab26b 100644 --- a/pkg/db/queries/mocks/sums_versions.go +++ b/pkg/db/queries/mocks/sums_versions.go @@ -1,8 +1,8 @@ package mocks import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/mocks/supplier_accounts.go b/pkg/db/queries/mocks/supplier_accounts.go index 4540e96..2021762 100644 --- a/pkg/db/queries/mocks/supplier_accounts.go +++ b/pkg/db/queries/mocks/supplier_accounts.go @@ -1,8 +1,8 @@ package mocks import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/mocks/supplier_organizations.go b/pkg/db/queries/mocks/supplier_organizations.go index 9946505..fe2e116 100644 --- a/pkg/db/queries/mocks/supplier_organizations.go +++ b/pkg/db/queries/mocks/supplier_organizations.go @@ -1,8 +1,8 @@ package mocks import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/mocks/swversion_rxswin.go b/pkg/db/queries/mocks/swversion_rxswin.go index f764364..9d60e10 100644 --- a/pkg/db/queries/mocks/swversion_rxswin.go +++ b/pkg/db/queries/mocks/swversion_rxswin.go @@ -1,8 +1,8 @@ package mocks import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/mocks/symkeys.go b/pkg/db/queries/mocks/symkeys.go index 53dcc98..98f7893 100644 --- a/pkg/db/queries/mocks/symkeys.go +++ b/pkg/db/queries/mocks/symkeys.go @@ -3,7 +3,7 @@ package mocks import ( "github.com/go-pg/pg/v10/orm" - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" ) // SymKey query methods diff --git a/pkg/db/queries/mocks/tags.go b/pkg/db/queries/mocks/tags.go index 2f7ad10..1d8df82 100644 --- a/pkg/db/queries/mocks/tags.go +++ b/pkg/db/queries/mocks/tags.go @@ -1,8 +1,8 @@ package mocks import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/mocks/updatemanifests.go b/pkg/db/queries/mocks/updatemanifests.go index 4d4c996..a95ee7f 100644 --- a/pkg/db/queries/mocks/updatemanifests.go +++ b/pkg/db/queries/mocks/updatemanifests.go @@ -4,9 +4,9 @@ import ( "encoding/json" "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" diff --git a/pkg/db/queries/querybase.go b/pkg/db/queries/querybase.go index cbf0573..e1cd727 100644 --- a/pkg/db/queries/querybase.go +++ b/pkg/db/queries/querybase.go @@ -3,7 +3,7 @@ package queries import ( "sync" - "fiskerinc.com/modules/db" + "github.com/fiskerinc/cloud-services/pkg/db" "github.com/go-pg/pg/v10" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" diff --git a/pkg/db/queries/rate_plan_tomobile.go b/pkg/db/queries/rate_plan_tomobile.go index 3620b67..eadb973 100644 --- a/pkg/db/queries/rate_plan_tomobile.go +++ b/pkg/db/queries/rate_plan_tomobile.go @@ -3,7 +3,7 @@ package queries import ( "fmt" - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" ) diff --git a/pkg/db/queries/rate_plan_tomobile_test.go b/pkg/db/queries/rate_plan_tomobile_test.go index fa16c00..87f3642 100644 --- a/pkg/db/queries/rate_plan_tomobile_test.go +++ b/pkg/db/queries/rate_plan_tomobile_test.go @@ -3,8 +3,8 @@ package queries_test import ( "testing" - m "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + m "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" ) func TestCreateSelectQuery(t *testing.T) { diff --git a/pkg/db/queries/signed_images.go b/pkg/db/queries/signed_images.go index d60d2f0..75c921d 100644 --- a/pkg/db/queries/signed_images.go +++ b/pkg/db/queries/signed_images.go @@ -1,8 +1,8 @@ package queries import ( - "fiskerinc.com/modules/common" - s "fiskerinc.com/modules/security" + "github.com/fiskerinc/cloud-services/pkg/common" + s "github.com/fiskerinc/cloud-services/pkg/security" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" ) diff --git a/pkg/db/queries/signed_images_test.go b/pkg/db/queries/signed_images_test.go index 3c32481..b750ec1 100644 --- a/pkg/db/queries/signed_images_test.go +++ b/pkg/db/queries/signed_images_test.go @@ -3,11 +3,11 @@ package queries_test import ( "testing" - "fiskerinc.com/modules/common" - m "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + m "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/go-pg/pg/v10" "github.com/pkg/errors" ) diff --git a/pkg/db/queries/subscription_configurations.go b/pkg/db/queries/subscription_configurations.go index 0c6159e..3b6be67 100644 --- a/pkg/db/queries/subscription_configurations.go +++ b/pkg/db/queries/subscription_configurations.go @@ -1,7 +1,7 @@ package queries import ( - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/go-pg/pg/v10/orm" "github.com/google/uuid" diff --git a/pkg/db/queries/subscription_features.go b/pkg/db/queries/subscription_features.go index 5d12e89..d17c1ab 100644 --- a/pkg/db/queries/subscription_features.go +++ b/pkg/db/queries/subscription_features.go @@ -1,8 +1,8 @@ package queries import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/go-pg/pg/v10/orm" "github.com/google/uuid" diff --git a/pkg/db/queries/subscription_packages.go b/pkg/db/queries/subscription_packages.go index 6997f52..8dec1d2 100644 --- a/pkg/db/queries/subscription_packages.go +++ b/pkg/db/queries/subscription_packages.go @@ -1,7 +1,7 @@ package queries import ( - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/go-pg/pg/v10/orm" "github.com/google/uuid" diff --git a/pkg/db/queries/subscription_packages_test.go b/pkg/db/queries/subscription_packages_test.go index 1030d07..2f05a18 100644 --- a/pkg/db/queries/subscription_packages_test.go +++ b/pkg/db/queries/subscription_packages_test.go @@ -3,10 +3,10 @@ package queries_test import ( "testing" - m "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" - th "fiskerinc.com/modules/testhelper" + m "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + th "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/google/uuid" ) diff --git a/pkg/db/queries/subscriptions._test.go b/pkg/db/queries/subscriptions._test.go index 6e130c8..5ccbf4b 100644 --- a/pkg/db/queries/subscriptions._test.go +++ b/pkg/db/queries/subscriptions._test.go @@ -3,10 +3,10 @@ package queries_test import ( "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/google/uuid" ) diff --git a/pkg/db/queries/subscriptions.go b/pkg/db/queries/subscriptions.go index fb1295c..7a770d6 100644 --- a/pkg/db/queries/subscriptions.go +++ b/pkg/db/queries/subscriptions.go @@ -3,8 +3,8 @@ package queries import ( "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/go-pg/pg/v10/orm" "github.com/google/uuid" diff --git a/pkg/db/queries/subscriptiontypes._test.go b/pkg/db/queries/subscriptiontypes._test.go index 973c91e..66022bb 100644 --- a/pkg/db/queries/subscriptiontypes._test.go +++ b/pkg/db/queries/subscriptiontypes._test.go @@ -4,11 +4,11 @@ import ( "errors" "testing" - "fiskerinc.com/modules/common" - m "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + m "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/google/uuid" ) diff --git a/pkg/db/queries/subscriptiontypes.go b/pkg/db/queries/subscriptiontypes.go index 47ae032..1a4b322 100644 --- a/pkg/db/queries/subscriptiontypes.go +++ b/pkg/db/queries/subscriptiontypes.go @@ -1,8 +1,8 @@ package queries import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/go-pg/pg/v10/orm" "github.com/google/uuid" diff --git a/pkg/db/queries/sums_versions.go b/pkg/db/queries/sums_versions.go index 8bb340c..5983399 100644 --- a/pkg/db/queries/sums_versions.go +++ b/pkg/db/queries/sums_versions.go @@ -1,7 +1,7 @@ package queries import ( - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" ) diff --git a/pkg/db/queries/supplier_accounts.go b/pkg/db/queries/supplier_accounts.go index 02f83c2..4f1095c 100644 --- a/pkg/db/queries/supplier_accounts.go +++ b/pkg/db/queries/supplier_accounts.go @@ -3,7 +3,7 @@ package queries import ( "fmt" - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/go-pg/pg/v10" "github.com/go-pg/pg/v10/orm" diff --git a/pkg/db/queries/supplier_organizations.go b/pkg/db/queries/supplier_organizations.go index cb3bf43..b43ddf9 100644 --- a/pkg/db/queries/supplier_organizations.go +++ b/pkg/db/queries/supplier_organizations.go @@ -1,8 +1,8 @@ package queries import ( - "fiskerinc.com/modules/common" - s "fiskerinc.com/modules/security" + "github.com/fiskerinc/cloud-services/pkg/common" + s "github.com/fiskerinc/cloud-services/pkg/security" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/swversion_rxswin.go b/pkg/db/queries/swversion_rxswin.go index 04477fb..f40a937 100644 --- a/pkg/db/queries/swversion_rxswin.go +++ b/pkg/db/queries/swversion_rxswin.go @@ -1,7 +1,7 @@ package queries import ( - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" ) diff --git a/pkg/db/queries/symkeys.go b/pkg/db/queries/symkeys.go index 30ed322..9c7b9b3 100644 --- a/pkg/db/queries/symkeys.go +++ b/pkg/db/queries/symkeys.go @@ -1,8 +1,8 @@ package queries import ( - "fiskerinc.com/modules/common" - s "fiskerinc.com/modules/security" + "github.com/fiskerinc/cloud-services/pkg/common" + s "github.com/fiskerinc/cloud-services/pkg/security" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" ) diff --git a/pkg/db/queries/tags.go b/pkg/db/queries/tags.go index ab77ab6..807b99f 100644 --- a/pkg/db/queries/tags.go +++ b/pkg/db/queries/tags.go @@ -1,7 +1,7 @@ package queries import ( - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/go-pg/pg/v10" "github.com/go-pg/pg/v10/orm" ) diff --git a/pkg/db/queries/updatemanifests.go b/pkg/db/queries/updatemanifests.go index 3603da6..f92aca7 100644 --- a/pkg/db/queries/updatemanifests.go +++ b/pkg/db/queries/updatemanifests.go @@ -3,10 +3,10 @@ package queries import ( "fmt" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/go-pg/pg/v10" "github.com/go-pg/pg/v10/orm" diff --git a/pkg/db/queries/updatemanifests_test.go b/pkg/db/queries/updatemanifests_test.go index 50bbd73..9f43c9e 100644 --- a/pkg/db/queries/updatemanifests_test.go +++ b/pkg/db/queries/updatemanifests_test.go @@ -5,10 +5,10 @@ import ( "testing" "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestUpdatePackagesManifest(t *testing.T) { diff --git a/pkg/db/sqllogger.go b/pkg/db/sqllogger.go index 4daf592..6225d69 100644 --- a/pkg/db/sqllogger.go +++ b/pkg/db/sqllogger.go @@ -3,7 +3,7 @@ package db import ( "context" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" pg "github.com/go-pg/pg/v10" ) diff --git a/pkg/dbc/README.md b/pkg/dbc/README.md new file mode 100644 index 0000000..ea99e95 --- /dev/null +++ b/pkg/dbc/README.md @@ -0,0 +1,43 @@ +# DBC Package + +CAN database (DBC) signal definitions and parsing utilities. + +## Structure + +- `models/` - Core types (DBCCollection, CANMessage, CANSignal) +- `state/` - State tracking for CAN signals +- `diagnostics/` - Diagnostic signal handling +- `fm29_*/`, `n60/` - Generated packages from DBC files (stubs by default) + +## Generated Packages + +The `fm29_*` and `n60` packages contain CAN signal definitions generated from DBC files. By default, these are stubs that return empty collections. + +To generate real definitions, you need: +1. DBC source files (from CEC-Common repo or equivalent) +2. The `cantool` from `pkg/can-go` + +## Generating DBC Code + +```bash +# From cloud-services root +go run ./pkg/can-go/cmd/cantool generate /path/to/dbc/files ./pkg/dbc +``` + +Or use the generate script: +```bash +./scripts/generate-dbc.sh /path/to/dbc/files +``` + +## When Do You Need Real DBC Packages? + +- **Gateway**: Uses only `dbc/models` - stubs are fine +- **Optimus**: Needs real DBC for CAN signal parsing +- **Ditto**: Needs real DBC for digital twin state +- **Beacon**: Needs real DBC for diagnostic alerts + +## Adding New DBC Versions + +1. Add DBC files to source directory +2. Run cantool generate +3. Update `new_dbc_collection.go` to include new version diff --git a/pkg/dbc/dbc.go b/pkg/dbc/dbc.go index 61a0bd1..0899f6f 100644 --- a/pkg/dbc/dbc.go +++ b/pkg/dbc/dbc.go @@ -5,13 +5,13 @@ import ( "math" "strings" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/dbc/diagnostics" - "fiskerinc.com/modules/dbc/state" - "fiskerinc.com/modules/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/dbc/diagnostics" + "github.com/fiskerinc/cloud-services/pkg/dbc/state" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" - can "github.com/Fisker-Inc/project-ai-can-go" - "github.com/Fisker-Inc/project-ai-can-go/pkg/descriptor" + can "github.com/fiskerinc/cloud-services/pkg/can-go" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" "github.com/pkg/errors" ) diff --git a/pkg/dbc/dbc_fm29_frs90.go b/pkg/dbc/dbc_fm29_frs90.go index 4845aa1..391b433 100644 --- a/pkg/dbc/dbc_fm29_frs90.go +++ b/pkg/dbc/dbc_fm29_frs90.go @@ -1,10 +1,10 @@ package dbc import ( - "fiskerinc.com/modules/dbc/diagnostics" - fm29_frs90 "fiskerinc.com/modules/dbc/fm29_frs90" - "fiskerinc.com/modules/dbc/models" - "fiskerinc.com/modules/dbc/state" + "github.com/fiskerinc/cloud-services/pkg/dbc/diagnostics" + fm29_frs90 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frs90" + "github.com/fiskerinc/cloud-services/pkg/dbc/models" + "github.com/fiskerinc/cloud-services/pkg/dbc/state" ) func NewFM29_FRS90_DBC() models.DBCVersionInterface { diff --git a/pkg/dbc/dbc_fm29_frsd0.go b/pkg/dbc/dbc_fm29_frsd0.go index 40b746a..4485a79 100644 --- a/pkg/dbc/dbc_fm29_frsd0.go +++ b/pkg/dbc/dbc_fm29_frsd0.go @@ -1,10 +1,10 @@ package dbc import ( - "fiskerinc.com/modules/dbc/diagnostics" - fm29_frsd0 "fiskerinc.com/modules/dbc/fm29_frsd0" - "fiskerinc.com/modules/dbc/models" - "fiskerinc.com/modules/dbc/state" + "github.com/fiskerinc/cloud-services/pkg/dbc/diagnostics" + fm29_frsd0 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd0" + "github.com/fiskerinc/cloud-services/pkg/dbc/models" + "github.com/fiskerinc/cloud-services/pkg/dbc/state" ) func NewFM29_FRSD0_DBC() models.DBCVersionInterface { diff --git a/pkg/dbc/dbc_fm29_frsd21.go b/pkg/dbc/dbc_fm29_frsd21.go index 7534473..f5cfe16 100644 --- a/pkg/dbc/dbc_fm29_frsd21.go +++ b/pkg/dbc/dbc_fm29_frsd21.go @@ -1,10 +1,10 @@ package dbc import ( - "fiskerinc.com/modules/dbc/diagnostics" - fm29_frsd21 "fiskerinc.com/modules/dbc/fm29_frsd21" - "fiskerinc.com/modules/dbc/models" - "fiskerinc.com/modules/dbc/state" + "github.com/fiskerinc/cloud-services/pkg/dbc/diagnostics" + fm29_frsd21 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd21" + "github.com/fiskerinc/cloud-services/pkg/dbc/models" + "github.com/fiskerinc/cloud-services/pkg/dbc/state" ) func NewFM29_FRSD21_DBC() models.DBCVersionInterface { diff --git a/pkg/dbc/dbc_fm29_frsd390.go b/pkg/dbc/dbc_fm29_frsd390.go index 1229d5e..e7abb2e 100644 --- a/pkg/dbc/dbc_fm29_frsd390.go +++ b/pkg/dbc/dbc_fm29_frsd390.go @@ -1,10 +1,10 @@ package dbc import ( - "fiskerinc.com/modules/dbc/diagnostics" - fm29_frsd390 "fiskerinc.com/modules/dbc/fm29_frsd390" - "fiskerinc.com/modules/dbc/models" - "fiskerinc.com/modules/dbc/state" + "github.com/fiskerinc/cloud-services/pkg/dbc/diagnostics" + fm29_frsd390 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd390" + "github.com/fiskerinc/cloud-services/pkg/dbc/models" + "github.com/fiskerinc/cloud-services/pkg/dbc/state" ) func NewFM29_FRSD390_DBC() models.DBCVersionInterface { diff --git a/pkg/dbc/dbc_parsestate_test.go b/pkg/dbc/dbc_parsestate_test.go index 74810d1..0bf1bba 100644 --- a/pkg/dbc/dbc_parsestate_test.go +++ b/pkg/dbc/dbc_parsestate_test.go @@ -6,15 +6,15 @@ import ( "regexp" "testing" - "fiskerinc.com/modules/dbc" - fm29_frs90 "fiskerinc.com/modules/dbc/fm29_frs90" - fm29_frsd0 "fiskerinc.com/modules/dbc/fm29_frsd0" - fm29_frsd21 "fiskerinc.com/modules/dbc/fm29_frsd21" - fm29_frsd390 "fiskerinc.com/modules/dbc/fm29_frsd390" + "github.com/fiskerinc/cloud-services/pkg/dbc" + fm29_frs90 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frs90" + fm29_frsd0 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd0" + fm29_frsd21 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd21" + fm29_frsd390 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd390" - "fiskerinc.com/modules/dbc/state" - "fiskerinc.com/modules/grpc/kafka_grpc" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/dbc/state" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/golang/protobuf/proto" "github.com/jinzhu/copier" ) diff --git a/pkg/dbc/dbc_tools_test.go b/pkg/dbc/dbc_tools_test.go index 28cc7bd..1d9c3ca 100644 --- a/pkg/dbc/dbc_tools_test.go +++ b/pkg/dbc/dbc_tools_test.go @@ -5,15 +5,15 @@ import ( "fmt" "testing" - fm29_frs90 "fiskerinc.com/modules/dbc/fm29_frs90" - fm29_frsd0 "fiskerinc.com/modules/dbc/fm29_frsd0" - fm29_frsd21 "fiskerinc.com/modules/dbc/fm29_frsd21" - fm29_frsd390 "fiskerinc.com/modules/dbc/fm29_frsd390" - n60 "fiskerinc.com/modules/dbc/n60" - "fiskerinc.com/modules/testhelper" + fm29_frs90 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frs90" + fm29_frsd0 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd0" + fm29_frsd21 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd21" + fm29_frsd390 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd390" + n60 "github.com/fiskerinc/cloud-services/pkg/dbc/n60" + "github.com/fiskerinc/cloud-services/pkg/testhelper" - can "github.com/Fisker-Inc/project-ai-can-go" - "github.com/Fisker-Inc/project-ai-can-go/pkg/descriptor" + can "github.com/fiskerinc/cloud-services/pkg/can-go" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" ) // For generating test data diff --git a/pkg/dbc/fm29_frs90/stub.go b/pkg/dbc/fm29_frs90/stub.go new file mode 100644 index 0000000..8b43ac5 --- /dev/null +++ b/pkg/dbc/fm29_frs90/stub.go @@ -0,0 +1,19 @@ +// Package fm29_frs90 is a stub for generated DBC code. +// Run ./scripts/generate-dbc.sh to generate actual CAN signal definitions. +// See pkg/dbc/README.md for details. +package fm29_frs90 + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" +) + +// Hash is the DBC file hash - stub value +const Hash = "stub_fm29_frs90" + +// MessagesDescriptor is a stub for the generated CAN message descriptor +type MessagesDescriptor struct{} + +// Database returns an empty CAN database - regenerate with cantool for actual definitions +func (m *MessagesDescriptor) Database() *descriptor.Database { + return &descriptor.Database{} +} diff --git a/pkg/dbc/fm29_frsd0/stub.go b/pkg/dbc/fm29_frsd0/stub.go new file mode 100644 index 0000000..073ee29 --- /dev/null +++ b/pkg/dbc/fm29_frsd0/stub.go @@ -0,0 +1,19 @@ +// Package fm29_frsd0 is a stub for generated DBC code. +// Run ./scripts/generate-dbc.sh to generate actual CAN signal definitions. +// See pkg/dbc/README.md for details. +package fm29_frsd0 + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" +) + +// Hash is the DBC file hash - stub value +const Hash = "stub_fm29_frsd0" + +// MessagesDescriptor is a stub for the generated CAN message descriptor +type MessagesDescriptor struct{} + +// Database returns an empty CAN database - regenerate with cantool for actual definitions +func (m *MessagesDescriptor) Database() *descriptor.Database { + return &descriptor.Database{} +} diff --git a/pkg/dbc/fm29_frsd21/stub.go b/pkg/dbc/fm29_frsd21/stub.go new file mode 100644 index 0000000..123c853 --- /dev/null +++ b/pkg/dbc/fm29_frsd21/stub.go @@ -0,0 +1,19 @@ +// Package fm29_frsd21 is a stub for generated DBC code. +// Run ./scripts/generate-dbc.sh to generate actual CAN signal definitions. +// See pkg/dbc/README.md for details. +package fm29_frsd21 + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" +) + +// Hash is the DBC file hash - stub value +const Hash = "stub_fm29_frsd21" + +// MessagesDescriptor is a stub for the generated CAN message descriptor +type MessagesDescriptor struct{} + +// Database returns an empty CAN database - regenerate with cantool for actual definitions +func (m *MessagesDescriptor) Database() *descriptor.Database { + return &descriptor.Database{} +} diff --git a/pkg/dbc/fm29_frsd390/stub.go b/pkg/dbc/fm29_frsd390/stub.go new file mode 100644 index 0000000..6c17199 --- /dev/null +++ b/pkg/dbc/fm29_frsd390/stub.go @@ -0,0 +1,19 @@ +// Package fm29_frsd390 is a stub for generated DBC code. +// Run ./scripts/generate-dbc.sh to generate actual CAN signal definitions. +// See pkg/dbc/README.md for details. +package fm29_frsd390 + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" +) + +// Hash is the DBC file hash - stub value +const Hash = "stub_fm29_frsd390" + +// MessagesDescriptor is a stub for the generated CAN message descriptor +type MessagesDescriptor struct{} + +// Database returns an empty CAN database - regenerate with cantool for actual definitions +func (m *MessagesDescriptor) Database() *descriptor.Database { + return &descriptor.Database{} +} diff --git a/pkg/dbc/models/can_message.go b/pkg/dbc/models/can_message.go index 449c79c..585ef64 100644 --- a/pkg/dbc/models/can_message.go +++ b/pkg/dbc/models/can_message.go @@ -3,10 +3,10 @@ package models import ( "fmt" - "fiskerinc.com/modules/dbc/state" + "github.com/fiskerinc/cloud-services/pkg/dbc/state" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/digitaltwin" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/digitaltwin" ) type CANMessageInterface interface { diff --git a/pkg/dbc/models/can_signal.go b/pkg/dbc/models/can_signal.go index 784279d..180b3ee 100644 --- a/pkg/dbc/models/can_signal.go +++ b/pkg/dbc/models/can_signal.go @@ -1,7 +1,7 @@ package models import ( - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" ) func NewCANSignal(position int, name string, convert func(value float64) interface{}) CANSignal { diff --git a/pkg/dbc/models/dbc_collection.go b/pkg/dbc/models/dbc_collection.go index 7abeb15..8326aa2 100644 --- a/pkg/dbc/models/dbc_collection.go +++ b/pkg/dbc/models/dbc_collection.go @@ -3,11 +3,11 @@ package models import ( "fmt" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/dbc/state" - "fiskerinc.com/modules/digitaltwin" - "fiskerinc.com/modules/grpc/kafka_grpc" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/dbc/state" + "github.com/fiskerinc/cloud-services/pkg/digitaltwin" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/pkg/errors" ) diff --git a/pkg/dbc/models/dbc_version.go b/pkg/dbc/models/dbc_version.go index 8949856..274034a 100644 --- a/pkg/dbc/models/dbc_version.go +++ b/pkg/dbc/models/dbc_version.go @@ -6,12 +6,12 @@ import ( "math" "strings" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/digitaltwin" - "fiskerinc.com/modules/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/digitaltwin" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" - can "github.com/Fisker-Inc/project-ai-can-go" - "github.com/Fisker-Inc/project-ai-can-go/pkg/descriptor" + can "github.com/fiskerinc/cloud-services/pkg/can-go" + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" "github.com/pkg/errors" ) diff --git a/pkg/dbc/models/invalid_id_cache.go b/pkg/dbc/models/invalid_id_cache.go index ac82ec7..483f129 100644 --- a/pkg/dbc/models/invalid_id_cache.go +++ b/pkg/dbc/models/invalid_id_cache.go @@ -4,8 +4,8 @@ import ( "fmt" "sync" - "fiskerinc.com/modules/cache" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/cache" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" ) var ( diff --git a/pkg/dbc/n60/stub.go b/pkg/dbc/n60/stub.go new file mode 100644 index 0000000..ad6cc05 --- /dev/null +++ b/pkg/dbc/n60/stub.go @@ -0,0 +1,19 @@ +// Package n60 is a stub for generated DBC code. +// Run ./scripts/generate-dbc.sh to generate actual CAN signal definitions. +// See pkg/dbc/README.md for details. +package n60 + +import ( + "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" +) + +// Hash is the DBC file hash - stub value +const Hash = "stub_n60" + +// MessagesDescriptor is a stub for the generated CAN message descriptor +type MessagesDescriptor struct{} + +// Database returns an empty CAN database - regenerate with cantool for actual definitions +func (m *MessagesDescriptor) Database() *descriptor.Database { + return &descriptor.Database{} +} diff --git a/pkg/dbc/new_dbc_collection.go b/pkg/dbc/new_dbc_collection.go index c19e389..7e5c264 100644 --- a/pkg/dbc/new_dbc_collection.go +++ b/pkg/dbc/new_dbc_collection.go @@ -1,11 +1,11 @@ package dbc import ( - fm29_frs90 "fiskerinc.com/modules/dbc/fm29_frs90" - fm29_frsd0 "fiskerinc.com/modules/dbc/fm29_frsd0" - fm29_frsd21 "fiskerinc.com/modules/dbc/fm29_frsd21" - fm29_frsd390 "fiskerinc.com/modules/dbc/fm29_frsd390" - "fiskerinc.com/modules/dbc/models" + fm29_frs90 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frs90" + fm29_frsd0 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd0" + fm29_frsd21 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd21" + fm29_frsd390 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd390" + "github.com/fiskerinc/cloud-services/pkg/dbc/models" ) // This is the DBC hash before frsd0 was fixed diff --git a/pkg/dbc/new_dbc_collection_test.go b/pkg/dbc/new_dbc_collection_test.go index 28b0faa..bd6d8ed 100644 --- a/pkg/dbc/new_dbc_collection_test.go +++ b/pkg/dbc/new_dbc_collection_test.go @@ -3,11 +3,11 @@ package dbc_test import ( "testing" - "fiskerinc.com/modules/dbc" - fm29_frs90 "fiskerinc.com/modules/dbc/fm29_frs90" - fm29_frsd0 "fiskerinc.com/modules/dbc/fm29_frsd0" - "fiskerinc.com/modules/dbc/models" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/dbc" + fm29_frs90 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frs90" + fm29_frsd0 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd0" + "github.com/fiskerinc/cloud-services/pkg/dbc/models" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestNewDBCCollection(t *testing.T) { diff --git a/pkg/dbc/state/state_func.go b/pkg/dbc/state/state_func.go index e7dad43..cba171c 100644 --- a/pkg/dbc/state/state_func.go +++ b/pkg/dbc/state/state_func.go @@ -1,8 +1,8 @@ package state import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/redis" ) type StateFunc func(*redis.RedisBatchCommands, string, int, []common.CANSignal) error diff --git a/pkg/digitaltwin/cache.go b/pkg/digitaltwin/cache.go index edea0d3..cab271b 100644 --- a/pkg/digitaltwin/cache.go +++ b/pkg/digitaltwin/cache.go @@ -1,8 +1,8 @@ package digitaltwin import ( - "fiskerinc.com/modules/cache" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/cache" + "github.com/fiskerinc/cloud-services/pkg/logger" ) // var existsCount ExistsCount diff --git a/pkg/digitaltwin/cache_test.go b/pkg/digitaltwin/cache_test.go index 6955db1..8dbc90f 100644 --- a/pkg/digitaltwin/cache_test.go +++ b/pkg/digitaltwin/cache_test.go @@ -3,7 +3,7 @@ package digitaltwin_test import ( "testing" - "fiskerinc.com/modules/digitaltwin" + "github.com/fiskerinc/cloud-services/pkg/digitaltwin" "github.com/stretchr/testify/assert" ) diff --git a/pkg/digitaltwin/send.go b/pkg/digitaltwin/send.go index 19a4507..acd2fff 100644 --- a/pkg/digitaltwin/send.go +++ b/pkg/digitaltwin/send.go @@ -3,11 +3,11 @@ package digitaltwin import ( "encoding/json" - "fiskerinc.com/modules/cache" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/cache" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redis" "github.com/pkg/errors" ) diff --git a/pkg/digitaltwin/send_test.go b/pkg/digitaltwin/send_test.go index bd27501..a4dab4e 100644 --- a/pkg/digitaltwin/send_test.go +++ b/pkg/digitaltwin/send_test.go @@ -3,8 +3,8 @@ package digitaltwin_test import ( "encoding/json" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/duration" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/duration" ) func clone(orig *common.JSONDigitalTwin) (*common.JSONDigitalTwin, error) { diff --git a/pkg/digitaltwin/sendv2.go b/pkg/digitaltwin/sendv2.go index 266eee0..63f3407 100644 --- a/pkg/digitaltwin/sendv2.go +++ b/pkg/digitaltwin/sendv2.go @@ -3,11 +3,11 @@ package digitaltwin import ( "encoding/json" - cache "fiskerinc.com/modules/cachev2" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/logger" - redis "fiskerinc.com/modules/redisv2" + cache "github.com/fiskerinc/cloud-services/pkg/cachev2" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/logger" + redis "github.com/fiskerinc/cloud-services/pkg/redisv2" "github.com/pkg/errors" ) diff --git a/pkg/flashpackversion/flashpack_version_update.go b/pkg/flashpackversion/flashpack_version_update.go index 38a5212..6920b17 100644 --- a/pkg/flashpackversion/flashpack_version_update.go +++ b/pkg/flashpackversion/flashpack_version_update.go @@ -4,8 +4,8 @@ import ( "sort" "strconv" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/pkg/errors" "golang.org/x/exp/maps" ) diff --git a/pkg/flashpackversion/flashpack_version_update_test.go b/pkg/flashpackversion/flashpack_version_update_test.go index 6f2a780..b1142d1 100644 --- a/pkg/flashpackversion/flashpack_version_update_test.go +++ b/pkg/flashpackversion/flashpack_version_update_test.go @@ -3,9 +3,9 @@ package flashpackversion_test import ( "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries/mocks" - fv "fiskerinc.com/modules/flashpackversion" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks" + fv "github.com/fiskerinc/cloud-services/pkg/flashpackversion" "github.com/stretchr/testify/assert" ) diff --git a/pkg/go.mod b/pkg/go.mod index b4bc5fa..3b4d231 100644 --- a/pkg/go.mod +++ b/pkg/go.mod @@ -1,13 +1,11 @@ module github.com/fiskerinc/cloud-services/pkg -go 1.24 +go 1.25 -toolchain go1.24.3 +toolchain go1.25.6 require ( - github.com/Fisker-Inc/project-ai-can-go v1.3.1 github.com/aws/aws-sdk-go v1.44.327 - github.com/confluentinc/confluent-kafka-go/v2 v2.3.0 github.com/go-pg/pg/v10 v10.11.1 github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.15.1 @@ -19,6 +17,8 @@ require ( github.com/rs/zerolog v1.29.1 github.com/stretchr/testify v1.10.0 github.com/swaggo/http-swagger v1.3.3 + github.com/twmb/franz-go v1.20.6 + github.com/twmb/franz-go/pkg/kadm v1.17.2 github.com/vmihailenco/tagparser v0.1.2 github.com/xeipuuv/gojsonschema v1.2.0 google.golang.org/grpc v1.67.3 @@ -30,7 +30,7 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ReneKroon/ttlcache/v2 v2.11.0 github.com/philhofer/fwd v1.1.2 // indirect - golang.org/x/sys v0.29.0 // indirect + golang.org/x/sys v0.38.0 // indirect golang.org/x/time v0.8.0 // indirect ) @@ -41,8 +41,8 @@ require ( require ( go.mongodb.org/mongo-driver v1.14.0 - golang.org/x/crypto v0.32.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/crypto v0.45.0 // indirect + golang.org/x/text v0.31.0 // indirect ) require ( @@ -50,6 +50,7 @@ require ( github.com/Azure/azure-storage-blob-go v0.15.0 github.com/ClickHouse/clickhouse-go/v2 v2.6.0 github.com/albenik/bcd v0.0.0-20170831201648-635201416bc7 + github.com/confluentinc/confluent-kafka-go/v2 v2.3.0 github.com/elliotchance/orderedmap/v2 v2.2.0 github.com/go-jose/go-jose/v4 v4.1.0 github.com/go-openapi/errors v0.22.0 @@ -118,6 +119,7 @@ require ( github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect github.com/segmentio/asm v1.2.0 // indirect + github.com/twmb/franz-go/pkg/kmsg v1.12.0 // indirect go.opentelemetry.io/otel/metric v1.29.0 // indirect go.opentelemetry.io/otel/sdk v1.29.0 // indirect go.uber.org/atomic v1.11.0 // indirect @@ -126,7 +128,6 @@ require ( go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect gonum.org/v1/gonum v0.11.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect - gotest.tools/v3 v3.5.1 // indirect inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect ) @@ -150,14 +151,14 @@ require ( github.com/jinzhu/copier v0.3.5 github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.17.1 // indirect + github.com/klauspost/compress v1.18.2 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/paulmach/orb v0.8.0 // indirect - github.com/pierrec/lz4/v4 v4.1.18 // indirect + github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a // indirect @@ -174,10 +175,10 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect go.opentelemetry.io/otel v1.29.0 // indirect go.opentelemetry.io/otel/trace v1.29.0 // indirect - golang.org/x/mod v0.20.0 // indirect - golang.org/x/net v0.33.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/mod v0.29.0 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/tools v0.38.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gopkg.in/yaml.v3 v3.0.1 // indirect mellium.im/sasl v0.3.1 // indirect diff --git a/pkg/go.sum b/pkg/go.sum index 87d55e9..a2cdc22 100644 --- a/pkg/go.sum +++ b/pkg/go.sum @@ -74,8 +74,6 @@ github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/ github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= github.com/DataDog/sketches-go v1.4.2 h1:gppNudE9d19cQ98RYABOetxIhpTCl4m7CnbRZjvVA/o= github.com/DataDog/sketches-go v1.4.2/go.mod h1:xJIXldczJyyjnbDop7ZZcLxJdV3+7Kra7H1KMgpgkLk= -github.com/Fisker-Inc/project-ai-can-go v1.3.1 h1:OjqeBun9kQwZA0VP61dANOtMqsdYoDjwBCnDOE4zZsE= -github.com/Fisker-Inc/project-ai-can-go v1.3.1/go.mod h1:8YrzRtqxRfiXEmvXpcQlUvmfCGLlpn+rJE02HiGUm/I= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= @@ -357,8 +355,7 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.17.1 h1:NE3C767s2ak2bweCZo3+rdP4U/HoyVXLv/X9f2gPS5g= -github.com/klauspost/compress v1.17.1/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -447,8 +444,7 @@ github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2 github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= -github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -514,6 +510,9 @@ github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= +github.com/twmb/franz-go v1.20.6 h1:TpQTt4QcixJ1cHEmQGPOERvTzo99s8jAutmS7rbSD6w= +github.com/twmb/franz-go/pkg/kadm v1.17.2 h1:g5f1sAxnTkYC6G96pV5u715HWhxd66hWaDZUAQ8xHY8= +github.com/twmb/franz-go/pkg/kmsg v1.12.0 h1:CbatD7ers1KzDNgJqPbKOq0Bz/WLBdsTH75wgzeVaPc= github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94= github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ= github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= @@ -592,8 +591,7 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -642,8 +640,7 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -676,8 +673,7 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -693,8 +689,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -740,8 +735,7 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -757,8 +751,7 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -806,8 +799,7 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -913,8 +905,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= -gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/gotraceui v0.2.0 h1:dmNsfQ9Vl3GwbiVD7Z8d/osC6WtGGrasyrC2suc4ZIQ= honnef.co/go/gotraceui v0.2.0/go.mod h1:qHo4/W75cA3bX0QQoSvDjbJa4R8mAyyFjbWAj63XElc= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/hashvault/mock_vault.go b/pkg/hashvault/mock_vault.go index 1ee2f7d..7d70d9f 100644 --- a/pkg/hashvault/mock_vault.go +++ b/pkg/hashvault/mock_vault.go @@ -1,6 +1,6 @@ package hashvault -import "fiskerinc.com/modules/common" +import "github.com/fiskerinc/cloud-services/pkg/common" type VaultMock struct { } diff --git a/pkg/hashvault/vault.go b/pkg/hashvault/vault.go index 1f19f3e..2f291ad 100644 --- a/pkg/hashvault/vault.go +++ b/pkg/hashvault/vault.go @@ -13,11 +13,11 @@ import ( "sync" "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/httpclient" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/httpclient" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/pkg/errors" ) diff --git a/pkg/health/clickhouse_test.go b/pkg/health/clickhouse_test.go index 193475f..001dbed 100644 --- a/pkg/health/clickhouse_test.go +++ b/pkg/health/clickhouse_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" - "fiskerinc.com/modules/health" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/health" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/pkg/errors" ) diff --git a/pkg/health/goroutines_test.go b/pkg/health/goroutines_test.go index e566a76..031503c 100644 --- a/pkg/health/goroutines_test.go +++ b/pkg/health/goroutines_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" - "fiskerinc.com/modules/health" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/health" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestGoRoutinesCheck(t *testing.T) { diff --git a/pkg/health/health.go b/pkg/health/health.go index f0e0381..f01819b 100644 --- a/pkg/health/health.go +++ b/pkg/health/health.go @@ -9,7 +9,7 @@ import ( "sync" "time" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/gomodule/redigo/redis" "github.com/pkg/errors" ) diff --git a/pkg/health/health_test.go b/pkg/health/health_test.go index d22478d..7def6cf 100644 --- a/pkg/health/health_test.go +++ b/pkg/health/health_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) const ( diff --git a/pkg/health/http_test.go b/pkg/health/http_test.go index 86d17d7..3444859 100644 --- a/pkg/health/http_test.go +++ b/pkg/health/http_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - "fiskerinc.com/modules/health" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/health" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestHTTPCheck(t *testing.T) { diff --git a/pkg/health/mongodb_test.go b/pkg/health/mongodb_test.go index f516969..a0d5764 100644 --- a/pkg/health/mongodb_test.go +++ b/pkg/health/mongodb_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" - "fiskerinc.com/modules/health" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/health" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/pkg/errors" ) diff --git a/pkg/health/options_test.go b/pkg/health/options_test.go index 58572d7..acb4d43 100644 --- a/pkg/health/options_test.go +++ b/pkg/health/options_test.go @@ -3,7 +3,7 @@ package health import ( "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestWithChecks(t *testing.T) { diff --git a/pkg/health/postgres_test.go b/pkg/health/postgres_test.go index c7d51f4..209b619 100644 --- a/pkg/health/postgres_test.go +++ b/pkg/health/postgres_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" - "fiskerinc.com/modules/health" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/health" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/go-pg/pg/v10/orm" "github.com/pkg/errors" diff --git a/pkg/health/redis.go b/pkg/health/redis.go index d8b9260..1ff9dce 100644 --- a/pkg/health/redis.go +++ b/pkg/health/redis.go @@ -3,8 +3,8 @@ package health import ( "context" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/redisv2" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/redisv2" ) type RedisHealth struct { diff --git a/pkg/health/redis_test.go b/pkg/health/redis_test.go index 6bd4628..e7469e0 100644 --- a/pkg/health/redis_test.go +++ b/pkg/health/redis_test.go @@ -4,10 +4,10 @@ import ( "context" "testing" - "fiskerinc.com/modules/health" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/redis/tester" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/health" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/redis/tester" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestRedisCheck(t *testing.T) { diff --git a/pkg/health/server.go b/pkg/health/server.go index 27c7e3f..46e162c 100644 --- a/pkg/health/server.go +++ b/pkg/health/server.go @@ -4,8 +4,8 @@ import ( "fmt" "net/http" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" ) type HealthCheckServer struct { diff --git a/pkg/health/server_test.go b/pkg/health/server_test.go index 05fa69c..8b51cbb 100644 --- a/pkg/health/server_test.go +++ b/pkg/health/server_test.go @@ -3,7 +3,7 @@ package health_test import ( "testing" - "fiskerinc.com/modules/health" + "github.com/fiskerinc/cloud-services/pkg/health" ) func TestHealthCheckServer(t *testing.T) { diff --git a/pkg/health/tmobile_test.go b/pkg/health/tmobile_test.go index 9d3031e..77934a7 100644 --- a/pkg/health/tmobile_test.go +++ b/pkg/health/tmobile_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" - "fiskerinc.com/modules/health" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/health" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/pkg/errors" ) diff --git a/pkg/httpclient/client_test.go b/pkg/httpclient/client_test.go index 18d68ca..cb79fd4 100644 --- a/pkg/httpclient/client_test.go +++ b/pkg/httpclient/client_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "fiskerinc.com/modules/httpclient/mock" + "github.com/fiskerinc/cloud-services/pkg/httpclient/mock" ) const ( diff --git a/pkg/httpclient/tester/http_test_case.go b/pkg/httpclient/tester/http_test_case.go index 6ee603c..3cf95c3 100644 --- a/pkg/httpclient/tester/http_test_case.go +++ b/pkg/httpclient/tester/http_test_case.go @@ -10,8 +10,8 @@ import ( "github.com/jeremywohl/flatten" "github.com/julienschmidt/httprouter" - th "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/validator" + th "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/validator" ) type HttpTestCase struct { diff --git a/pkg/httphandlers/auth_apitoken.go b/pkg/httphandlers/auth_apitoken.go index 2b7ec75..3b03677 100644 --- a/pkg/httphandlers/auth_apitoken.go +++ b/pkg/httphandlers/auth_apitoken.go @@ -7,14 +7,14 @@ import ( "strings" "sync" - "fiskerinc.com/modules/adminroles" - "fiskerinc.com/modules/cache" - "fiskerinc.com/modules/common/authproviders" - c "fiskerinc.com/modules/common/context" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/jwt" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils" + "github.com/fiskerinc/cloud-services/pkg/adminroles" + "github.com/fiskerinc/cloud-services/pkg/cache" + "github.com/fiskerinc/cloud-services/pkg/common/authproviders" + c "github.com/fiskerinc/cloud-services/pkg/common/context" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/jwt" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils" ) var ErrorNoAPITokenHeader = errors.New("no api token header") diff --git a/pkg/httphandlers/auth_apitoken_test.go b/pkg/httphandlers/auth_apitoken_test.go index c0a7b1f..a279896 100644 --- a/pkg/httphandlers/auth_apitoken_test.go +++ b/pkg/httphandlers/auth_apitoken_test.go @@ -7,16 +7,16 @@ import ( "testing" "time" - "fiskerinc.com/modules/adminroles" - "fiskerinc.com/modules/cache" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/common/authproviders" - c "fiskerinc.com/modules/common/context" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/db/queries/mocks" - "fiskerinc.com/modules/httphandlers" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/adminroles" + "github.com/fiskerinc/cloud-services/pkg/cache" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/common/authproviders" + c "github.com/fiskerinc/cloud-services/pkg/common/context" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks" + "github.com/fiskerinc/cloud-services/pkg/httphandlers" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) type testCaseAuthAPIToken struct { diff --git a/pkg/httphandlers/auth_base.go b/pkg/httphandlers/auth_base.go index 7ebcaaa..f4c23ed 100644 --- a/pkg/httphandlers/auth_base.go +++ b/pkg/httphandlers/auth_base.go @@ -3,9 +3,9 @@ package httphandlers import ( "context" - "fiskerinc.com/modules/adminroles" - "fiskerinc.com/modules/common/authproviders" - c "fiskerinc.com/modules/common/context" + "github.com/fiskerinc/cloud-services/pkg/adminroles" + "github.com/fiskerinc/cloud-services/pkg/common/authproviders" + c "github.com/fiskerinc/cloud-services/pkg/common/context" ) type AuthBase struct { diff --git a/pkg/httphandlers/auth_jwttoken.go b/pkg/httphandlers/auth_jwttoken.go index 58c8883..9f8ba68 100644 --- a/pkg/httphandlers/auth_jwttoken.go +++ b/pkg/httphandlers/auth_jwttoken.go @@ -5,13 +5,13 @@ import ( "errors" "net/http" - "fiskerinc.com/modules/adminroles" - "fiskerinc.com/modules/common/authproviders" - c "fiskerinc.com/modules/common/context" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/jwt" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils" + "github.com/fiskerinc/cloud-services/pkg/adminroles" + "github.com/fiskerinc/cloud-services/pkg/common/authproviders" + c "github.com/fiskerinc/cloud-services/pkg/common/context" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/jwt" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils" ) var errNoUsername = errors.New("no username") diff --git a/pkg/httphandlers/auth_jwttoken_test.go b/pkg/httphandlers/auth_jwttoken_test.go index af1e7a5..afad6b9 100644 --- a/pkg/httphandlers/auth_jwttoken_test.go +++ b/pkg/httphandlers/auth_jwttoken_test.go @@ -7,12 +7,12 @@ import ( "strings" "testing" - "fiskerinc.com/modules/adminroles" - "fiskerinc.com/modules/common/authproviders" - c "fiskerinc.com/modules/common/context" - "fiskerinc.com/modules/db/queries/mocks" - "fiskerinc.com/modules/httphandlers" - helper "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/adminroles" + "github.com/fiskerinc/cloud-services/pkg/common/authproviders" + c "github.com/fiskerinc/cloud-services/pkg/common/context" + "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks" + "github.com/fiskerinc/cloud-services/pkg/httphandlers" + helper "github.com/fiskerinc/cloud-services/pkg/testhelper" ) const expectedOkBody = "OK" diff --git a/pkg/httphandlers/base_url_handler.go b/pkg/httphandlers/base_url_handler.go index 3cd5e9b..deee36f 100644 --- a/pkg/httphandlers/base_url_handler.go +++ b/pkg/httphandlers/base_url_handler.go @@ -4,7 +4,7 @@ import ( "net/http" "strings" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" ) // ServiceBaseURL base url of service i.e. "/service" diff --git a/pkg/httphandlers/base_url_handler_test.go b/pkg/httphandlers/base_url_handler_test.go index 34a5a93..82745f9 100644 --- a/pkg/httphandlers/base_url_handler_test.go +++ b/pkg/httphandlers/base_url_handler_test.go @@ -5,7 +5,7 @@ import ( "reflect" "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestHandleBaseURL(t *testing.T) { diff --git a/pkg/httphandlers/context.go b/pkg/httphandlers/context.go index 43c87b7..21f7c2f 100644 --- a/pkg/httphandlers/context.go +++ b/pkg/httphandlers/context.go @@ -3,7 +3,7 @@ package httphandlers import ( "net/http" - "fiskerinc.com/modules/common/context" + "github.com/fiskerinc/cloud-services/pkg/common/context" ) const ClientIDContextKey context.ContextType = "client_id" diff --git a/pkg/httphandlers/log_request.go b/pkg/httphandlers/log_request.go index 2a541e1..a599d79 100644 --- a/pkg/httphandlers/log_request.go +++ b/pkg/httphandlers/log_request.go @@ -4,7 +4,7 @@ import ( "fmt" "net/http" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" ) func LogRequest(next http.HandlerFunc) http.HandlerFunc { diff --git a/pkg/httphandlers/log_request_test.go b/pkg/httphandlers/log_request_test.go index 2f41264..5aaf20f 100644 --- a/pkg/httphandlers/log_request_test.go +++ b/pkg/httphandlers/log_request_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/httphandlers" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/httphandlers" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestLogRequest(t *testing.T) { diff --git a/pkg/httphandlers/method_checker.go b/pkg/httphandlers/method_checker.go index 9a328c8..0dcaae7 100644 --- a/pkg/httphandlers/method_checker.go +++ b/pkg/httphandlers/method_checker.go @@ -4,7 +4,7 @@ import ( "fmt" "net/http" - "fiskerinc.com/modules/utils" + "github.com/fiskerinc/cloud-services/pkg/utils" ) // MethodAll to handle all http method diff --git a/pkg/httphandlers/method_checker_test.go b/pkg/httphandlers/method_checker_test.go index b7d25b4..3613044 100644 --- a/pkg/httphandlers/method_checker_test.go +++ b/pkg/httphandlers/method_checker_test.go @@ -5,7 +5,7 @@ import ( "net/http/httptest" "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestCheckMethod(t *testing.T) { diff --git a/pkg/httphandlers/panic_http_handler.go b/pkg/httphandlers/panic_http_handler.go index 42caf0d..7e1f0d5 100644 --- a/pkg/httphandlers/panic_http_handler.go +++ b/pkg/httphandlers/panic_http_handler.go @@ -5,8 +5,8 @@ import ( "net/http" "runtime/debug" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils" ) // PanicHandler Panic handler wrapper for http handlers diff --git a/pkg/httphandlers/panic_http_handler_test.go b/pkg/httphandlers/panic_http_handler_test.go index f3f4178..68c02d1 100644 --- a/pkg/httphandlers/panic_http_handler_test.go +++ b/pkg/httphandlers/panic_http_handler_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func checkPanic(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/httphandlers/parserequest_handler.go b/pkg/httphandlers/parserequest_handler.go index ae179f4..95fc855 100644 --- a/pkg/httphandlers/parserequest_handler.go +++ b/pkg/httphandlers/parserequest_handler.go @@ -5,7 +5,7 @@ import ( "encoding/xml" "net/http" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/pkg/errors" ) diff --git a/pkg/httphandlers/parserequest_handler_test.go b/pkg/httphandlers/parserequest_handler_test.go index c07870c..3bdce61 100644 --- a/pkg/httphandlers/parserequest_handler_test.go +++ b/pkg/httphandlers/parserequest_handler_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "fiskerinc.com/modules/common" - h "fiskerinc.com/modules/httphandlers" - th "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + h "github.com/fiskerinc/cloud-services/pkg/httphandlers" + th "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestParseRequestHandler(t *testing.T) { diff --git a/pkg/httphandlers/swagger_docs_handler.go b/pkg/httphandlers/swagger_docs_handler.go index e36f422..f048e7f 100644 --- a/pkg/httphandlers/swagger_docs_handler.go +++ b/pkg/httphandlers/swagger_docs_handler.go @@ -9,8 +9,8 @@ import ( httpSwagger "github.com/swaggo/http-swagger" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" ) var swaggerHandler func(http.ResponseWriter, *http.Request) diff --git a/pkg/httphandlers/swagger_docs_handler_test.go b/pkg/httphandlers/swagger_docs_handler_test.go index 276a011..89e33f8 100644 --- a/pkg/httphandlers/swagger_docs_handler_test.go +++ b/pkg/httphandlers/swagger_docs_handler_test.go @@ -4,7 +4,7 @@ import ( "net/http" "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) const expectedRedirectURL = "/docs/index.html" diff --git a/pkg/hwversion/setHWVersion.go b/pkg/hwversion/setHWVersion.go index 19b36ff..b84c599 100644 --- a/pkg/hwversion/setHWVersion.go +++ b/pkg/hwversion/setHWVersion.go @@ -3,10 +3,10 @@ package hwversion import ( "fmt" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/whereami" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/whereami" ) // Because of import loops, I moved this out to its own file and package diff --git a/pkg/hwversion/setHWVersion_test.go b/pkg/hwversion/setHWVersion_test.go index 9d9fc2c..8f6c99a 100644 --- a/pkg/hwversion/setHWVersion_test.go +++ b/pkg/hwversion/setHWVersion_test.go @@ -4,8 +4,8 @@ import ( "encoding/json" "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries/mocks" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks" "github.com/go-playground/assert/v2" ) diff --git a/pkg/immobilizer/immobilizerditto/immoDitto.go b/pkg/immobilizer/immobilizerditto/immoDitto.go index 2d779da..023ed86 100644 --- a/pkg/immobilizer/immobilizerditto/immoDitto.go +++ b/pkg/immobilizer/immobilizerditto/immoDitto.go @@ -9,11 +9,11 @@ import ( "sync" "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/dbc/state" - "fiskerinc.com/modules/immobilizer/immobilizershared" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redisv2" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/dbc/state" + "github.com/fiskerinc/cloud-services/pkg/immobilizer/immobilizershared" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redisv2" ) // I should put these parked values as a constant somewhere TODO diff --git a/pkg/immobilizer/immobilizershared/immobilizershared.go b/pkg/immobilizer/immobilizershared/immobilizershared.go index bd19e77..037758a 100644 --- a/pkg/immobilizer/immobilizershared/immobilizershared.go +++ b/pkg/immobilizer/immobilizershared/immobilizershared.go @@ -5,8 +5,8 @@ import ( "encoding/json" "time" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redisv2" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redisv2" ) // Grabs all the vehicle statuses from redis diff --git a/pkg/jwt/jwt_test.go b/pkg/jwt/jwt_test.go index c431721..4d93b14 100644 --- a/pkg/jwt/jwt_test.go +++ b/pkg/jwt/jwt_test.go @@ -5,7 +5,7 @@ import ( "os" "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) const expiredToken = "eyJraWQiOiJlUTNuZFJLaUVcL084VUZ5RHFsYjN0S1RzWG00SzVPMlc4NXd3VWkzT2tNZz0iLCJhbGciOiJSUzI1NiJ9.eyJhdF9oYXNoIjoiUGFqSzVNX0d0M3lta0ZOTjhOMUJydyIsInN1YiI6IjJkZDZmZWQ5LWU1ODItNDUxYi1hOTNiLTViOTQxMGRmYmM0MyIsImNvZ25pdG86Z3JvdXBzIjpbInVzLXdlc3QtMl9BV3dqTFh5bTJfQXp1cmVBRCJdLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC51cy13ZXN0LTIuYW1hem9uYXdzLmNvbVwvdXMtd2VzdC0yX0FXd2pMWHltMiIsImNvZ25pdG86dXNlcm5hbWUiOiJhenVyZWFkX2p3dUBmaXNrZXJpbmMuY29tIiwiYXVkIjoiN2NrMnRmb3FhdmM3MmM0NWhoN3RnZTQya2QiLCJpZGVudGl0aWVzIjpbeyJ1c2VySWQiOiJqd3VAZmlza2VyaW5jLmNvbSIsInByb3ZpZGVyTmFtZSI6IkF6dXJlQUQiLCJwcm92aWRlclR5cGUiOiJTQU1MIiwiaXNzdWVyIjoiaHR0cHM6XC9cL3N0cy53aW5kb3dzLm5ldFwvNWFhNGI2NDAtYzlmYy00YTliLWIzYTMtZDRhN2QwMDhmYjVlXC8iLCJwcmltYXJ5IjoidHJ1ZSIsImRhdGVDcmVhdGVkIjoiMTYxMjkwMjQxMzM4MyJ9XSwidG9rZW5fdXNlIjoiaWQiLCJhdXRoX3RpbWUiOjE2MTMxNTkzNDAsImV4cCI6MTYxMzE3OTk2MywiaWF0IjoxNjEzMTc2MzYzLCJlbWFpbCI6Imp3dUBmaXNrZXJpbmMuY29tIn0.lMIMjTaG11Y-Ft6wbuE9J3ic4EWmK-VgDXbcO583r8sckgKfWgpTI9Qy3zkkhmN0btDtQP4EqKI5afHKbDVu02wZk2y_y1adgWBxLtOJX3yCifxK99mCQUAjMvyBQ3_YbhLUexv3kvh047w0Fe3VjdPftfRwpfbmQsIYjWhF-MzDjdZJPXnXm3GjbtW6g3eKqA9AHg05ghBC4seatrDhHWKVSYS8DzmfJlsJCcdbdzZQ3fVLnYsVOU8-LK6B-IbpmpTUaobcF-acAwFaNPD56mGxI3xpnvExc9sM8ZBQD2NNhnHqY04p7mjaK2Wf4p73yLtI3SdW5SWy-w1reiaElQ" diff --git a/pkg/jwt/jwt_validator.go b/pkg/jwt/jwt_validator.go index ed67c71..140fdee 100644 --- a/pkg/jwt/jwt_validator.go +++ b/pkg/jwt/jwt_validator.go @@ -5,8 +5,8 @@ import ( "encoding/json" "time" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/lestrrat-go/jwx/jwk" "github.com/lestrrat-go/jwx/jwt" diff --git a/pkg/kafka/README.md b/pkg/kafka/README.md new file mode 100644 index 0000000..f922990 --- /dev/null +++ b/pkg/kafka/README.md @@ -0,0 +1,83 @@ +# Kafka Package + +Pure Go Kafka client using [franz-go](https://github.com/twmb/franz-go). + +## Why franz-go? + +- Pure Go - no CGO, no librdkafka dependency +- Builds to static binaries, works with scratch/distroless images +- Well-maintained, performant, full Kafka protocol support + +## Usage + +### Producer + +```go +producer, err := kafka.NewProducer(ctx) +if err != nil { + return err +} +defer producer.Close() + +// Sync produce +err = producer.Produce("my-topic", "key", myPayload, nil) + +// Async produce +err = producer.ProduceToChannel("my-topic", "key", myPayload) +``` + +### Consumer + +```go +consumer, err := kafka.NewConsumer("my-service") +if err != nil { + return err +} +defer consumer.Stop() + +// With handler +consumer.Consume([]string{"topic1", "topic2"}, func(key, value []byte) error { + // process message + return nil +}) + +// Or with channel +events := make(chan *kafka.Message) +go consumer.ConsumeToChannel([]string{"topic1"}, events) +for msg := range events { + // process msg +} +``` + +### Admin + +```go +// Ensure topics exist (creates if missing) +kafka.EnsureTopicsExist([]string{"topic1", "topic2"}) +``` + +## Configuration + +All config via environment variables: + +| Variable | Default | Description | +|----------|---------|-------------| +| `KAFKA_HOSTS` | `localhost:9092` | Broker addresses (comma-separated) | +| `KAFKA_SECURITY_PROTOCOL` | `plaintext` | `plaintext`, `ssl`, `sasl_plaintext`, `sasl_ssl` | +| `KAFKA_SASL_MECHANISMS` | `` | `PLAIN`, `SCRAM-SHA-256`, `SCRAM-SHA-512` | +| `KAFKA_SASL_USERNAME` | `` | SASL username | +| `KAFKA_SASL_PASSWORD` | `` | SASL password | +| `KAFKA_SESSION_TIMEOUT_MS` | `45000` | Consumer session timeout | +| `KAFKA_HEARTBEAT_INTERVAL_MS` | `3000` | Consumer heartbeat interval | +| `KAFKA_ENABLE_AUTO_COMMIT` | `true` | Auto-commit offsets | +| `KAFKA_AUTO_COMMIT_INTERVAL_MS` | `5000` | Auto-commit interval | +| `KAFKA_TIMEOUT` | `10000` | Poll timeout (ms) | +| `KAFKA_TOPIC_PARTITIONS` | `1` | Default partitions for new topics | +| `KAFKA_TOPIC_REPLICATION_FACTOR` | `1` | Default replication factor | + +## Migration from confluent-kafka-go + +The interfaces are compatible. Main changes: +- `*kafka.Message` → `*kafka.Message` (our wrapper type) +- `kafka.TopicPartition` → `kafka.TopicPartition` (our type) +- No more CGO build tags or librdkafka deps diff --git a/pkg/kafka/admin.go b/pkg/kafka/admin.go index d5bcae9..fa93b16 100644 --- a/pkg/kafka/admin.go +++ b/pkg/kafka/admin.go @@ -2,54 +2,52 @@ package kafka import ( "context" - "fmt" "time" - "fiskerinc.com/modules/utils/envtool" - - "github.com/confluentinc/confluent-kafka-go/v2/kafka" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/pkg/errors" + "github.com/twmb/franz-go/pkg/kadm" + "github.com/twmb/franz-go/pkg/kgo" ) -// AdminClient interface -type AdminClient interface { - CreateTopics(ctx context.Context, topics []kafka.TopicSpecification, options ...kafka.CreateTopicsAdminOption) ([]kafka.TopicResult, error) -} - -// Global -var ( - Admin AdminClient - kafkaHosts string = envtool.GetEnv("KAFKA_HOSTS", "localhost:9093") -) +// Global admin client +var adminClient *kadm.Client func init() { - Admin, _ = kafka.NewAdminClient(&kafka.ConfigMap{"bootstrap.servers": kafkaHosts}) + cfg := LoadConfig() + client, err := kgo.NewClient(kgo.SeedBrokers(cfg.Brokers...)) + if err != nil { + logger.Warn().Err(err).Msg("failed to create kafka admin client") + return + } + adminClient = kadm.NewClient(client) } -// EnsureTopicsExist checks Kafka for topic, if it doesn't exist it creates topic +// EnsureTopicsExist checks Kafka for topics, creates them if they don't exist func EnsureTopicsExist(topics []string) error { - ctx, cancel := context.WithCancel(context.Background()) + if adminClient == nil { + return errors.New("kafka admin client not initialized") + } + + ctx, cancel := context.WithTimeout(context.Background(), + time.Duration(envtool.GetEnvInt("KAFKA_TIMEOUT", 30))*time.Second) defer cancel() - s := fmt.Sprintf("%vs", envtool.GetEnvInt("KAFKA_TIMEOUT", 30)) - maxDur, err := time.ParseDuration(s) + partitions := int32(envtool.GetEnvInt("KAFKA_TOPIC_PARTITIONS", 1)) + replicationFactor := int16(envtool.GetEnvInt("KAFKA_TOPIC_REPLICATION_FACTOR", 1)) + + responses, err := adminClient.CreateTopics(ctx, partitions, replicationFactor, nil, topics...) if err != nil { return errors.WithStack(err) } - ts := make([]kafka.TopicSpecification, len(topics)) - for i, t := range topics { - ts[i] = kafka.TopicSpecification{ - Topic: t, - NumPartitions: envtool.GetEnvInt("KAFKA_TOPIC_PARTITIONS", 1), - ReplicationFactor: envtool.GetEnvInt("KAFKA_TOPIC_REPLICATION_FACTOR", 1), + // Check for errors (topic already exists is not an error) + for _, r := range responses { + if r.Err != nil && r.Err.Error() != "TOPIC_ALREADY_EXISTS" { + logger.Warn().Err(r.Err).Msgf("failed to create topic %s", r.Topic) } } - _, err = Admin.CreateTopics(ctx, ts, kafka.SetAdminOperationTimeout(maxDur)) - if err != nil { - return errors.WithStack(err) - } - return nil } diff --git a/pkg/kafka/admin_test.go b/pkg/kafka/admin_test.go index c7c3bff..f78c4dd 100644 --- a/pkg/kafka/admin_test.go +++ b/pkg/kafka/admin_test.go @@ -5,8 +5,8 @@ import ( "errors" "testing" - "fiskerinc.com/modules/kafka/mock" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/kafka/mock" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/confluentinc/confluent-kafka-go/v2/kafka" ) diff --git a/pkg/kafka/async_producer.go b/pkg/kafka/async_producer.go index 4b4f3fd..d3c26a3 100644 --- a/pkg/kafka/async_producer.go +++ b/pkg/kafka/async_producer.go @@ -3,168 +3,200 @@ package kafka import ( "context" "encoding/json" + "sync" - "fiskerinc.com/modules/logger" - - "github.com/confluentinc/confluent-kafka-go/v2/kafka" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/pkg/errors" + "github.com/twmb/franz-go/pkg/kgo" ) +// AsyncProducer is an async-first producer implementation type AsyncProducer struct { - producer *kafka.Producer + client *kgo.Client + ctx context.Context + cancel context.CancelFunc + pending int + pendingMu sync.Mutex } -// NewProducer serves as factory method for producer to kafka +// NewAsyncProducer serves as factory method for async producer to kafka func NewAsyncProducer(ctx context.Context) (ProducerInterface, error) { - var producer *kafka.Producer - configuration := loadKafkaProducerConfig() - producer, err := kafka.NewProducer( - &configuration, - // kafkaTrace.WithContext(ctx), + cfg := LoadConfig() + opts := buildClientOpts(cfg) + + // Async producer options - optimized for throughput + opts = append(opts, + kgo.ProducerBatchCompression(kgo.NoCompression()), + kgo.AllowAutoTopicCreation(), + kgo.ProducerLinger(5), // 5ms linger for batching ) - logger.Info().Msgf("NewAsyncProducer hosts: %s", kafkaHosts) + + client, err := kgo.NewClient(opts...) if err != nil { return nil, errors.WithStack(err) } - return &AsyncProducer{producer: producer}, nil + + logger.Info().Msgf("NewAsyncProducer hosts: %v", cfg.Brokers) + + pctx, cancel := context.WithCancel(ctx) + return &AsyncProducer{ + client: client, + ctx: pctx, + cancel: cancel, + }, nil } -// SetProducer sets the producer instance -func (p *AsyncProducer) SetProducer(producer *kafka.Producer) { - p.producer = producer -} - -// Len returns len of messages in queue. +// Len returns approximate pending message count func (p *AsyncProducer) Len() int { - return p.producer.Len() + p.pendingMu.Lock() + defer p.pendingMu.Unlock() + return p.pending } -// Flush calls producer's Flush function. +// Flush waits for all buffered records to be flushed func (p *AsyncProducer) Flush(timeoutMs int) int { - return p.producer.Flush(timeoutMs) + ctx := p.ctx + if timeoutMs > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(p.ctx, durationMs(timeoutMs)) + defer cancel() + } + if err := p.client.Flush(ctx); err != nil { + logger.Warn().Err(err).Msg("flush incomplete") + return p.Len() + } + return 0 } -// Produce sends a Kafka Message to Kafka +// Produce sends a JSON-encoded message asynchronously func (p *AsyncProducer) Produce(topic string, key string, payload interface{}, headers map[string][]byte) error { v, err := json.Marshal(payload) if err != nil { return errors.WithStack(err) } - return p.ProduceBinary(topic, key, v, headers) } -func (p *AsyncProducer) makeHeaders(headers map[string][]byte) []kafka.Header { - if headers == nil { - return nil - } - - i := 0 - result := make([]kafka.Header, len(headers)) - - for key, value := range headers { - result[i] = kafka.Header{ - Key: key, - } - copy(result[i].Value, value) - i++ - } - - return result -} - +// ProduceBinary sends a binary message asynchronously func (p *AsyncProducer) ProduceBinary(topic string, key string, data []byte, headers map[string][]byte) error { - km := kafka.Message{ - TopicPartition: kafka.TopicPartition{Topic: &topic, Partition: kafka.PartitionAny}, - Key: []byte(key), - Value: data, - } - if headers != nil { - km.Headers = p.makeHeaders(headers) + record := &kgo.Record{ + Topic: topic, + Key: []byte(key), + Value: data, + Headers: makeHeaders(headers), } - err := p.producer.Produce(&km, nil) - if err != nil { - // error handle inability to connect to kafka - return errors.WithStack(err) - } + p.pendingMu.Lock() + p.pending++ + p.pendingMu.Unlock() - return err + p.client.Produce(p.ctx, record, func(r *kgo.Record, err error) { + p.pendingMu.Lock() + p.pending-- + p.pendingMu.Unlock() + + if err != nil { + logger.Error().Err(err).Msgf("Async delivery failed to topic %s", topic) + } + }) + + return nil } -// ProduceToChannel sends a list of Kafka Messages to Kafka +// ProduceToChannel sends a message asynchronously func (p *AsyncProducer) ProduceToChannel(topic string, key string, payload interface{}) error { v, err := json.Marshal(payload) if err != nil { return errors.WithStack(err) } - km := kafka.Message{ - TopicPartition: kafka.TopicPartition{Topic: &topic, Partition: kafka.PartitionAny}, - Key: []byte(key), - Value: v, + record := &kgo.Record{ + Topic: topic, + Key: []byte(key), + Value: v, } - p.producer.ProduceChannel() <- &km + p.pendingMu.Lock() + p.pending++ + p.pendingMu.Unlock() + + p.client.Produce(p.ctx, record, func(r *kgo.Record, err error) { + p.pendingMu.Lock() + p.pending-- + p.pendingMu.Unlock() + + if err != nil { + logger.Error().Err(err).Msgf("Async delivery failed to topic %s", topic) + } + }) + return nil } +// ProduceBinaryToChannel sends binary data asynchronously func (p *AsyncProducer) ProduceBinaryToChannel(topic string, key string, payload []byte) error { - km := kafka.Message{ - TopicPartition: kafka.TopicPartition{Topic: &topic, Partition: kafka.PartitionAny}, - Key: []byte(key), - Value: payload, + record := &kgo.Record{ + Topic: topic, + Key: []byte(key), + Value: payload, } - p.producer.ProduceChannel() <- &km + p.pendingMu.Lock() + p.pending++ + p.pendingMu.Unlock() + + p.client.Produce(p.ctx, record, func(r *kgo.Record, err error) { + p.pendingMu.Lock() + p.pending-- + p.pendingMu.Unlock() + + if err != nil { + logger.Error().Err(err).Msgf("Async delivery failed to topic %s", topic) + } + }) + return nil } -// ProduceSignalBatch sends a specified batch of CAN signals -// -// NOTE: does NOT produce proper JSON, Value is custom for Clickhouse +// ProduceSignalBatch sends a batch of CAN signals (custom format for Clickhouse) func (p *AsyncProducer) ProduceSignalBatch(topic string, key string, payload interface{}) error { v, err := json.Marshal(payload) if err != nil { return errors.WithStack(err) } - km := kafka.Message{ - TopicPartition: kafka.TopicPartition{Topic: &topic, Partition: kafka.PartitionAny}, - Key: []byte(key), - Value: v[1 : len(v)-1], + // Strip JSON array brackets for Clickhouse format + record := &kgo.Record{ + Topic: topic, + Key: []byte(key), + Value: v[1 : len(v)-1], } - err = p.producer.Produce(&km, nil) - if err != nil { - // error handle inability to connect to kafka - return errors.WithStack(err) - } + p.pendingMu.Lock() + p.pending++ + p.pendingMu.Unlock() + + p.client.Produce(p.ctx, record, func(r *kgo.Record, err error) { + p.pendingMu.Lock() + p.pending-- + p.pendingMu.Unlock() + + if err != nil { + logger.Error().Err(err).Msgf("Async delivery failed to topic %s", topic) + } + }) return nil } -// ReadEvents listens to producer's events which are emitted as responses to producing events +// ReadEvents listens to producer events (callbacks handle this in franz-go) func (p *AsyncProducer) ReadEvents() { - for e := range p.producer.Events() { - switch m := e.(type) { - case *kafka.Message: - if m.TopicPartition.Error != nil { - logger.Error(). - Err(m.TopicPartition.Error). - Send() - } - case kafka.Error: - logger.Error(). - Str("err", m.String()). - Send() - } - } + // franz-go uses callbacks, kept for interface compatibility } // Close the Kafka Producer func (p *AsyncProducer) Close() { - p.producer.Flush(0) - p.producer.Close() - p.producer = nil + p.Flush(5000) + p.cancel() + p.client.Close() } diff --git a/pkg/kafka/base_consumer.go b/pkg/kafka/base_consumer.go index 0361e7a..d9a1c6c 100644 --- a/pkg/kafka/base_consumer.go +++ b/pkg/kafka/base_consumer.go @@ -3,209 +3,262 @@ package kafka import ( "context" "sync" + "time" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" - - "github.com/confluentinc/confluent-kafka-go/v2/kafka" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/pkg/errors" + "github.com/twmb/franz-go/pkg/kgo" ) -const KafkaTimeout = 30000 //ms -// BaseConsumerInterface interface for Consumer utility -// -// NOTE: DOES NOT AUTO COMMIT OFFSETS +const KafkaTimeout = 30000 // ms + +// BaseConsumerInterface interface for Consumer utility with manual commit control type BaseConsumerInterface interface { Check(ctx context.Context) error - Commit(message *kafka.Message) ([]kafka.TopicPartition, error) - CommitOffsets(offsets []kafka.TopicPartition) ([]kafka.TopicPartition, error) - ConsumeToChannel(topics []string, events chan *kafka.Message) error - ConsumeOrRebalancedCatch(topics []string, events chan *kafka.Message, reb chan struct{}) error - LastOffsetConsumed(topic string, partition int32) (kafka.Offset, error) - Seek(topic string, offset kafka.Offset) error + Commit(message *Message) ([]TopicPartition, error) + CommitOffsets(offsets []TopicPartition) ([]TopicPartition, error) + ConsumeToChannel(topics []string, events chan *Message) error + ConsumeOrRebalancedCatch(topics []string, events chan *Message, reb chan struct{}) error + LastOffsetConsumed(topic string, partition int32) (int64, error) + Seek(topic string, offset int64) error Stop() Subscribe(topics []string) error } -// NoncommitalConsumer utility to produce messages +// BaseConsumer utility for consuming with manual offset control type BaseConsumer struct { - consumer *kafka.Consumer + client *kgo.Client running bool - timeout int + timeout time.Duration connected bool connectedLock sync.RWMutex pollingError error + ctx context.Context + cancel context.CancelFunc + lastOffsets map[string]map[int32]int64 + offsetsMu sync.RWMutex } -// NewConsumer serves as factory method for consumer to kafka -func NewBaseConsumer(serviceName string, minBufSize int, fetchMaxWaitTimems int, auto_commit bool) (BaseConsumerInterface, error) { - var consumer *kafka.Consumer - kafkaConfigMap := loadKafkaConsumerConfig(serviceName) - kafkaConfigMap.SetKey("enable.auto.commit", auto_commit) - if minBufSize != -1 { - kafkaConfigMap.SetKey("fetch.min.bytes", minBufSize) +// NewBaseConsumer serves as factory method for consumer with manual commit +func NewBaseConsumer(serviceName string, minBufSize int, fetchMaxWaitTimems int, autoCommit bool) (BaseConsumerInterface, error) { + cfg := LoadConfig() + opts := buildClientOpts(cfg) + + // Consumer-specific options + opts = append(opts, + kgo.ConsumerGroup(serviceName), + kgo.SessionTimeout(cfg.SessionTimeout), + kgo.HeartbeatInterval(cfg.HeartbeatInterval), + ) + + if autoCommit { + opts = append(opts, kgo.AutoCommitInterval( + time.Duration(envtool.GetEnvInt("KAFKA_AUTO_COMMIT_INTERVAL_MS", 5000))*time.Millisecond, + )) + } else { + opts = append(opts, kgo.DisableAutoCommit()) } - consumer, err := kafka.NewConsumer(&kafkaConfigMap) - kafkaHosts := envtool.GetEnv("KAFKA_HOSTS", "localhost:9093") - logger.Info().Msgf("NewNoncommitalConsumer hosts: %s", kafkaHosts) + if minBufSize > 0 { + opts = append(opts, kgo.FetchMinBytes(int32(minBufSize))) + } + + if fetchMaxWaitTimems > 0 { + opts = append(opts, kgo.FetchMaxWait(time.Duration(fetchMaxWaitTimems)*time.Millisecond)) + } + + client, err := kgo.NewClient(opts...) if err != nil { return nil, errors.WithStack(err) } + + logger.Info().Msgf("NewBaseConsumer hosts: %v group: %s", cfg.Brokers, serviceName) + + ctx, cancel := context.WithCancel(context.Background()) return &BaseConsumer{ - consumer: consumer, - timeout: envtool.GetEnvInt("KAFKA_TIMEOUT", 10000), + client: client, + timeout: time.Duration(envtool.GetEnvInt("KAFKA_TIMEOUT", 10000)) * time.Millisecond, + ctx: ctx, + cancel: cancel, + lastOffsets: make(map[string]map[int32]int64), }, nil } +// Subscribe subscribes to topics func (c *BaseConsumer) Subscribe(topics []string) error { - err := c.consumer.SubscribeTopics(topics, nil) - if err != nil { - return errors.WithStack(err) - } + c.client.AddConsumeTopics(topics...) return nil } // ConsumeToChannel runs a poll loop which waits for messages to consume -func (c *BaseConsumer) ConsumeToChannel(topics []string, events chan *kafka.Message) error { +func (c *BaseConsumer) ConsumeToChannel(topics []string, events chan *Message) error { c.running = true - c.setConnected(true) defer c.setConnected(false) for c.running { - e := c.consumer.Poll(c.timeout) - - switch msg := e.(type) { - case *kafka.Message: - events <- msg - msg = nil - c.setConnected(true) - case kafka.Error: - c.setConnected(false) - logger.Error().Msg(e.String()) - c.pollingError = errors.WithStack(errors.New(e.String())) - return c.pollingError + fetches := c.client.PollFetches(c.ctx) + if errs := fetches.Errors(); len(errs) > 0 { + for _, e := range errs { + if e.Err == context.Canceled { + return nil + } + logger.Error().Err(e.Err).Msgf("fetch error on %s", e.Topic) + c.setConnected(false) + c.pollingError = errors.WithStack(e.Err) + return c.pollingError + } + continue } + c.setConnected(true) + fetches.EachRecord(func(r *kgo.Record) { + c.trackOffset(r.Topic, r.Partition, r.Offset) + events <- recordToMessage(r) + }) } return nil } -// ConsumeOrRebalancedCatch runs a poll loop which waits for messages to consume -func (c *BaseConsumer) ConsumeOrRebalancedCatch(topics []string, events chan *kafka.Message, reb chan struct{}) error { +// ConsumeOrRebalancedCatch runs a poll loop with rebalance notification +func (c *BaseConsumer) ConsumeOrRebalancedCatch(topics []string, events chan *Message, reb chan struct{}) error { c.running = true - c.setConnected(true) defer c.setConnected(false) - c.pollingError = nil + for c.running { - e := c.consumer.Poll(c.timeout) - switch msg := e.(type) { - case *kafka.Message: - events <- msg - msg = nil - c.setConnected(true) - - case kafka.AssignedPartitions: - reb <- struct{}{} - c.setConnected(true) - - case kafka.Error: - c.setConnected(false) - c.pollingError = errors.WithStack(errors.New(e.String())) - logger.Error().Err(c.pollingError).Send() - return c.pollingError + fetches := c.client.PollFetches(c.ctx) + if errs := fetches.Errors(); len(errs) > 0 { + for _, e := range errs { + if e.Err == context.Canceled { + return nil + } + logger.Error().Err(e.Err).Send() + c.setConnected(false) + c.pollingError = errors.WithStack(e.Err) + return c.pollingError + } + continue } + + c.setConnected(true) + fetches.EachRecord(func(r *kgo.Record) { + c.trackOffset(r.Topic, r.Partition, r.Offset) + events <- recordToMessage(r) + }) } return nil } -// Seek provides a wrapper for private consumer method "Seek" -func (c *BaseConsumer) Seek(topic string, offset kafka.Offset) error { - err := c.consumer.Seek(kafka.TopicPartition{ - Topic: &topic, - Offset: offset, - }, c.timeout) - if err != nil { - return errors.WithStack(err) +// Seek sets the offset for a topic partition +func (c *BaseConsumer) Seek(topic string, offset int64) error { + offsets := map[string]map[int32]kgo.Offset{ + topic: {0: kgo.NewOffset().At(offset)}, } + c.client.AddConsumePartitions(offsets) return nil } -func (c *BaseConsumer) commitWithoutMessage() ([]kafka.TopicPartition, error) { - partitions, err := c.consumer.Commit() - if err != nil { - return partitions, errors.WithStack(err) - } - return partitions, nil -} - -func (c *BaseConsumer) Commit(message *kafka.Message) ([]kafka.TopicPartition, error) { +// Commit commits the offset for a message +func (c *BaseConsumer) Commit(message *Message) ([]TopicPartition, error) { if message == nil { - return c.commitWithoutMessage() + // Commit all uncommitted + if err := c.client.CommitUncommittedOffsets(c.ctx); err != nil { + return nil, errors.WithStack(err) + } + return nil, nil } - partitions, err := c.consumer.CommitMessage(message) - if err != nil { - return partitions, errors.WithStack(err) + + // Mark the record as consumed and commit + c.client.MarkCommitRecords(&kgo.Record{ + Topic: message.Topic, + Partition: message.Partition, + Offset: message.Offset, + }) + + if err := c.client.CommitUncommittedOffsets(c.ctx); err != nil { + return nil, errors.WithStack(err) } - return partitions, nil + + return []TopicPartition{{ + Topic: message.Topic, + Partition: message.Partition, + Offset: message.Offset + 1, + }}, nil } -func (c *BaseConsumer) LastOffsetConsumed(topic string, partition int32) (kafka.Offset, error) { - partitions, err := c.consumer.Position([]kafka.TopicPartition{{ - Topic: &topic, - Partition: partition, - }}) - if err != nil { - return -1, errors.WithStack(err) - } else if len(partitions) != 1 { - return -1, errors.Errorf("no subscription for topic %s", topic) +// CommitOffsets commits specific offsets +func (c *BaseConsumer) CommitOffsets(offsets []TopicPartition) ([]TopicPartition, error) { + for _, o := range offsets { + c.client.MarkCommitRecords(&kgo.Record{ + Topic: o.Topic, + Partition: o.Partition, + Offset: o.Offset - 1, // MarkCommitRecords expects the record offset, not next + }) } - return partitions[0].Offset, nil + + if err := c.client.CommitUncommittedOffsets(c.ctx); err != nil { + return nil, errors.WithStack(err) + } + + return offsets, nil } -// Stop stops the poll loop running -func (c *BaseConsumer) Stop() { - if c.consumer != nil { - c.running = false - if err := c.consumer.Close(); err != nil { - logger.Warn().Err(err).Send() +// LastOffsetConsumed returns the last consumed offset for a partition +func (c *BaseConsumer) LastOffsetConsumed(topic string, partition int32) (int64, error) { + c.offsetsMu.RLock() + defer c.offsetsMu.RUnlock() + + if topicOffsets, ok := c.lastOffsets[topic]; ok { + if offset, ok := topicOffsets[partition]; ok { + return offset, nil } } + return -1, errors.Errorf("no subscription for topic %s partition %d", topic, partition) } -// very basic implementation for health_check +// Stop stops the poll loop +func (c *BaseConsumer) Stop() { + c.running = false + c.cancel() + if c.client != nil { + c.client.Close() + } +} + +// Check verifies consumer connectivity func (c *BaseConsumer) Check(ctx context.Context) error { if c.pollingError != nil { return c.pollingError } - if !c.isConnected() { return errors.New("consumer isn't connected to Kafka") } - return nil } func (c *BaseConsumer) setConnected(cntd bool) { c.connectedLock.Lock() defer c.connectedLock.Unlock() - c.connected = cntd } func (c *BaseConsumer) isConnected() bool { c.connectedLock.RLock() defer c.connectedLock.RUnlock() - return c.connected } -// passthrough function for CommitOffsets() -func (c *BaseConsumer) CommitOffsets(offsets []kafka.TopicPartition) ([]kafka.TopicPartition, error) { - return c.consumer.CommitOffsets(offsets) +func (c *BaseConsumer) trackOffset(topic string, partition int32, offset int64) { + c.offsetsMu.Lock() + defer c.offsetsMu.Unlock() + + if c.lastOffsets[topic] == nil { + c.lastOffsets[topic] = make(map[int32]int64) + } + c.lastOffsets[topic][partition] = offset } diff --git a/pkg/kafka/base_consumer_test.go b/pkg/kafka/base_consumer_test.go index 9a5ce1b..7c41cb8 100644 --- a/pkg/kafka/base_consumer_test.go +++ b/pkg/kafka/base_consumer_test.go @@ -3,7 +3,7 @@ package kafka import ( "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestNonCommitalConsumer(t *testing.T) { diff --git a/pkg/kafka/config.go b/pkg/kafka/config.go new file mode 100644 index 0000000..cda2915 --- /dev/null +++ b/pkg/kafka/config.go @@ -0,0 +1,45 @@ +package kafka + +import ( + "strings" + "time" + + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" + "github.com/twmb/franz-go/pkg/kgo" +) + +// Config holds kafka connection configuration +type Config struct { + Brokers []string + SecurityProtocol string + SessionTimeout time.Duration + HeartbeatInterval time.Duration +} + +// LoadConfig loads kafka configuration from environment +func LoadConfig() Config { + hosts := envtool.GetEnv("KAFKA_HOSTS", "localhost:9092") + brokers := strings.Split(hosts, ",") + for i := range brokers { + brokers[i] = strings.TrimSpace(brokers[i]) + } + + return Config{ + Brokers: brokers, + SecurityProtocol: envtool.GetEnv("KAFKA_SECURITY_PROTOCOL", "plaintext"), + SessionTimeout: time.Duration(envtool.GetEnvInt("KAFKA_SESSION_TIMEOUT_MS", 45000)) * time.Millisecond, + HeartbeatInterval: time.Duration(envtool.GetEnvInt("KAFKA_HEARTBEAT_INTERVAL_MS", 3000)) * time.Millisecond, + } +} + +// buildClientOpts creates common kgo.Opt slice from config +func buildClientOpts(cfg Config) []kgo.Opt { + opts := []kgo.Opt{ + kgo.SeedBrokers(cfg.Brokers...), + } + + // SASL can be added here if needed in the future + // For now we use PLAINTEXT (no auth) for the mini cluster + + return opts +} diff --git a/pkg/kafka/consumer.go b/pkg/kafka/consumer.go index 823fed8..25a4f60 100644 --- a/pkg/kafka/consumer.go +++ b/pkg/kafka/consumer.go @@ -3,240 +3,309 @@ package kafka import ( "context" "sync" + "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" - - "github.com/confluentinc/confluent-kafka-go/v2/kafka" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/pkg/errors" + "github.com/twmb/franz-go/pkg/kadm" + "github.com/twmb/franz-go/pkg/kgo" ) +// Message wraps kgo.Record for compatibility +type Message struct { + Topic string + Partition int32 + Offset int64 + Key []byte + Value []byte + Headers map[string][]byte +} + +// TopicPartition represents a topic-partition pair +type TopicPartition struct { + Topic string + Partition int32 + Offset int64 +} + +// Metadata holds topic metadata +type Metadata struct { + Topics map[string]TopicMetadata +} + +// TopicMetadata holds partition info for a topic +type TopicMetadata struct { + Partitions []PartitionMetadata +} + +// PartitionMetadata holds partition details +type PartitionMetadata struct { + ID int32 + Leader int32 +} + // ConsumerInterface interface for Consumer utility type ConsumerInterface interface { Consume(topics []string, handler func([]byte, []byte) error) error - ConsumeToChannel(topics []string, events chan *kafka.Message) error + ConsumeToChannel(topics []string, events chan *Message) error ConsumeToChannelJson(topics []string, events chan common.EventRawJSON) error - ConsumePartitionsToChannel(partitions []kafka.TopicPartition, events chan *kafka.Message) error - GetMetadata(topic string) (*kafka.Metadata, error) + ConsumePartitionsToChannel(partitions []TopicPartition, events chan *Message) error + GetMetadata(topic string) (*Metadata, error) Check(ctx context.Context) error Stop() } -// Consumer utility to produce messages +// Consumer utility to consume messages type Consumer struct { - consumer *kafka.Consumer + client *kgo.Client running bool - timeout int + timeout time.Duration connected bool connectedLock sync.RWMutex + ctx context.Context + cancel context.CancelFunc } // NewConsumer serves as factory method for consumer to kafka func NewConsumer(serviceName string) (ConsumerInterface, error) { - var consumer *kafka.Consumer - config := loadKafkaConsumerConfig(serviceName) + cfg := LoadConfig() + opts := buildClientOpts(cfg) - consumer, err := kafka.NewConsumer(&config) - logger.Info().Msgf("NewConsumer hosts: %s", envtool.GetEnv("KAFKA_HOSTS", "localhost:9093")) + // Consumer-specific options + opts = append(opts, + kgo.ConsumerGroup(serviceName), + kgo.SessionTimeout(cfg.SessionTimeout), + kgo.HeartbeatInterval(cfg.HeartbeatInterval), + ) + + if envtool.GetEnvBool("KAFKA_ENABLE_AUTO_COMMIT", true) { + opts = append(opts, kgo.AutoCommitInterval( + time.Duration(envtool.GetEnvInt("KAFKA_AUTO_COMMIT_INTERVAL_MS", 5000))*time.Millisecond, + )) + } else { + opts = append(opts, kgo.DisableAutoCommit()) + } + + client, err := kgo.NewClient(opts...) if err != nil { return nil, errors.WithStack(err) } - return &Consumer{ - consumer: consumer, - timeout: envtool.GetEnvInt("KAFKA_TIMEOUT", -1), - }, nil -} + logger.Info().Msgf("NewConsumer hosts: %v group: %s", cfg.Brokers, serviceName) -// SetConsumer sets the consumer -func (c *Consumer) SetConsumer(consumer *kafka.Consumer) { - c.consumer = consumer + ctx, cancel := context.WithCancel(context.Background()) + return &Consumer{ + client: client, + timeout: time.Duration(envtool.GetEnvInt("KAFKA_TIMEOUT", 10000)) * time.Millisecond, + ctx: ctx, + cancel: cancel, + }, nil } // Consume runs a poll loop which waits for messages to consume func (c *Consumer) Consume(topics []string, handler func([]byte, []byte) error) error { - if err := c.consumer.SubscribeTopics(topics, nil); err != nil { - return errors.WithStack(err) - } - + c.client.AddConsumeTopics(topics...) c.setConnected(true) defer c.setConnected(false) c.running = true - connected := true for c.running { - e := c.consumer.Poll(c.timeout) - switch msg := e.(type) { - case *kafka.Message: - if !connected { - connected = true - c.setConnected(true) - } - - if err := handler(msg.Key, msg.Value); err != nil { - logger.Warn().Err(err).Send() - } - case kafka.Error: - if connected { - connected = false + fetches := c.client.PollFetches(c.ctx) + if errs := fetches.Errors(); len(errs) > 0 { + for _, e := range errs { + if e.Err == context.Canceled { + return nil + } + logger.Error().Err(e.Err).Msgf("fetch error on %s", e.Topic) c.setConnected(false) } - - logger.Error().Msg(e.String()) + continue } + + c.setConnected(true) + fetches.EachRecord(func(r *kgo.Record) { + if err := handler(r.Key, r.Value); err != nil { + logger.Warn().Err(err).Send() + } + }) } return nil } // ConsumeToChannel runs a poll loop which waits for messages to consume -func (c *Consumer) ConsumeToChannel(topics []string, events chan *kafka.Message) error { - err := c.consumer.SubscribeTopics(topics, nil) - if err != nil { - return errors.WithStack(err) - } - +func (c *Consumer) ConsumeToChannel(topics []string, events chan *Message) error { + c.client.AddConsumeTopics(topics...) c.setConnected(true) defer c.setConnected(false) c.running = true - connected := true for c.running { - e := c.consumer.Poll(c.timeout) - switch msg := e.(type) { - case *kafka.Message: - if !connected { - connected = true - c.setConnected(true) - } - - events <- msg - case kafka.Error: - if connected { - connected = false + fetches := c.client.PollFetches(c.ctx) + if errs := fetches.Errors(); len(errs) > 0 { + for _, e := range errs { + if e.Err == context.Canceled { + return nil + } + logger.Error().Err(e.Err).Msgf("fetch error on %s", e.Topic) c.setConnected(false) } - - logger.Error().Msg(e.String()) + continue } + + c.setConnected(true) + fetches.EachRecord(func(r *kgo.Record) { + events <- recordToMessage(r) + }) } + return nil } +// ConsumeToChannelJson runs a poll loop for JSON events func (c *Consumer) ConsumeToChannelJson(topics []string, events chan common.EventRawJSON) error { - err := c.consumer.SubscribeTopics(topics, nil) - if err != nil { - return errors.WithStack(err) - } - + c.client.AddConsumeTopics(topics...) c.setConnected(true) defer c.setConnected(false) - connected := true c.running = true for c.running { - e := c.consumer.Poll(c.timeout) - switch msg := e.(type) { - case *kafka.Message: - if !connected { - connected = true - c.setConnected(true) + fetches := c.client.PollFetches(c.ctx) + if errs := fetches.Errors(); len(errs) > 0 { + for _, e := range errs { + if e.Err == context.Canceled { + return nil + } + logger.Error().Err(e.Err).Send() + c.setConnected(false) } + continue + } + c.setConnected(true) + fetches.EachRecord(func(r *kgo.Record) { events <- common.EventRawJSON{ - Topic: *msg.TopicPartition.Topic, - Key: string(msg.Key), - Payload: msg.Value, + Topic: r.Topic, + Key: string(r.Key), + Payload: r.Value, } - case kafka.Error: - if connected { - connected = false - c.setConnected(false) - } - - logger.Error().Err(errors.New(e.String())).Send() - } + }) } return nil } -// ConsumePartitionsToChannel runs a poll loop which waits for messages to consume -func (c *Consumer) ConsumePartitionsToChannel(partitions []kafka.TopicPartition, events chan *kafka.Message) error { +// ConsumePartitionsToChannel consumes from specific partitions +func (c *Consumer) ConsumePartitionsToChannel(partitions []TopicPartition, events chan *Message) error { if len(partitions) == 0 { - return errors.WithStack(errors.New("no partitions provided")) + return errors.New("no partitions provided") } - topic := "attendant_service" - m, err := c.consumer.GetMetadata(&topic, false, 200) - _ = m - err = c.consumer.Assign(partitions) - if err != nil { - return errors.WithStack(err) + + // Build partition map for direct assignment + offsets := make(map[string]map[int32]kgo.Offset) + for _, p := range partitions { + if offsets[p.Topic] == nil { + offsets[p.Topic] = make(map[int32]kgo.Offset) + } + offsets[p.Topic][p.Partition] = kgo.NewOffset().At(p.Offset) } + c.client.AddConsumePartitions(offsets) c.setConnected(true) defer c.setConnected(false) c.running = true - connected := true for c.running { - e := <-c.consumer.Events() - if e != nil { - logger.Error().Msg(e.String()) - } - switch msg := e.(type) { - case *kafka.Message: - if !connected { - connected = true - c.setConnected(true) - } - - events <- msg - case kafka.Error: - if connected { - connected = false + fetches := c.client.PollFetches(c.ctx) + if errs := fetches.Errors(); len(errs) > 0 { + for _, e := range errs { + if e.Err == context.Canceled { + return nil + } + logger.Error().Err(e.Err).Send() c.setConnected(false) } - - logger.Error().Msg(e.String()) + continue } + + c.setConnected(true) + fetches.EachRecord(func(r *kgo.Record) { + events <- recordToMessage(r) + }) } + return nil } -func (c *Consumer) GetMetadata(topic string) (*kafka.Metadata, error) { - return c.consumer.GetMetadata(&topic, false, 200) +// GetMetadata returns topic metadata +func (c *Consumer) GetMetadata(topic string) (*Metadata, error) { + ctx, cancel := context.WithTimeout(c.ctx, 5*time.Second) + defer cancel() + + // Use kadm for metadata + adm := kadm.NewClient(c.client) + meta, err := adm.Metadata(ctx, topic) + if err != nil { + return nil, errors.WithStack(err) + } + + result := &Metadata{Topics: make(map[string]TopicMetadata)} + for _, t := range meta.Topics { + tm := TopicMetadata{} + for _, p := range t.Partitions { + tm.Partitions = append(tm.Partitions, PartitionMetadata{ + ID: p.Partition, + Leader: p.Leader, + }) + } + result.Topics[t.Topic] = tm + } + + return result, nil } -// Stop stops the poll loop running +// Stop stops the poll loop func (c *Consumer) Stop() { - if c.consumer != nil { - c.running = false - if err := c.consumer.Close(); err != nil { - logger.Warn().Err(err).Send() - } + c.running = false + c.cancel() + if c.client != nil { + c.client.Close() } } +// Check verifies consumer connectivity func (c *Consumer) Check(ctx context.Context) error { if !c.isConnected() { return errors.New("consumer isn't connected to Kafka") } - return nil } func (c *Consumer) setConnected(cntd bool) { c.connectedLock.Lock() defer c.connectedLock.Unlock() - c.connected = cntd } func (c *Consumer) isConnected() bool { c.connectedLock.RLock() defer c.connectedLock.RUnlock() - return c.connected } + +func recordToMessage(r *kgo.Record) *Message { + headers := make(map[string][]byte) + for _, h := range r.Headers { + headers[h.Key] = h.Value + } + return &Message{ + Topic: r.Topic, + Partition: r.Partition, + Offset: r.Offset, + Key: r.Key, + Value: r.Value, + Headers: headers, + } +} diff --git a/pkg/kafka/consumer_test.go b/pkg/kafka/consumer_test.go index 631ba46..98b45d1 100644 --- a/pkg/kafka/consumer_test.go +++ b/pkg/kafka/consumer_test.go @@ -4,7 +4,7 @@ import ( "errors" "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/confluentinc/confluent-kafka-go/v2/kafka" ) diff --git a/pkg/kafka/kafka_config.go b/pkg/kafka/kafka_config.go deleted file mode 100644 index 316eacd..0000000 --- a/pkg/kafka/kafka_config.go +++ /dev/null @@ -1,261 +0,0 @@ -package kafka - -import ( - "fiskerinc.com/modules/utils/envtool" - "github.com/confluentinc/confluent-kafka-go/v2/kafka" -) - -// https://github.com/confluentinc/librdkafka/blob/master/CONFIGURATION.md -// Load all the different settings we want for Kafka configuration into a kafka config object -func loadKafkaConsumerConfig(serviceName string) (configuration kafka.ConfigMap) { - configuration = kafka.ConfigMap{ - "group.id": serviceName, - "bootstrap.servers": envtool.GetEnv("KAFKA_HOSTS", "localhost:9093"), - // "builtin.features": envtool.GetEnv("KAFKA_BUILTIN_FEATURES", kafka_builtin_features_default), //Desc: Indicates the builtin features for this build of librdkafka. An application can either query this value or attempt to set it with its list of required features to check for library support.
*Type: CSV flags* - // "client.id": envtool.GetEnv("KAFKA_CLIENT_ID", "rdkafka"), //Desc: Client identifier.
*Type: string* - "metadata.broker.list": envtool.GetEnv("KAFKA_METADATA_BROKER_LIST", "localhost:9093"), //Desc: Initial list of brokers as a CSV list of broker host or host:port. The application may also use 'rd_kafka_brokers_add()' to add brokers during runtime.
*Type: string* - // "message.max.bytes": envtool.GetEnvInt("KAFKA_MESSAGE_MAX_BYTES", 1000000), //Desc: Maximum Kafka protocol request message size. Due to differing framing overhead between protocol versions the producer is unable to reliably enforce a strict max message limit at produce time and may exceed the maximum size by one message in protocol ProduceRequests, the broker will enforce the the topic's 'max.message.bytes' limit (see Apache Kafka documentation).
*Type: integer* Range: 1000 .. 1000000000 - // "message.copy.max.bytes": envtool.GetEnvInt("KAFKA_MESSAGE_COPY_MAX_BYTES", 65535), //Desc: Maximum size for message to be copied to buffer. Messages larger than this will be passed by reference (zero-copy) at the expense of larger iovecs.
*Type: integer* Range: 0 .. 1000000000 - // "receive.message.max.bytes": envtool.GetEnvInt("KAFKA_RECEIVE_MESSAGE_MAX_BYTES", 100000000), //Desc: Maximum Kafka protocol response message size. This serves as a safety precaution to avoid memory exhaustion in case of protocol hickups. This value must be at least 'fetch.max.bytes' + 512 to allow for protocol overhead; the value is adjusted automatically unless the configuration property is explicitly set.
*Type: integer* Range: 1000 .. 2147483647 - // "max.in.flight.requests.per.connection": envtool.GetEnvInt("KAFKA_MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION", 1000000), //Desc: Maximum number of in-flight requests per broker connection. This is a generic property applied to all broker communication, however it is primarily relevant to produce requests. In particular, note that other mechanisms limit the number of outstanding consumer fetch request per broker to one.
*Type: integer* Range: 1 .. 1000000 - // "topic.metadata.refresh.interval.ms": envtool.GetEnvInt("KAFKA_TOPIC_METADATA_REFRESH_INTERVAL_MS", 300000), //Desc: Period of time in milliseconds at which topic and broker metadata is refreshed in order to proactively discover any new brokers, topics, partitions or partition leader changes. Use -1 to disable the intervalled refresh (not recommended). If there are no locally referenced topics (no topic objects created, no messages produced, no subscription or no assignment) then only the broker list will be refreshed every interval but no more often than every 10s.
*Type: integer* Range: -1 .. 3600000 - "metadata.max.age.ms": envtool.GetEnvInt("KAFKA_METADATA_MAX_AGE_MS", 900000), //Desc: Metadata cache max age. Defaults to topic.metadata.refresh.interval.ms * 3
*Type: integer* Range: 1 .. 86400000 - // "topic.metadata.refresh.fast.interval.ms": envtool.GetEnvInt("KAFKA_TOPIC_METADATA_REFRESH_FAST_INTERVAL_MS", 100), //Desc: When a topic loses its leader a new metadata request will be enqueued immediately and then with this initial interval, exponentially increasing upto 'retry.backoff.max.ms', until the topic metadata has been refreshed. If not set explicitly, it will be defaulted to 'retry.backoff.ms'. This is used to recover quickly from transitioning leader brokers.
*Type: integer* Range: 1 .. 60000 - // "topic.metadata.refresh.sparse": envtool.GetEnvBool("KAFKA_TOPIC_METADATA_REFRESH_SPARSE", true), //Desc: Sparse metadata requests (consumes less network bandwidth)
*Type: boolean* Range: true, false - // "topic.metadata.propagation.max.ms": envtool.GetEnvInt("KAFKA_TOPIC_METADATA_PROPAGATION_MAX_MS", 30000), //Desc: Apache Kafka topic creation is asynchronous and it takes some time for a new topic to propagate throughout the cluster to all brokers. If a client requests topic metadata after manual topic creation but before the topic has been fully propagated to the broker the client is requesting metadata from, the topic will seem to be non-existent and the client will mark the topic as such, failing queued produced messages with 'ERR__UNKNOWN_TOPIC'. This setting delays marking a topic as non-existent until the configured propagation max time has passed. The maximum propagation time is calculated from the time the topic is first referenced in the client, e.g., on produce().
*Type: integer* Range: 0 .. 3600000 - // "topic.blacklist": envtool.GetEnv("KAFKA_TOPIC_BLACKLIST", ""), //Desc: Topic blacklist, a comma-separated list of regular expressions for matching topic names that should be ignored in broker metadata information as if the topics did not exist.
*Type: pattern list* - // "debug": envtool.GetEnv("KAFKA_DEBUG", ""), //Desc: A comma-separated list of debug contexts to enable. Detailed Producer debugging: broker,topic,msg. Consumer: consumer,cgrp,topic,fetch
*Type: CSV flags* Range: generic, broker, topic, metadata, feature, queue, msg, protocol, cgrp, security, fetch, interceptor, plugin, consumer, admin, eos, mock, assignor, conf, all - "socket.timeout.ms": envtool.GetEnvInt("KAFKA_SOCKET_TIMEOUT_MS", 60000), //Desc: Default timeout for network requests. Producer: ProduceRequests will use the lesser value of 'socket.timeout.ms' and remaining 'message.timeout.ms' for the first message in the batch. Consumer: FetchRequests will use 'fetch.wait.max.ms' + 'socket.timeout.ms'. Admin: Admin requests will use 'socket.timeout.ms' or explicitly set 'rd_kafka_AdminOptions_set_operation_timeout()' value.
*Type: integer* Range: 10 .. 300000 - // "socket.send.buffer.bytes": envtool.GetEnvInt("KAFKA_SOCKET_SEND_BUFFER_BYTES", 0), //Desc: Broker socket send buffer size. System default is used if 0.
*Type: integer* Range: 0 .. 100000000 - // "socket.receive.buffer.bytes": envtool.GetEnvInt("KAFKA_SOCKET_RECEIVE_BUFFER_BYTES", 0), //Desc: Broker socket receive buffer size. System default is used if 0.
*Type: integer* Range: 0 .. 100000000 - "socket.keepalive.enable": envtool.GetEnvBool("KAFKA_SOCKET_KEEPALIVE_ENABLE", true), //Desc: Enable TCP keep-alives (SO_KEEPALIVE) on broker sockets
*Type: boolean* Range: true, false - // "socket.nagle.disable": envtool.GetEnvBool("KAFKA_SOCKET_NAGLE_DISABLE", false), //Desc: Disable the Nagle algorithm (TCP_NODELAY) on broker sockets.
*Type: boolean* Range: true, false - // "socket.max.fails": envtool.GetEnvInt("KAFKA_SOCKET_MAX_FAILS", 1), //Desc: Disconnect from broker when this number of send failures (e.g., timed out requests) is reached. Disable with 0. WARNING: It is highly recommended to leave this setting at its default value of 1 to avoid the client and broker to become desynchronized in case of request timeouts. NOTE: The connection is automatically re-established.
*Type: integer* Range: 0 .. 1000000 - // "broker.address.ttl": envtool.GetEnvInt("KAFKA_BROKER_ADDRESS_TTL", 1000), //Desc: How long to cache the broker address resolving results (milliseconds).
*Type: integer* Range: 0 .. 86400000 - // "broker.address.family": envtool.GetEnv("KAFKA_BROKER_ADDRESS_FAMILY", "any"), //Desc: Allowed broker IP address families: any, v4, v6
*Type: enum value* Range: any, v4, v6 - // "socket.connection.setup.timeout.ms": envtool.GetEnvInt("KAFKA_SOCKET_CONNECTION_SETUP_TIMEOUT_MS", 30000), //Desc: Maximum time allowed for broker connection setup (TCP connection setup as well SSL and SASL handshake). If the connection to the broker is not fully functional after this the connection will be closed and retried.
*Type: integer* Range: 1000 .. 2147483647 - "connections.max.idle.ms": envtool.GetEnvInt("KAFKA_CONNECTIONS_MAX_IDLE_MS", 0), //Desc: Close broker connections after the specified time of inactivity. Disable with 0. If this property is left at its default value some heuristics are performed to determine a suitable default value, this is currently limited to identifying brokers on Azure (see librdkafka issue #3109 for more info).
*Type: integer* Range: 0 .. 2147483647 - // "reconnect.backoff.ms": envtool.GetEnvInt("KAFKA_RECONNECT_BACKOFF_MS", 100), //Desc: The initial time to wait before reconnecting to a broker after the connection has been closed. The time is increased exponentially until 'reconnect.backoff.max.ms' is reached. -25% to +50% jitter is applied to each reconnect backoff. A value of 0 disables the backoff and reconnects immediately.
*Type: integer* Range: 0 .. 3600000 - // "reconnect.backoff.max.ms": envtool.GetEnvInt("KAFKA_RECONNECT_BACKOFF_MAX_MS", 10000), //Desc: The maximum time to wait before reconnecting to a broker after the connection has been closed.
*Type: integer* Range: 0 .. 3600000 - "statistics.interval.ms": envtool.GetEnvInt("KAFKA_STATISTICS_INTERVAL_MS", 0), //Desc: librdkafka statistics emit interval. The application also needs to register a stats callback using 'rd_kafka_conf_set_stats_cb()'. The granularity is 1000ms. A value of 0 disables statistics.
*Type: integer* Range: 0 .. 86400000 - // "enabled_events": envtool.GetEnvInt("KAFKA_ENABLED_EVENTS", 0), //Desc: See 'rd_kafka_conf_set_events()'
*Type: integer* Range: 0 .. 2147483647 - // "error_cb": envtool.GetEnv("KAFKA_ERROR_CB", ""), //Desc: Error callback (set with rd_kafka_conf_set_error_cb())
*Type: see dedicated API* - // "throttle_cb": envtool.GetEnv("KAFKA_THROTTLE_CB", ""), //Desc: Throttle callback (set with rd_kafka_conf_set_throttle_cb())
*Type: see dedicated API* - // "stats_cb": envtool.GetEnv("KAFKA_STATS_CB", ""), //Desc: Statistics callback (set with rd_kafka_conf_set_stats_cb())
*Type: see dedicated API* - // "log_cb": envtool.GetEnv("KAFKA_LOG_CB", ""), //Desc: Log callback (set with rd_kafka_conf_set_log_cb())
*Type: see dedicated API* - "log_level": envtool.GetEnvInt("KAFKA_LOG_LEVEL", 6), //Desc: Logging level (syslog(3) levels)
*Type: integer* Range: 0 .. 7 - // "log.queue": envtool.GetEnvBool("KAFKA_LOG_QUEUE", false), //Desc: Disable spontaneous log_cb from internal librdkafka threads, instead enqueue log messages on queue set with 'rd_kafka_set_log_queue()' and serve log callbacks or events through the standard poll APIs. **NOTE**: Log messages will linger in a temporary queue until the log queue has been set.
*Type: boolean* Range: true, false - // "log.thread.name": envtool.GetEnvBool("KAFKA_LOG_THREAD_NAME", true), //Desc: Print internal thread name in log messages (useful for debugging librdkafka internals)
*Type: boolean* Range: true, false - // "enable.random.seed": envtool.GetEnvBool("KAFKA_ENABLE_RANDOM_SEED", true), //Desc: If enabled librdkafka will initialize the PRNG with srand(current_time.milliseconds) on the first invocation of rd_kafka_new() (required only if rand_r() is not available on your platform). If disabled the application must call srand() prior to calling rd_kafka_new().
*Type: boolean* Range: true, false - "log.connection.close": envtool.GetEnvBool("KAFKA_LOG_CONNECTION_CLOSE", true), //Desc: Log broker disconnects. It might be useful to turn this off when interacting with 0.9 brokers with an aggressive 'connections.max.idle.ms' value.
*Type: boolean* Range: true, false - // "background_event_cb": envtool.GetEnv("KAFKA_BACKGROUND_EVENT_CB", ""), //Desc: Background queue event callback (set with rd_kafka_conf_set_background_event_cb())
*Type: see dedicated API* - // "socket_cb": envtool.GetEnv("KAFKA_SOCKET_CB", ""), //Desc: Socket creation callback to provide race-free CLOEXEC
*Type: see dedicated API* - // "connect_cb": envtool.GetEnv("KAFKA_CONNECT_CB", ""), //Desc: Socket connect callback
*Type: see dedicated API* - // "closesocket_cb": envtool.GetEnv("KAFKA_CLOSESOCKET_CB", ""), //Desc: Socket close callback
*Type: see dedicated API* - // "open_cb": envtool.GetEnv("KAFKA_OPEN_CB", ""), //Desc: File open callback to provide race-free CLOEXEC
*Type: see dedicated API* - // "resolve_cb": envtool.GetEnv("KAFKA_RESOLVE_CB", ""), //Desc: Address resolution callback (set with rd_kafka_conf_set_resolve_cb()).
*Type: see dedicated API* - // "opaque": envtool.GetEnv("KAFKA_OPAQUE", ""), //Desc: Application opaque (set with rd_kafka_conf_set_opaque())
*Type: see dedicated API* - // "default_topic_conf": envtool.GetEnv("KAFKA_DEFAULT_TOPIC_CONF", ""), //Desc: Default topic configuration for automatically subscribed topics
*Type: see dedicated API* - // "internal.termination.signal": envtool.GetEnvInt("KAFKA_INTERNAL_TERMINATION_SIGNAL", 0), //Desc: Signal that librdkafka will use to quickly terminate on rd_kafka_destroy(). If this signal is not set then there will be a delay before rd_kafka_wait_destroyed() returns true as internal threads are timing out their system calls. If this signal is set however the delay will be minimal. The application should mask this signal as an internal signal handler is installed.
*Type: integer* Range: 0 .. 128 - "api.version.request": envtool.GetEnvBool("KAFKA_API_VERSION_REQUEST", true), //Desc: Request broker's supported API versions to adjust functionality to available protocol features. If set to false, or the ApiVersionRequest fails, the fallback version 'broker.version.fallback' will be used. **NOTE**: Depends on broker version >=0.10.0. If the request is not supported by (an older) broker the 'broker.version.fallback' fallback is used.
*Type: boolean* Range: true, false - // "api.version.request.timeout.ms": envtool.GetEnvInt("KAFKA_API_VERSION_REQUEST_TIMEOUT_MS", 10000), //Desc: Timeout for broker API version requests.
*Type: integer* Range: 1 .. 300000 - "api.version.fallback.ms": envtool.GetEnvInt("KAFKA_API_VERSION_FALLBACK_MS", 0), //Desc: Dictates how long the 'broker.version.fallback' fallback is used in the case the ApiVersionRequest fails. **NOTE**: The ApiVersionRequest is only issued when a new connection to the broker is made (such as after an upgrade).
*Type: integer* Range: 0 .. 604800000 - "broker.version.fallback": envtool.GetEnv("KAFKA_BROKER_VERSION_FALLBACK", "0.10.0"), //Desc: Older broker versions (before 0.10.0) provide no way for a client to query for supported protocol features (ApiVersionRequest, see 'api.version.request') making it impossible for the client to know what features it may use. As a workaround a user may set this property to the expected broker version and the client will automatically adjust its feature set accordingly if the ApiVersionRequest fails (or is disabled). The fallback broker version will be used for 'api.version.fallback.ms'. Valid values are: 0.9.0, 0.8.2, 0.8.1, 0.8.0. Any other value >= 0.10, such as 0.10.2.1, enables ApiVersionRequests.
*Type: string* - "allow.auto.create.topics": envtool.GetEnvBool("KAFKA_ALLOW_AUTO_CREATE_TOPICS", true), //Desc: Allow automatic topic creation on the broker when subscribing to or assigning non-existent topics. The broker must also be configured with 'auto.create.topics.enable=true' for this configuration to take effect. Note: the default value (true) for the producer is different from the default value (false) for the consumer. Further, the consumer default value is different from the Java consumer (true), and this property is not supported by the Java producer. Requires broker version >= 0.11.0.0, for older broker versions only the broker configuration applies.
*Type: boolean* Range: true, false - "security.protocol": envtool.GetEnv("KAFKA_SECURITY_PROTOCOL", "plaintext"), //Desc: Protocol used to communicate with brokers.
*Type: enum value* Range: plaintext, ssl, sasl_plaintext, sasl_ssl - // "ssl.cipher.suites": envtool.GetEnv("KAFKA_SSL_CIPHER_SUITES", ""), //Desc: A cipher suite is a named combination of authentication, encryption, MAC and key exchange algorithm used to negotiate the security settings for a network connection using TLS or SSL network protocol. See manual page for 'ciphers(1)' and 'SSL_CTX_set_cipher_list(3).
*Type: string* - // "ssl.curves.list": envtool.GetEnv("KAFKA_SSL_CURVES_LIST", ""), //Desc: The supported-curves extension in the TLS ClientHello message specifies the curves (standard/named, or 'explicit' GF(2^k) or GF(p)) the client is willing to have the server use. See manual page for 'SSL_CTX_set1_curves_list(3)'. OpenSSL >= 1.0.2 required.
*Type: string* - // "ssl.sigalgs.list": envtool.GetEnv("KAFKA_SSL_SIGALGS_LIST", ""), //Desc: The client uses the TLS ClientHello signature_algorithms extension to indicate to the server which signature/hash algorithm pairs may be used in digital signatures. See manual page for 'SSL_CTX_set1_sigalgs_list(3)'. OpenSSL >= 1.0.2 required.
*Type: string* - // "ssl.key.location": envtool.GetEnv("KAFKA_SSL_KEY_LOCATION", ""), //Desc: Path to client's private key (PEM) used for authentication.
*Type: string* - // "ssl.key.password": envtool.GetEnv("KAFKA_SSL_KEY_PASSWORD", ""), //Desc: Private key passphrase (for use with 'ssl.key.location' and 'set_ssl_cert()')
*Type: string* - // "ssl.key.pem": envtool.GetEnv("KAFKA_SSL_KEY_PEM", ""), //Desc: Client's private key string (PEM format) used for authentication.
*Type: string* - // "ssl_key": envtool.GetEnv("KAFKA_SSL_KEY", ""), //Desc: Client's private key as set by rd_kafka_conf_set_ssl_cert()
*Type: see dedicated API* - // "ssl.certificate.location": envtool.GetEnv("KAFKA_SSL_CERTIFICATE_LOCATION", ""), //Desc: Path to client's public key (PEM) used for authentication.
*Type: string* - // "ssl.certificate.pem": envtool.GetEnv("KAFKA_SSL_CERTIFICATE_PEM", ""), //Desc: Client's public key string (PEM format) used for authentication.
*Type: string* - // "ssl_certificate": envtool.GetEnv("KAFKA_SSL_CERTIFICATE", ""), //Desc: Client's public key as set by rd_kafka_conf_set_ssl_cert()
*Type: see dedicated API* - // "ssl.ca.location": envtool.GetEnv("KAFKA_SSL_CA_LOCATION", ""), //Desc: File or directory path to CA certificate(s) for verifying the broker's key. Defaults: On Windows the system's CA certificates are automatically looked up in the Windows Root certificate store. On Mac OSX this configuration defaults to 'probe'. It is recommended to install openssl using Homebrew, to provide CA certificates. On Linux install the distribution's ca-certificates package. If OpenSSL is statically linked or 'ssl.ca.location' is set to 'probe' a list of standard paths will be probed and the first one found will be used as the default CA certificate location path. If OpenSSL is dynamically linked the OpenSSL library's default path will be used (see 'OPENSSLDIR' in 'openssl version -a').
*Type: string* - // "ssl.ca.pem": envtool.GetEnv("KAFKA_SSL_CA_PEM", ""), //Desc: CA certificate string (PEM format) for verifying the broker's key.
*Type: string* - // "ssl_ca": envtool.GetEnv("KAFKA_SSL_CA", ""), //Desc: CA certificate as set by rd_kafka_conf_set_ssl_cert()
*Type: see dedicated API* - // "ssl.ca.certificate.stores": envtool.GetEnv("KAFKA_SSL_CA_CERTIFICATE_STORES", "Root"), //Desc: Comma-separated list of Windows Certificate stores to load CA certificates from. Certificates will be loaded in the same order as stores are specified. If no certificates can be loaded from any of the specified stores an error is logged and the OpenSSL library's default CA location is used instead. Store names are typically one or more of: MY, Root, Trust, CA.
*Type: string* - // "ssl.crl.location": envtool.GetEnv("KAFKA_SSL_CRL_LOCATION", ""), //Desc: Path to CRL for verifying broker's certificate validity.
*Type: string* - // "ssl.keystore.location": envtool.GetEnv("KAFKA_SSL_KEYSTORE_LOCATION", ""), //Desc: Path to client's keystore (PKCS#12) used for authentication.
*Type: string* - // "ssl.keystore.password": envtool.GetEnv("KAFKA_SSL_KEYSTORE_PASSWORD", ""), //Desc: Client's keystore (PKCS#12) password.
*Type: string* - // "ssl.providers": envtool.GetEnv("KAFKA_SSL_PROVIDERS", ""), //Desc: Comma-separated list of OpenSSL 3.0.x implementation providers. E.g., "default,legacy".
*Type: string* - // "ssl.engine.id": envtool.GetEnv("KAFKA_SSL_ENGINE_ID", "dynamic"), //Desc: OpenSSL engine id is the name used for loading engine.
*Type: string* - // "ssl_engine_callback_data": envtool.GetEnv("KAFKA_SSL_ENGINE_CALLBACK_DATA", ""), //Desc: OpenSSL engine callback data (set with rd_kafka_conf_set_engine_callback_data()).
*Type: see dedicated API* - "enable.ssl.certificate.verification": envtool.GetEnvBool("KAFKA_SSL_VERIFY", true), //Desc: Enable OpenSSL's builtin broker (server) certificate verification. This verification can be extended by the application by implementing a certificate_verify_cb. Type: boolean - // "ssl.endpoint.identification.algorithm": envtool.GetEnv("KAFKA_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM", "https"), //Desc: Endpoint identification algorithm to validate broker hostname using broker certificate. https - Server (broker) hostname verification as specified in RFC2818. none - No endpoint verification. OpenSSL >= 1.0.2 required.
*Type: enum value* Range: none, https - // "ssl.certificate.verify_cb": envtool.GetEnv("KAFKA_SSL_CERTIFICATE_VERIFY_CB", ""), //Desc: Callback to verify the broker certificate chain.
*Type: see dedicated API* - "sasl.mechanisms": envtool.GetEnv("KAFKA_SASL_MECHANISMS", "GSSAPI"), //Desc: SASL mechanism to use for authentication. Supported: GSSAPI, PLAIN, SCRAM-SHA-256, SCRAM-SHA-512, OAUTHBEARER. **NOTE**: Despite the name only one mechanism must be configured.
*Type: string* - // "sasl.kerberos.service.name": envtool.GetEnv("KAFKA_SASL_KERBEROS_SERVICE_NAME", "kafka"), //Desc: Kerberos principal name that Kafka runs as, not including /hostname@REALM
*Type: string* - // "sasl.kerberos.principal": envtool.GetEnv("KAFKA_SASL_KERBEROS_PRINCIPAL", "kafkaclient"), //Desc: This client's Kerberos principal name. (Not supported on Windows, will use the logon user's principal).
*Type: string* - // "sasl.kerberos.kinit.cmd": envtool.GetEnv("KAFKA_SASL_KERBEROS_KINIT_CMD", kafka_sasl_kerberos_kinit_cmd_default), //Desc: Shell command to refresh or acquire the client's Kerberos ticket. This command is executed on client creation and every sasl.kerberos.min.time.before.relogin (0=disable). %{config.prop.name} is replaced by corresponding config object value.
*Type: string* - // "sasl.kerberos.keytab": envtool.GetEnv("KAFKA_SASL_KERBEROS_KEYTAB", ""), //Desc: Path to Kerberos keytab file. This configuration property is only used as a variable in 'sasl.kerberos.kinit.cmd' as ' ... -t "%{sasl.kerberos.keytab}"'.
*Type: string* - // "sasl.kerberos.min.time.before.relogin": envtool.GetEnvInt("KAFKA_SASL_KERBEROS_MIN_TIME_BEFORE_RELOGIN", 60000), //Desc: Minimum time in milliseconds between key refresh attempts. Disable automatic key refresh by setting this property to 0.
*Type: integer* Range: 0 .. 86400000 - "sasl.username": envtool.GetEnv("KAFKA_SASL_USERNAME", ""), //Desc: SASL username for use with the PLAIN and SASL-SCRAM-.. mechanisms
*Type: string* - "sasl.password": envtool.GetEnv("KAFKA_SASL_PASSWORD", ""), //Desc: SASL password for use with the PLAIN and SASL-SCRAM-.. mechanism
*Type: string* - // "sasl.oauthbearer.config": envtool.GetEnv("KAFKA_SASL_OAUTHBEARER_CONFIG", ""), //Desc: SASL/OAUTHBEARER configuration. The format is implementation-dependent and must be parsed accordingly. The default unsecured token implementation (see https://tools.ietf.org/html/rfc7515#appendix-A.5) recognizes space-separated name=value pairs with valid names including principalClaimName, principal, scopeClaimName, scope, and lifeSeconds. The default value for principalClaimName is "sub", the default value for scopeClaimName is "scope", and the default value for lifeSeconds is 3600. The scope value is CSV format with the default value being no/empty scope. For example: 'principalClaimName=azp principal=admin scopeClaimName=roles scope=role1,role2 lifeSeconds=600'. In addition, SASL extensions can be communicated to the broker via 'extension_NAME=value'. For example: 'principal=admin extension_traceId=123'
*Type: string* - // "enable.sasl.oauthbearer.unsecure.jwt": envtool.GetEnvBool("KAFKA_ENABLE_SASL_OAUTHBEARER_UNSECURE_JWT", false), //Desc: Enable the builtin unsecure JWT OAUTHBEARER token handler if no oauthbearer_refresh_cb has been set. This builtin handler should only be used for development or testing, and not in production.
*Type: boolean* Range: true, false - // "oauthbearer_token_refresh_cb": envtool.GetEnv("KAFKA_OAUTHBEARER_TOKEN_REFRESH_CB", ""), //Desc: SASL/OAUTHBEARER token refresh callback (set with rd_kafka_conf_set_oauthbearer_token_refresh_cb(), triggered by rd_kafka_poll(), et.al. This callback will be triggered when it is time to refresh the client's OAUTHBEARER token. Also see 'rd_kafka_conf_enable_sasl_queue()'.
*Type: see dedicated API* - // "sasl.oauthbearer.method": envtool.GetEnv("KAFKA_SASL_OAUTHBEARER_METHOD", "default"), //Desc: Set to "default" or "oidc" to control which login method to be used. If set to "oidc", the following properties must also be be specified: 'sasl.oauthbearer.client.id', 'sasl.oauthbearer.client.secret', and 'sasl.oauthbearer.token.endpoint.url'.
*Type: enum value* Range: default, oidc - // "sasl.oauthbearer.client.id": envtool.GetEnv("KAFKA_SASL_OAUTHBEARER_CLIENT_ID", ""), //Desc: Public identifier for the application. Must be unique across all clients that the authorization server handles. Only used when 'sasl.oauthbearer.method' is set to "oidc".
*Type: string* - // "sasl.oauthbearer.client.secret": envtool.GetEnv("KAFKA_SASL_OAUTHBEARER_CLIENT_SECRET", ""), //Desc: Client secret only known to the application and the authorization server. This should be a sufficiently random string that is not guessable. Only used when 'sasl.oauthbearer.method' is set to "oidc".
*Type: string* - // "sasl.oauthbearer.scope": envtool.GetEnv("KAFKA_SASL_OAUTHBEARER_SCOPE", ""), //Desc: Client use this to specify the scope of the access request to the broker. Only used when 'sasl.oauthbearer.method' is set to "oidc".
*Type: string* - // "sasl.oauthbearer.extensions": envtool.GetEnv("KAFKA_SASL_OAUTHBEARER_EXTENSIONS", ""), //Desc: Allow additional information to be provided to the broker. Comma-separated list of key=value pairs. E.g., "supportFeatureX=true,organizationId=sales-emea".Only used when 'sasl.oauthbearer.method' is set to "oidc".
*Type: string* - // "sasl.oauthbearer.token.endpoint.url": envtool.GetEnv("KAFKA_SASL_OAUTHBEARER_TOKEN_ENDPOINT_URL", ""), //Desc: OAuth/OIDC issuer token endpoint HTTP(S) URI used to retrieve token. Only used when 'sasl.oauthbearer.method' is set to "oidc".
*Type: string* - // "plugin.library.paths": envtool.GetEnv("KAFKA_PLUGIN_LIBRARY_PATHS", ""), //Desc: List of plugin libraries to load (; separated). The library search path is platform dependent (see dlopen(3) for Unix and LoadLibrary() for Windows). If no filename extension is specified the platform-specific extension (such as .dll or .so) will be appended automatically.
*Type: string* - // "interceptors": envtool.GetEnv("KAFKA_INTERCEPTORS", ""), //Desc: Interceptors added through rd_kafka_conf_interceptor_add_..() and any configuration handled by interceptors.
*Type: see dedicated API* - // "group.instance.id": envtool.GetEnv("KAFKA_GROUP_INSTANCE_ID", ""), //Desc: Enable static group membership. Static group members are able to leave and rejoin a group within the configured 'session.timeout.ms' without prompting a group rebalance. This should be used in combination with a larger 'session.timeout.ms' to avoid group rebalances caused by transient unavailability (e.g. process restarts). Requires broker version >= 2.3.0.
*Type: string* - "partition.assignment.strategy": envtool.GetEnv("KAFKA_PARTITION_ASSIGNMENT_STRATEGY", "range,roundrobin"), //Desc: The name of one or more partition assignment strategies. The elected group leader will use a strategy supported by all members of the group to assign partitions to group members. If there is more than one eligible strategy, preference is determined by the order of this list (strategies earlier in the list have higher priority). Cooperative and non-cooperative (eager) strategies must not be mixed. Available strategies: range, roundrobin, cooperative-sticky.
*Type: string* - "session.timeout.ms": envtool.GetEnvInt("KAFKA_SESSION_TIMEOUT_MS", 45000), //Desc: Client group session and failure detection timeout. The consumer sends periodic heartbeats (heartbeat.interval.ms) to indicate its liveness to the broker. If no hearts are received by the broker for a group member within the session timeout, the broker will remove the consumer from the group and trigger a rebalance. The allowed range is configured with the **broker** configuration properties 'group.min.session.timeout.ms' and 'group.max.session.timeout.ms'. Also see 'max.poll.interval.ms'.
*Type: integer* Range: 1 .. 3600000 - "heartbeat.interval.ms": envtool.GetEnvInt("KAFKA_HEARTBEAT_INTERVAL_MS", 3000), //Desc: Group session keepalive heartbeat interval.
*Type: integer* Range: 1 .. 3600000 - // "group.protocol.type": envtool.GetEnv("KAFKA_GROUP_PROTOCOL_TYPE", "consumer"), //Desc: Group protocol type. NOTE: Currently, the only supported group protocol type is 'consumer'.
*Type: string* - // "coordinator.query.interval.ms": envtool.GetEnvInt("KAFKA_COORDINATOR_QUERY_INTERVAL_MS", 600000), //Desc: How often to query for the current client group coordinator. If the currently assigned coordinator is down the configured query interval will be divided by ten to more quickly recover in case of coordinator reassignment.
*Type: integer* Range: 1 .. 3600000 - "max.poll.interval.ms": envtool.GetEnvInt("KAFKA_MAX_POLL_INTERVAL_MS", 300000), //Desc: Maximum allowed time between calls to consume messages (e.g., rd_kafka_consumer_poll()) for high-level consumers. If this interval is exceeded the consumer is considered failed and the group will rebalance in order to reassign the partitions to another consumer group member. Warning: Offset commits may be not possible at this point. Note: It is recommended to set 'enable.auto.offset.store=false' for long-time processing applications and then explicitly store offsets (using offsets_store()) *after* message processing, to make sure offsets are not auto-committed prior to processing has finished. The interval is checked two times per second. See KIP-62 for more information.
*Type: integer* Range: 1 .. 86400000 - "enable.auto.commit": envtool.GetEnvBool("KAFKA_ENABLE_AUTO_COMMIT", true), //Desc: Automatically and periodically commit offsets in the background. Note: setting this to false does not prevent the consumer from fetching previously committed start offsets. To circumvent this behaviour set specific start offsets per partition in the call to assign().
*Type: boolean* Range: true, false - "auto.commit.interval.ms": envtool.GetEnvInt("KAFKA_AUTO_COMMIT_INTERVAL_MS", 5000), //Desc: The frequency in milliseconds that the consumer offsets are committed (written) to offset storage. (0 = disable). This setting is used by the high-level consumer.
*Type: integer* Range: 0 .. 86400000 - "enable.auto.offset.store": envtool.GetEnvBool("KAFKA_ENABLE_AUTO_OFFSET_STORE", true), //Desc: Automatically store offset of last message provided to application. The offset store is an in-memory store of the next offset to (auto-)commit for each partition.
*Type: boolean* Range: true, false - // "queued.min.messages": envtool.GetEnvInt("KAFKA_QUEUED_MIN_MESSAGES", 100000), //Desc: Minimum number of messages per topic+partition librdkafka tries to maintain in the local consumer queue.
*Type: integer* Range: 1 .. 10000000 - // "queued.max.messages.kbytes": envtool.GetEnvInt("KAFKA_QUEUED_MAX_MESSAGES_KBYTES", 65536), //Desc: Maximum number of kilobytes of queued pre-fetched messages in the local consumer queue. If using the high-level consumer this setting applies to the single consumer queue, regardless of the number of partitions. When using the legacy simple consumer or when separate partition queues are used this setting applies per partition. This value may be overshot by fetch.message.max.bytes. This property has higher priority than queued.min.messages.
*Type: integer* Range: 1 .. 2097151 - // "fetch.wait.max.ms": envtool.GetEnvInt("KAFKA_FETCH_WAIT_MAX_MS", 500), //Desc: Maximum time the broker may wait to fill the Fetch response with fetch.min.bytes of messages.
*Type: integer* Range: 0 .. 300000 - // "fetch.queue.backoff.ms": envtool.GetEnvInt("KAFKA_FETCH_QUEUE_BACKOFF_MS", 1000), //Desc: How long to postpone the next fetch request for a topic+partition in case the current fetch queue thresholds (queued.min.messages or queued.max.messages.kbytes) have been exceded. This property may need to be decreased if the queue thresholds are set low and the application is experiencing long (~1s) delays between messages. Low values may increase CPU utilization.
*Type: integer* Range: 0 .. 300000 - // "fetch.message.max.bytes": envtool.GetEnvInt("KAFKA_FETCH_MESSAGE_MAX_BYTES", 1048576), //Desc: Initial maximum number of bytes per topic+partition to request when fetching messages from the broker. If the client encounters a message larger than this value it will gradually try to increase it until the entire message can be fetched.
*Type: integer* Range: 1 .. 1000000000 - // "fetch.max.bytes": envtool.GetEnvInt("KAFKA_FETCH_MAX_BYTES", 52428800), //Desc: Maximum amount of data the broker shall return for a Fetch request. Messages are fetched in batches by the consumer and if the first message batch in the first non-empty partition of the Fetch request is larger than this value, then the message batch will still be returned to ensure the consumer can make progress. The maximum message batch size accepted by the broker is defined via 'message.max.bytes' (broker config) or 'max.message.bytes' (broker topic config). 'fetch.max.bytes' is automatically adjusted upwards to be at least 'message.max.bytes' (consumer config).
*Type: integer* Range: 0 .. 2147483135 - "fetch.min.bytes": envtool.GetEnvInt("KAFKA_FETCH_MIN_BYTES", 1), //Desc: Minimum number of bytes the broker responds with. If fetch.wait.max.ms expires the accumulated data will be sent to the client regardless of this setting.
*Type: integer* Range: 1 .. 100000000 - // "fetch.error.backoff.ms": envtool.GetEnvInt("KAFKA_FETCH_ERROR_BACKOFF_MS", 500), //Desc: How long to postpone the next fetch request for a topic+partition in case of a fetch error.
*Type: integer* Range: 0 .. 300000 - "isolation.level": envtool.GetEnv("KAFKA_ISOLATION_LEVEL", "read_committed"), //Desc: Controls how to read messages written transactionally: 'read_committed' - only return transactional messages which have been committed. 'read_uncommitted' - return all messages, even transactional messages which have been aborted.
*Type: enum value* Range: read_uncommitted, read_committed - // "consume_cb": envtool.GetEnv("KAFKA_CONSUME_CB", ""), //Desc: Message consume callback (set with rd_kafka_conf_set_consume_cb())
*Type: see dedicated API* - // "rebalance_cb": envtool.GetEnv("KAFKA_REBALANCE_CB", ""), //Desc: Called after consumer group has been rebalanced (set with rd_kafka_conf_set_rebalance_cb())
*Type: see dedicated API* - // "offset_commit_cb": envtool.GetEnv("KAFKA_OFFSET_COMMIT_CB", ""), //Desc: Offset commit result propagation callback. (set with rd_kafka_conf_set_offset_commit_cb())
*Type: see dedicated API* - // "enable.partition.eof": envtool.GetEnvBool("KAFKA_ENABLE_PARTITION_EOF", false), //Desc: Emit RD_KAFKA_RESP_ERR__PARTITION_EOF event whenever the consumer reaches the end of a partition.
*Type: boolean* Range: true, false - // "check.crcs": envtool.GetEnvBool("KAFKA_CHECK_CRCS", false), //Desc: Verify CRC32 of consumed messages, ensuring no on-the-wire or on-disk corruption to the messages occurred. This check comes at slightly increased CPU usage.
*Type: boolean* Range: true, false - // "client.rack": envtool.GetEnv("KAFKA_CLIENT_RACK", ""), //Desc: A rack identifier for this client. This can be any string value which indicates where this client is physically located. It corresponds with the broker config 'broker.rack'.
*Type: string* - // "client.dns.lookup": envtool.GetEnv("KAFKA_CLIENT_DNS_LOOKUP", "use_all_dns_ips"), //Desc: Controls how the client uses DNS lookups. By default, when the lookup returns multiple IP addresses for a hostname, they will all be attempted for connection before the connection is considered failed. This applies to both bootstrap and advertised servers. If the value is set to 'resolve_canonical_bootstrap_servers_only', each entry will be resolved and expanded into a list of canonical names. NOTE: Default here is different from the Java client's default behavior, which connects only to the first IP address returned for a hostname.
*Type: enum value* Range: use_all_dns_ips, resolve_canonical_bootstrap_servers_only - - } - return -} - -// Shared by async producer and normal producer. No service calls both NewAsync and New -func loadKafkaProducerConfig() (configuration kafka.ConfigMap) { - configuration = kafka.ConfigMap{ - // "builtin.features": envtool.GetEnv("KAFKA_BUILTIN_FEATURES", kafka_builtin_features_default), //Desc: Indicates the builtin features for this build of librdkafka. An application can either query this value or attempt to set it with its list of required features to check for library support.
*Type: CSV flags* - // "client.id": envtool.GetEnv("KAFKA_CLIENT_ID", "rdkafka"), //Desc: Client identifier.
*Type: string* - "bootstrap.servers": envtool.GetEnv("KAFKA_HOSTS", "localhost:9093"), - "metadata.broker.list": envtool.GetEnv("KAFKA_METADATA_BROKER_LIST", ""), //Desc: Initial list of brokers as a CSV list of broker host or host:port. The application may also use 'rd_kafka_brokers_add()' to add brokers during runtime.
*Type: string* - // "message.max.bytes": envtool.GetEnvInt("KAFKA_MESSAGE_MAX_BYTES", 1000000), //Desc: Maximum Kafka protocol request message size. Due to differing framing overhead between protocol versions the producer is unable to reliably enforce a strict max message limit at produce time and may exceed the maximum size by one message in protocol ProduceRequests, the broker will enforce the the topic's 'max.message.bytes' limit (see Apache Kafka documentation).
*Type: integer* Range: 1000 .. 1000000000 - // "message.copy.max.bytes": envtool.GetEnvInt("KAFKA_MESSAGE_COPY_MAX_BYTES", 65535), //Desc: Maximum size for message to be copied to buffer. Messages larger than this will be passed by reference (zero-copy) at the expense of larger iovecs.
*Type: integer* Range: 0 .. 1000000000 - // "receive.message.max.bytes": envtool.GetEnvInt("KAFKA_RECEIVE_MESSAGE_MAX_BYTES", 100000000), //Desc: Maximum Kafka protocol response message size. This serves as a safety precaution to avoid memory exhaustion in case of protocol hickups. This value must be at least 'fetch.max.bytes' + 512 to allow for protocol overhead; the value is adjusted automatically unless the configuration property is explicitly set.
*Type: integer* Range: 1000 .. 2147483647 - // "max.in.flight.requests.per.connection": envtool.GetEnvInt("KAFKA_MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION", 1000000), //Desc: Maximum number of in-flight requests per broker connection. This is a generic property applied to all broker communication, however it is primarily relevant to produce requests. In particular, note that other mechanisms limit the number of outstanding consumer fetch request per broker to one.
*Type: integer* Range: 1 .. 1000000 - // "topic.metadata.refresh.interval.ms": envtool.GetEnvInt("KAFKA_TOPIC_METADATA_REFRESH_INTERVAL_MS", 300000), //Desc: Period of time in milliseconds at which topic and broker metadata is refreshed in order to proactively discover any new brokers, topics, partitions or partition leader changes. Use -1 to disable the intervalled refresh (not recommended). If there are no locally referenced topics (no topic objects created, no messages produced, no subscription or no assignment) then only the broker list will be refreshed every interval but no more often than every 10s.
*Type: integer* Range: -1 .. 3600000 - "metadata.max.age.ms": envtool.GetEnvInt("KAFKA_METADATA_MAX_AGE_MS", 900000), //Desc: Metadata cache max age. Defaults to topic.metadata.refresh.interval.ms * 3
*Type: integer* Range: 1 .. 86400000 - // "topic.metadata.refresh.fast.interval.ms": envtool.GetEnvInt("KAFKA_TOPIC_METADATA_REFRESH_FAST_INTERVAL_MS", 100), //Desc: When a topic loses its leader a new metadata request will be enqueued immediately and then with this initial interval, exponentially increasing upto 'retry.backoff.max.ms', until the topic metadata has been refreshed. If not set explicitly, it will be defaulted to 'retry.backoff.ms'. This is used to recover quickly from transitioning leader brokers.
*Type: integer* Range: 1 .. 60000 - // "topic.metadata.refresh.sparse": envtool.GetEnvBool("KAFKA_TOPIC_METADATA_REFRESH_SPARSE", true), //Desc: Sparse metadata requests (consumes less network bandwidth)
*Type: boolean* Range: true, false - // "topic.metadata.propagation.max.ms": envtool.GetEnvInt("KAFKA_TOPIC_METADATA_PROPAGATION_MAX_MS", 30000), //Desc: Apache Kafka topic creation is asynchronous and it takes some time for a new topic to propagate throughout the cluster to all brokers. If a client requests topic metadata after manual topic creation but before the topic has been fully propagated to the broker the client is requesting metadata from, the topic will seem to be non-existent and the client will mark the topic as such, failing queued produced messages with 'ERR__UNKNOWN_TOPIC'. This setting delays marking a topic as non-existent until the configured propagation max time has passed. The maximum propagation time is calculated from the time the topic is first referenced in the client, e.g., on produce().
*Type: integer* Range: 0 .. 3600000 - // "topic.blacklist": envtool.GetEnv("KAFKA_TOPIC_BLACKLIST", ""), //Desc: Topic blacklist, a comma-separated list of regular expressions for matching topic names that should be ignored in broker metadata information as if the topics did not exist.
*Type: pattern list* - // "debug": envtool.GetEnv("KAFKA_DEBUG", "broker,topic,msg"), //Desc: A comma-separated list of debug contexts to enable. Detailed Producer debugging: broker,topic,msg. Consumer: consumer,cgrp,topic,fetch
*Type: CSV flags* Range: generic, broker, topic, metadata, feature, queue, msg, protocol, cgrp, security, fetch, interceptor, plugin, consumer, admin, eos, mock, assignor, conf, all - "socket.timeout.ms": envtool.GetEnvInt("KAFKA_SOCKET_TIMEOUT_MS", 60000), //Desc: Default timeout for network requests. Producer: ProduceRequests will use the lesser value of 'socket.timeout.ms' and remaining 'message.timeout.ms' for the first message in the batch. Consumer: FetchRequests will use 'fetch.wait.max.ms' + 'socket.timeout.ms'. Admin: Admin requests will use 'socket.timeout.ms' or explicitly set 'rd_kafka_AdminOptions_set_operation_timeout()' value.
*Type: integer* Range: 10 .. 300000 - // "socket.send.buffer.bytes": envtool.GetEnvInt("KAFKA_SOCKET_SEND_BUFFER_BYTES", 0), //Desc: Broker socket send buffer size. System default is used if 0.
*Type: integer* Range: 0 .. 100000000 - // "socket.receive.buffer.bytes": envtool.GetEnvInt("KAFKA_SOCKET_RECEIVE_BUFFER_BYTES", 0), //Desc: Broker socket receive buffer size. System default is used if 0.
*Type: integer* Range: 0 .. 100000000 - "socket.keepalive.enable": envtool.GetEnvBool("KAFKA_SOCKET_KEEPALIVE_ENABLE", true), //Desc: Enable TCP keep-alives (SO_KEEPALIVE) on broker sockets
*Type: boolean* Range: true, false - // "socket.nagle.disable": envtool.GetEnvBool("KAFKA_SOCKET_NAGLE_DISABLE", false), //Desc: Disable the Nagle algorithm (TCP_NODELAY) on broker sockets.
*Type: boolean* Range: true, false - // "socket.max.fails": envtool.GetEnvInt("KAFKA_SOCKET_MAX_FAILS", 1), //Desc: Disconnect from broker when this number of send failures (e.g., timed out requests) is reached. Disable with 0. WARNING: It is highly recommended to leave this setting at its default value of 1 to avoid the client and broker to become desynchronized in case of request timeouts. NOTE: The connection is automatically re-established.
*Type: integer* Range: 0 .. 1000000 - // "broker.address.ttl": envtool.GetEnvInt("KAFKA_BROKER_ADDRESS_TTL", 1000), //Desc: How long to cache the broker address resolving results (milliseconds).
*Type: integer* Range: 0 .. 86400000 - // "broker.address.family": envtool.GetEnv("KAFKA_BROKER_ADDRESS_FAMILY", "any"), //Desc: Allowed broker IP address families: any, v4, v6
*Type: enum value* Range: any, v4, v6 - // "socket.connection.setup.timeout.ms": envtool.GetEnvInt("KAFKA_SOCKET_CONNECTION_SETUP_TIMEOUT_MS", 30000), //Desc: Maximum time allowed for broker connection setup (TCP connection setup as well SSL and SASL handshake). If the connection to the broker is not fully functional after this the connection will be closed and retried.
*Type: integer* Range: 1000 .. 2147483647 - "connections.max.idle.ms": envtool.GetEnvInt("KAFKA_CONNECTIONS_MAX_IDLE_MS", 0), //Desc: Close broker connections after the specified time of inactivity. Disable with 0. If this property is left at its default value some heuristics are performed to determine a suitable default value, this is currently limited to identifying brokers on Azure (see librdkafka issue #3109 for more info).
*Type: integer* Range: 0 .. 2147483647 - // "reconnect.backoff.ms": envtool.GetEnvInt("KAFKA_RECONNECT_BACKOFF_MS", 100), //Desc: The initial time to wait before reconnecting to a broker after the connection has been closed. The time is increased exponentially until 'reconnect.backoff.max.ms' is reached. -25% to +50% jitter is applied to each reconnect backoff. A value of 0 disables the backoff and reconnects immediately.
*Type: integer* Range: 0 .. 3600000 - // "reconnect.backoff.max.ms": envtool.GetEnvInt("KAFKA_RECONNECT_BACKOFF_MAX_MS", 10000), //Desc: The maximum time to wait before reconnecting to a broker after the connection has been closed.
*Type: integer* Range: 0 .. 3600000 - "statistics.interval.ms": envtool.GetEnvInt("KAFKA_STATISTICS_INTERVAL_MS", 0), //Desc: librdkafka statistics emit interval. The application also needs to register a stats callback using 'rd_kafka_conf_set_stats_cb()'. The granularity is 1000ms. A value of 0 disables statistics.
*Type: integer* Range: 0 .. 86400000 - // "enabled_events": envtool.GetEnvInt("KAFKA_ENABLED_EVENTS", 0), //Desc: See 'rd_kafka_conf_set_events()'
*Type: integer* Range: 0 .. 2147483647 - // "error_cb": envtool.GetEnv("KAFKA_ERROR_CB", ""), //Desc: Error callback (set with rd_kafka_conf_set_error_cb())
*Type: see dedicated API* - // "throttle_cb": envtool.GetEnv("KAFKA_THROTTLE_CB", ""), //Desc: Throttle callback (set with rd_kafka_conf_set_throttle_cb())
*Type: see dedicated API* - // "stats_cb": envtool.GetEnv("KAFKA_STATS_CB", ""), //Desc: Statistics callback (set with rd_kafka_conf_set_stats_cb())
*Type: see dedicated API* - // "log_cb": envtool.GetEnv("KAFKA_LOG_CB", ""), //Desc: Log callback (set with rd_kafka_conf_set_log_cb())
*Type: see dedicated API* - "log_level": envtool.GetEnvInt("KAFKA_LOG_LEVEL", 6), //Desc: Logging level (syslog(3) levels)
*Type: integer* Range: 0 .. 7 - // "log.queue": envtool.GetEnvBool("KAFKA_LOG_QUEUE", false), //Desc: Disable spontaneous log_cb from internal librdkafka threads, instead enqueue log messages on queue set with 'rd_kafka_set_log_queue()' and serve log callbacks or events through the standard poll APIs. **NOTE**: Log messages will linger in a temporary queue until the log queue has been set.
*Type: boolean* Range: true, false - // "log.thread.name": envtool.GetEnvBool("KAFKA_LOG_THREAD_NAME", true), //Desc: Print internal thread name in log messages (useful for debugging librdkafka internals)
*Type: boolean* Range: true, false - // "enable.random.seed": envtool.GetEnvBool("KAFKA_ENABLE_RANDOM_SEED", true), //Desc: If enabled librdkafka will initialize the PRNG with srand(current_time.milliseconds) on the first invocation of rd_kafka_new() (required only if rand_r() is not available on your platform). If disabled the application must call srand() prior to calling rd_kafka_new().
*Type: boolean* Range: true, false - "log.connection.close": envtool.GetEnvBool("KAFKA_LOG_CONNECTION_CLOSE", true), //Desc: Log broker disconnects. It might be useful to turn this off when interacting with 0.9 brokers with an aggressive 'connections.max.idle.ms' value.
*Type: boolean* Range: true, false - // "background_event_cb": envtool.GetEnv("KAFKA_BACKGROUND_EVENT_CB", ""), //Desc: Background queue event callback (set with rd_kafka_conf_set_background_event_cb())
*Type: see dedicated API* - // "socket_cb": envtool.GetEnv("KAFKA_SOCKET_CB", ""), //Desc: Socket creation callback to provide race-free CLOEXEC
*Type: see dedicated API* - // "connect_cb": envtool.GetEnv("KAFKA_CONNECT_CB", ""), //Desc: Socket connect callback
*Type: see dedicated API* - // "closesocket_cb": envtool.GetEnv("KAFKA_CLOSESOCKET_CB", ""), //Desc: Socket close callback
*Type: see dedicated API* - // "open_cb": envtool.GetEnv("KAFKA_OPEN_CB", ""), //Desc: File open callback to provide race-free CLOEXEC
*Type: see dedicated API* - // "resolve_cb": envtool.GetEnv("KAFKA_RESOLVE_CB", ""), //Desc: Address resolution callback (set with rd_kafka_conf_set_resolve_cb()).
*Type: see dedicated API* - // "opaque": envtool.GetEnv("KAFKA_OPAQUE", ""), //Desc: Application opaque (set with rd_kafka_conf_set_opaque())
*Type: see dedicated API* - // "default_topic_conf": envtool.GetEnv("KAFKA_DEFAULT_TOPIC_CONF", ""), //Desc: Default topic configuration for automatically subscribed topics
*Type: see dedicated API* - // "internal.termination.signal": envtool.GetEnvInt("KAFKA_INTERNAL_TERMINATION_SIGNAL", 0), //Desc: Signal that librdkafka will use to quickly terminate on rd_kafka_destroy(). If this signal is not set then there will be a delay before rd_kafka_wait_destroyed() returns true as internal threads are timing out their system calls. If this signal is set however the delay will be minimal. The application should mask this signal as an internal signal handler is installed.
*Type: integer* Range: 0 .. 128 - "api.version.request": envtool.GetEnvBool("KAFKA_API_VERSION_REQUEST", true), //Desc: Request broker's supported API versions to adjust functionality to available protocol features. If set to false, or the ApiVersionRequest fails, the fallback version 'broker.version.fallback' will be used. **NOTE**: Depends on broker version >=0.10.0. If the request is not supported by (an older) broker the 'broker.version.fallback' fallback is used.
*Type: boolean* Range: true, false - // "api.version.request.timeout.ms": envtool.GetEnvInt("KAFKA_API_VERSION_REQUEST_TIMEOUT_MS", 10000), //Desc: Timeout for broker API version requests.
*Type: integer* Range: 1 .. 300000 - "api.version.fallback.ms": envtool.GetEnvInt("KAFKA_API_VERSION_FALLBACK_MS", 0), //Desc: Dictates how long the 'broker.version.fallback' fallback is used in the case the ApiVersionRequest fails. **NOTE**: The ApiVersionRequest is only issued when a new connection to the broker is made (such as after an upgrade).
*Type: integer* Range: 0 .. 604800000 - "broker.version.fallback": envtool.GetEnv("KAFKA_BROKER_VERSION_FALLBACK", "0.10.0"), //Desc: Older broker versions (before 0.10.0) provide no way for a client to query for supported protocol features (ApiVersionRequest, see 'api.version.request') making it impossible for the client to know what features it may use. As a workaround a user may set this property to the expected broker version and the client will automatically adjust its feature set accordingly if the ApiVersionRequest fails (or is disabled). The fallback broker version will be used for 'api.version.fallback.ms'. Valid values are: 0.9.0, 0.8.2, 0.8.1, 0.8.0. Any other value >= 0.10, such as 0.10.2.1, enables ApiVersionRequests.
*Type: string* - "allow.auto.create.topics": envtool.GetEnvBool("KAFKA_ALLOW_AUTO_CREATE_TOPICS", true), //Desc: Allow automatic topic creation on the broker when subscribing to or assigning non-existent topics. The broker must also be configured with 'auto.create.topics.enable=true' for this configuration to take effect. Note: the default value (true) for the producer is different from the default value (false) for the consumer. Further, the consumer default value is different from the Java consumer (true), and this property is not supported by the Java producer. Requires broker version >= 0.11.0.0, for older broker versions only the broker configuration applies.
*Type: boolean* Range: true, false - "security.protocol": envtool.GetEnv("KAFKA_SECURITY_PROTOCOL", "plaintext"), //Desc: Protocol used to communicate with brokers.
*Type: enum value* Range: plaintext, ssl, sasl_plaintext, sasl_ssl - // "ssl.cipher.suites": envtool.GetEnv("KAFKA_SSL_CIPHER_SUITES", ""), //Desc: A cipher suite is a named combination of authentication, encryption, MAC and key exchange algorithm used to negotiate the security settings for a network connection using TLS or SSL network protocol. See manual page for 'ciphers(1)' and 'SSL_CTX_set_cipher_list(3).
*Type: string* - // "ssl.curves.list": envtool.GetEnv("KAFKA_SSL_CURVES_LIST", ""), //Desc: The supported-curves extension in the TLS ClientHello message specifies the curves (standard/named, or 'explicit' GF(2^k) or GF(p)) the client is willing to have the server use. See manual page for 'SSL_CTX_set1_curves_list(3)'. OpenSSL >= 1.0.2 required.
*Type: string* - // "ssl.sigalgs.list": envtool.GetEnv("KAFKA_SSL_SIGALGS_LIST", ""), //Desc: The client uses the TLS ClientHello signature_algorithms extension to indicate to the server which signature/hash algorithm pairs may be used in digital signatures. See manual page for 'SSL_CTX_set1_sigalgs_list(3)'. OpenSSL >= 1.0.2 required.
*Type: string* - // "ssl.key.location": envtool.GetEnv("KAFKA_SSL_KEY_LOCATION", ""), //Desc: Path to client's private key (PEM) used for authentication.
*Type: string* - // "ssl.key.password": envtool.GetEnv("KAFKA_SSL_KEY_PASSWORD", ""), //Desc: Private key passphrase (for use with 'ssl.key.location' and 'set_ssl_cert()')
*Type: string* - // "ssl.key.pem": envtool.GetEnv("KAFKA_SSL_KEY_PEM", ""), //Desc: Client's private key string (PEM format) used for authentication.
*Type: string* - // "ssl_key": envtool.GetEnv("KAFKA_SSL_KEY", ""), //Desc: Client's private key as set by rd_kafka_conf_set_ssl_cert()
*Type: see dedicated API* - // "ssl.certificate.location": envtool.GetEnv("KAFKA_SSL_CERTIFICATE_LOCATION", ""), //Desc: Path to client's public key (PEM) used for authentication.
*Type: string* - // "ssl.certificate.pem": envtool.GetEnv("KAFKA_SSL_CERTIFICATE_PEM", ""), //Desc: Client's public key string (PEM format) used for authentication.
*Type: string* - // "ssl_certificate": envtool.GetEnv("KAFKA_SSL_CERTIFICATE", ""), //Desc: Client's public key as set by rd_kafka_conf_set_ssl_cert()
*Type: see dedicated API* - // "ssl.ca.location": envtool.GetEnv("KAFKA_SSL_CA_LOCATION", ""), //Desc: File or directory path to CA certificate(s) for verifying the broker's key. Defaults: On Windows the system's CA certificates are automatically looked up in the Windows Root certificate store. On Mac OSX this configuration defaults to 'probe'. It is recommended to install openssl using Homebrew, to provide CA certificates. On Linux install the distribution's ca-certificates package. If OpenSSL is statically linked or 'ssl.ca.location' is set to 'probe' a list of standard paths will be probed and the first one found will be used as the default CA certificate location path. If OpenSSL is dynamically linked the OpenSSL library's default path will be used (see 'OPENSSLDIR' in 'openssl version -a').
*Type: string* - // "ssl.ca.pem": envtool.GetEnv("KAFKA_SSL_CA_PEM", ""), //Desc: CA certificate string (PEM format) for verifying the broker's key.
*Type: string* - // "ssl_ca": envtool.GetEnv("KAFKA_SSL_CA", ""), //Desc: CA certificate as set by rd_kafka_conf_set_ssl_cert()
*Type: see dedicated API* - // "ssl.ca.certificate.stores": envtool.GetEnv("KAFKA_SSL_CA_CERTIFICATE_STORES", "Root"), //Desc: Comma-separated list of Windows Certificate stores to load CA certificates from. Certificates will be loaded in the same order as stores are specified. If no certificates can be loaded from any of the specified stores an error is logged and the OpenSSL library's default CA location is used instead. Store names are typically one or more of: MY, Root, Trust, CA.
*Type: string* - // "ssl.crl.location": envtool.GetEnv("KAFKA_SSL_CRL_LOCATION", ""), //Desc: Path to CRL for verifying broker's certificate validity.
*Type: string* - // "ssl.keystore.location": envtool.GetEnv("KAFKA_SSL_KEYSTORE_LOCATION", ""), //Desc: Path to client's keystore (PKCS#12) used for authentication.
*Type: string* - // "ssl.keystore.password": envtool.GetEnv("KAFKA_SSL_KEYSTORE_PASSWORD", ""), //Desc: Client's keystore (PKCS#12) password.
*Type: string* - // "ssl.providers": envtool.GetEnv("KAFKA_SSL_PROVIDERS", ""), //Desc: Comma-separated list of OpenSSL 3.0.x implementation providers. E.g., "default,legacy".
*Type: string* - // "ssl.engine.id": envtool.GetEnv("KAFKA_SSL_ENGINE_ID", "dynamic"), //Desc: OpenSSL engine id is the name used for loading engine.
*Type: string* - // "ssl_engine_callback_data": envtool.GetEnv("KAFKA_SSL_ENGINE_CALLBACK_DATA", ""), //Desc: OpenSSL engine callback data (set with rd_kafka_conf_set_engine_callback_data()).
*Type: see dedicated API* - "enable.ssl.certificate.verification": envtool.GetEnvBool("KAFKA_SSL_VERIFY", true), //Desc: Enable OpenSSL's builtin broker (server) certificate verification. This verification can be extended by the application by implementing a certificate_verify_cb. Type: boolean - // "ssl.endpoint.identification.algorithm": envtool.GetEnv("KAFKA_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM", "https"), //Desc: Endpoint identification algorithm to validate broker hostname using broker certificate. https - Server (broker) hostname verification as specified in RFC2818. none - No endpoint verification. OpenSSL >= 1.0.2 required.
*Type: enum value* Range: none, https - // "ssl.certificate.verify_cb": envtool.GetEnv("KAFKA_SSL_CERTIFICATE_VERIFY_CB", ""), //Desc: Callback to verify the broker certificate chain.
*Type: see dedicated API* - "sasl.mechanisms": envtool.GetEnv("KAFKA_SASL_MECHANISMS", "GSSAPI"), //Desc: SASL mechanism to use for authentication. Supported: GSSAPI, PLAIN, SCRAM-SHA-256, SCRAM-SHA-512, OAUTHBEARER. **NOTE**: Despite the name only one mechanism must be configured.
*Type: string* - // "sasl.kerberos.service.name": envtool.GetEnv("KAFKA_SASL_KERBEROS_SERVICE_NAME", "kafka"), //Desc: Kerberos principal name that Kafka runs as, not including /hostname@REALM
*Type: string* - // "sasl.kerberos.principal": envtool.GetEnv("KAFKA_SASL_KERBEROS_PRINCIPAL", "kafkaclient"), //Desc: This client's Kerberos principal name. (Not supported on Windows, will use the logon user's principal).
*Type: string* - // "sasl.kerberos.kinit.cmd": envtool.GetEnv("KAFKA_SASL_KERBEROS_KINIT_CMD", kafka_sasl_kerberos_kinit_cmd_default), //Desc: Shell command to refresh or acquire the client's Kerberos ticket. This command is executed on client creation and every sasl.kerberos.min.time.before.relogin (0=disable). %{config.prop.name} is replaced by corresponding config object value.
*Type: string* - // "sasl.kerberos.keytab": envtool.GetEnv("KAFKA_SASL_KERBEROS_KEYTAB", ""), //Desc: Path to Kerberos keytab file. This configuration property is only used as a variable in 'sasl.kerberos.kinit.cmd' as ' ... -t "%{sasl.kerberos.keytab}"'.
*Type: string* - // "sasl.kerberos.min.time.before.relogin": envtool.GetEnvInt("KAFKA_SASL_KERBEROS_MIN_TIME_BEFORE_RELOGIN", 60000), //Desc: Minimum time in milliseconds between key refresh attempts. Disable automatic key refresh by setting this property to 0.
*Type: integer* Range: 0 .. 86400000 - "sasl.username": envtool.GetEnv("KAFKA_SASL_USERNAME", ""), //Desc: SASL username for use with the PLAIN and SASL-SCRAM-.. mechanisms
*Type: string* - "sasl.password": envtool.GetEnv("KAFKA_SASL_PASSWORD", ""), //Desc: SASL password for use with the PLAIN and SASL-SCRAM-.. mechanism
*Type: string* - // "sasl.oauthbearer.config": envtool.GetEnv("KAFKA_SASL_OAUTHBEARER_CONFIG", ""), //Desc: SASL/OAUTHBEARER configuration. The format is implementation-dependent and must be parsed accordingly. The default unsecured token implementation (see https://tools.ietf.org/html/rfc7515#appendix-A.5) recognizes space-separated name=value pairs with valid names including principalClaimName, principal, scopeClaimName, scope, and lifeSeconds. The default value for principalClaimName is "sub", the default value for scopeClaimName is "scope", and the default value for lifeSeconds is 3600. The scope value is CSV format with the default value being no/empty scope. For example: 'principalClaimName=azp principal=admin scopeClaimName=roles scope=role1,role2 lifeSeconds=600'. In addition, SASL extensions can be communicated to the broker via 'extension_NAME=value'. For example: 'principal=admin extension_traceId=123'
*Type: string* - // "enable.sasl.oauthbearer.unsecure.jwt": envtool.GetEnvBool("KAFKA_ENABLE_SASL_OAUTHBEARER_UNSECURE_JWT", false), //Desc: Enable the builtin unsecure JWT OAUTHBEARER token handler if no oauthbearer_refresh_cb has been set. This builtin handler should only be used for development or testing, and not in production.
*Type: boolean* Range: true, false - // "oauthbearer_token_refresh_cb": envtool.GetEnv("KAFKA_OAUTHBEARER_TOKEN_REFRESH_CB", ""), //Desc: SASL/OAUTHBEARER token refresh callback (set with rd_kafka_conf_set_oauthbearer_token_refresh_cb(), triggered by rd_kafka_poll(), et.al. This callback will be triggered when it is time to refresh the client's OAUTHBEARER token. Also see 'rd_kafka_conf_enable_sasl_queue()'.
*Type: see dedicated API* - // "sasl.oauthbearer.method": envtool.GetEnv("KAFKA_SASL_OAUTHBEARER_METHOD", "default"), //Desc: Set to "default" or "oidc" to control which login method to be used. If set to "oidc", the following properties must also be be specified: 'sasl.oauthbearer.client.id', 'sasl.oauthbearer.client.secret', and 'sasl.oauthbearer.token.endpoint.url'.
*Type: enum value* Range: default, oidc - // "sasl.oauthbearer.client.id": envtool.GetEnv("KAFKA_SASL_OAUTHBEARER_CLIENT_ID", ""), //Desc: Public identifier for the application. Must be unique across all clients that the authorization server handles. Only used when 'sasl.oauthbearer.method' is set to "oidc".
*Type: string* - // "sasl.oauthbearer.client.secret": envtool.GetEnv("KAFKA_SASL_OAUTHBEARER_CLIENT_SECRET", ""), //Desc: Client secret only known to the application and the authorization server. This should be a sufficiently random string that is not guessable. Only used when 'sasl.oauthbearer.method' is set to "oidc".
*Type: string* - // "sasl.oauthbearer.scope": envtool.GetEnv("KAFKA_SASL_OAUTHBEARER_SCOPE", ""), //Desc: Client use this to specify the scope of the access request to the broker. Only used when 'sasl.oauthbearer.method' is set to "oidc".
*Type: string* - // "sasl.oauthbearer.extensions": envtool.GetEnv("KAFKA_SASL_OAUTHBEARER_EXTENSIONS", ""), //Desc: Allow additional information to be provided to the broker. Comma-separated list of key=value pairs. E.g., "supportFeatureX=true,organizationId=sales-emea".Only used when 'sasl.oauthbearer.method' is set to "oidc".
*Type: string* - // "sasl.oauthbearer.token.endpoint.url": envtool.GetEnv("KAFKA_SASL_OAUTHBEARER_TOKEN_ENDPOINT_URL", ""), //Desc: OAuth/OIDC issuer token endpoint HTTP(S) URI used to retrieve token. Only used when 'sasl.oauthbearer.method' is set to "oidc".
*Type: string* - // "plugin.library.paths": envtool.GetEnv("KAFKA_PLUGIN_LIBRARY_PATHS", ""), //Desc: List of plugin libraries to load (; separated). The library search path is platform dependent (see dlopen(3) for Unix and LoadLibrary() for Windows). If no filename extension is specified the platform-specific extension (such as .dll or .so) will be appended automatically.
*Type: string* - // "interceptors": envtool.GetEnv("KAFKA_INTERCEPTORS", ""), //Desc: Interceptors added through rd_kafka_conf_interceptor_add_..() and any configuration handled by interceptors.
*Type: see dedicated API* - // "client.rack": envtool.GetEnv("KAFKA_CLIENT_RACK", ""), //Desc: A rack identifier for this client. This can be any string value which indicates where this client is physically located. It corresponds with the broker config 'broker.rack'.
*Type: string* - // "transactional.id": envtool.GetEnv("KAFKA_TRANSACTIONAL_ID", ""), //Desc: Enables the transactional producer. The transactional.id is used to identify the same transactional producer instance across process restarts. It allows the producer to guarantee that transactions corresponding to earlier instances of the same producer have been finalized prior to starting any new transactions, and that any zombie instances are fenced off. If no transactional.id is provided, then the producer is limited to idempotent delivery (if enable.idempotence is set). Requires broker version >= 0.11.0.
*Type: string* - // "transaction.timeout.ms": envtool.GetEnvInt("KAFKA_TRANSACTION_TIMEOUT_MS", 60000), //Desc: The maximum amount of time in milliseconds that the transaction coordinator will wait for a transaction status update from the producer before proactively aborting the ongoing transaction. If this value is larger than the 'transaction.max.timeout.ms' setting in the broker, the init_transactions() call will fail with ERR_INVALID_TRANSACTION_TIMEOUT. The transaction timeout automatically adjusts 'message.timeout.ms' and 'socket.timeout.ms', unless explicitly configured in which case they must not exceed the transaction timeout ('socket.timeout.ms' must be at least 100ms lower than 'transaction.timeout.ms'). This is also the default timeout value if no timeout (-1) is supplied to the transactional API methods.
*Type: integer* Range: 1000 .. 2147483647 - "enable.idempotence": envtool.GetEnvBool("KAFKA_ENABLE_IDEMPOTENCE", false), //Desc: When set to 'true', the producer will ensure that messages are successfully produced exactly once and in the original produce order. The following configuration properties are adjusted automatically (if not modified by the user) when idempotence is enabled: 'max.in.flight.requests.per.connection=5' (must be less than or equal to 5), 'retries=INT32_MAX' (must be greater than 0), 'acks=all', 'queuing.strategy=fifo'. Producer instantation will fail if user-supplied configuration is incompatible.
*Type: boolean* Range: true, false - // "enable.gapless.guarantee": envtool.GetEnvBool("KAFKA_ENABLE_GAPLESS_GUARANTEE", false), //Desc: **EXPERIMENTAL**: subject to change or removal. When set to 'true', any error that could result in a gap in the produced message series when a batch of messages fails, will raise a fatal error (ERR__GAPLESS_GUARANTEE) and stop the producer. Messages failing due to 'message.timeout.ms' are not covered by this guarantee. Requires 'enable.idempotence=true'.
*Type: boolean* Range: true, false - "queue.buffering.max.messages": envtool.GetEnvInt("KAFKA_QUEUE_BUFFERING_MAX_MESSAGES", 100000), //Desc: Maximum number of messages allowed on the producer queue. This queue is shared by all topics and partitions. A value of 0 disables this limit.
*Type: integer* Range: 0 .. 2147483647 - "queue.buffering.max.kbytes": envtool.GetEnvInt("KAFKA_QUEUE_BUFFERING_MAX_KBYTES", 1048576), //Desc: Maximum total message size sum allowed on the producer queue. This queue is shared by all topics and partitions. This property has higher priority than queue.buffering.max.messages.
*Type: integer* Range: 1 .. 2147483647 - "queue.buffering.max.ms": envtool.GetEnvInt("KAFKA_QUEUE_BUFFERING_MAX_MS", 5), //Desc: Delay in milliseconds to wait for messages in the producer queue to accumulate before constructing message batches (MessageSets) to transmit to brokers. A higher value allows larger and more effective (less overhead, improved compression) batches of messages to accumulate at the expense of increased message delivery latency.
*Type: float* Range: 0 .. 900000 - "message.send.max.retries": envtool.GetEnvInt("KAFKA_MESSAGE_SEND_MAX_RETRIES", 2147483647), //Desc: How many times to retry sending a failing Message. **Note:** retrying may cause reordering unless 'enable.idempotence' is set to true.
*Type: integer* Range: 0 .. 2147483647 - // "retry.backoff.ms": envtool.GetEnvInt("KAFKA_RETRY_BACKOFF_MS", 100), //Desc: The backoff time in milliseconds before retrying a protocol request, this is the first backoff time, and will be backed off exponentially until number of retries is exhausted, and it's capped by retry.backoff.max.ms.
*Type: integer* Range: 1 .. 300000 - // "retry.backoff.max.ms": envtool.GetEnvInt("KAFKA_RETRY_BACKOFF_MAX_MS", 1000), //Desc: The max backoff time in milliseconds before retrying a protocol request, this is the atmost backoff allowed for exponentially backed off requests.
*Type: integer* Range: 1 .. 300000 - // "queue.buffering.backpressure.threshold": envtool.GetEnvInt("KAFKA_QUEUE_BUFFERING_BACKPRESSURE_THRESHOLD", 1), //Desc: The threshold of outstanding not yet transmitted broker requests needed to backpressure the producer's message accumulator. If the number of not yet transmitted requests equals or exceeds this number, produce request creation that would have otherwise been triggered (for example, in accordance with linger.ms) will be delayed. A lower number yields larger and more effective batches. A higher value can improve latency when using compression on slow machines.
*Type: integer* Range: 1 .. 1000000 - "compression.codec": envtool.GetEnv("KAFKA_COMPRESSION_CODEC", "none"), //Desc: compression codec to use for compressing message sets. This is the default value for all topics, may be overridden by the topic configuration property 'compression.codec'.
*Type: enum value* Range: none, gzip, snappy, lz4, zstd - "batch.num.messages": envtool.GetEnvInt("KAFKA_BATCH_NUM_MESSAGES", 10000), //Desc: Maximum number of messages batched in one MessageSet. The total MessageSet size is also limited by batch.size and message.max.bytes.
*Type: integer* Range: 1 .. 1000000 - "batch.size": envtool.GetEnvInt("KAFKA_BATCH_SIZE", 1000000), //Desc: Maximum size (in bytes) of all messages batched in one MessageSet, including protocol framing overhead. This limit is applied after the first message has been added to the batch, regardless of the first message's size, this is to ensure that messages that exceed batch.size are produced. The total MessageSet size is also limited by batch.num.messages and message.max.bytes.
*Type: integer* Range: 1 .. 2147483647 - // "delivery.report.only.error": envtool.GetEnvBool("KAFKA_DELIVERY_REPORT_ONLY_ERROR", false), //Desc: Only provide delivery reports for failed messages.
*Type: boolean* Range: true, false - // "dr_cb": envtool.GetEnv("KAFKA_DR_CB", ""), //Desc: Delivery report callback (set with rd_kafka_conf_set_dr_cb())
*Type: see dedicated API* - // "dr_msg_cb": envtool.GetEnv("KAFKA_DR_MSG_CB", ""), //Desc: Delivery report callback (set with rd_kafka_conf_set_dr_msg_cb())
*Type: see dedicated API* - "sticky.partitioning.linger.ms": envtool.GetEnvInt("KAFKA_STICKY_PARTITIONING_LINGER_MS", 10), //Desc: Delay in milliseconds to wait to assign new sticky partitions for each topic. By default, set to double the time of linger.ms. To disable sticky behavior, set to 0. This behavior affects messages with the key NULL in all cases, and messages with key lengths of zero when the consistent_random partitioner is in use. These messages would otherwise be assigned randomly. A higher value allows for more effective batching of these messages.
*Type: integer* Range: 0 .. 900000 - // "client.dns.lookup": envtool.GetEnv("KAFKA_CLIENT_DNS_LOOKUP", "use_all_dns_ips"), //Desc: Controls how the client uses DNS lookups. By default, when the lookup returns multiple IP addresses for a hostname, they will all be attempted for connection before the connection is considered failed. This applies to both bootstrap and advertised servers. If the value is set to 'resolve_canonical_bootstrap_servers_only', each entry will be resolved and expanded into a list of canonical names. NOTE: Default here is different from the Java client's default behavior, which connects only to the first IP address returned for a hostname.
*Type: enum value* Range: use_all_dns_ips, resolve_canonical_bootstrap_servers_only - "partitioner": envtool.GetEnv("KAFKA_PARTITIONER", "consistent_random"), //Desc: artitioner: random - random distribution, consistent - CRC32 hash of key (Empty and NULL keys are mapped to single partition), consistent_random - CRC32 hash of key (Empty and NULL keys are randomly partitioned), murmur2 - Java Producer compatible Murmur2 hash of key (NULL keys are mapped to single partition), murmur2_random - Java Producer compatible Murmur2 hash of key (NULL keys are randomly partitioned. This is functionally equivalent to the default partitioner in the Java Producer.), fnv1a - FNV-1a hash of key (NULL keys are mapped to single partition), fnv1a_random - FNV-1a hash of key (NULL keys are randomly partitioned). Type: string - "request.timeout.ms": envtool.GetEnvInt("KAFKA_REQUEST_TIMEOUT_MS", 30000), //Desc: The ack timeout of the producer request in milliseconds. This value is only enforced by the broker and relies on request.required.acks being != 0. Type: integer - } - return -} diff --git a/pkg/kafka/mock/kafka_test_case.go b/pkg/kafka/mock/kafka_test_case.go index 779da3a..834fcbb 100644 --- a/pkg/kafka/mock/kafka_test_case.go +++ b/pkg/kafka/mock/kafka_test_case.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) type KafkaTestCase struct { diff --git a/pkg/kafka/producer.go b/pkg/kafka/producer.go index d33db64..552a50d 100644 --- a/pkg/kafka/producer.go +++ b/pkg/kafka/producer.go @@ -3,14 +3,14 @@ package kafka import ( "context" "encoding/json" + "sync" - "fiskerinc.com/modules/logger" - - "github.com/confluentinc/confluent-kafka-go/v2/kafka" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/pkg/errors" + "github.com/twmb/franz-go/pkg/kgo" ) -// ProducerInterface interface for Producer utilty +// ProducerInterface interface for Producer utility type ProducerInterface interface { Produce(topic string, key string, payload interface{}, headers map[string][]byte) error ProduceBinary(topic string, key string, data []byte, headers map[string][]byte) error @@ -25,168 +25,179 @@ type ProducerInterface interface { // Producer utility to produce messages type Producer struct { - producer *kafka.Producer + client *kgo.Client + ctx context.Context + cancel context.CancelFunc + pending int + pendingMu sync.Mutex } // NewProducer serves as factory method for producer to kafka func NewProducer(ctx context.Context) (ProducerInterface, error) { - var producer *kafka.Producer - configuration := loadKafkaProducerConfig() - producer, err := kafka.NewProducer( - &configuration, + cfg := LoadConfig() + opts := buildClientOpts(cfg) + + // Producer-specific options + opts = append(opts, + kgo.ProducerBatchCompression(kgo.NoCompression()), + kgo.AllowAutoTopicCreation(), ) - logger.Info().Msgf("NewProducer hosts: %s", kafkaHosts) + + client, err := kgo.NewClient(opts...) if err != nil { return nil, errors.WithStack(err) } - return &Producer{producer: producer}, nil + + logger.Info().Msgf("NewProducer hosts: %v", cfg.Brokers) + + pctx, cancel := context.WithCancel(ctx) + return &Producer{ + client: client, + ctx: pctx, + cancel: cancel, + }, nil } -// SetProducer sets the producer instance -func (p *Producer) SetProducer(producer *kafka.Producer) { - p.producer = producer -} - -// Len returns len of messages in queue. +// Len returns len of messages in queue (approximate) func (p *Producer) Len() int { - return p.producer.Len() + p.pendingMu.Lock() + defer p.pendingMu.Unlock() + return p.pending } -// Flush calls producer's Flush function. +// Flush waits for all buffered records to be flushed func (p *Producer) Flush(timeoutMs int) int { - return p.producer.Flush(timeoutMs) + ctx := p.ctx + if timeoutMs > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(p.ctx, durationMs(timeoutMs)) + defer cancel() + } + if err := p.client.Flush(ctx); err != nil { + logger.Warn().Err(err).Msg("flush incomplete") + return p.Len() + } + return 0 } -// Produce sends a Kafka Message to Kafka +// Produce sends a JSON-encoded message to Kafka func (p *Producer) Produce(topic string, key string, payload interface{}, headers map[string][]byte) error { v, err := json.Marshal(payload) if err != nil { return errors.WithStack(err) } - return p.ProduceBinary(topic, key, v, headers) } -func (p *Producer) makeHeaders(headers map[string][]byte) []kafka.Header { +func makeHeaders(headers map[string][]byte) []kgo.RecordHeader { if headers == nil { return nil } - - i := 0 - result := make([]kafka.Header, len(headers)) - - for key, value := range headers { - result[i].Key = key - result[i].Value = value - i++ + result := make([]kgo.RecordHeader, 0, len(headers)) + for k, v := range headers { + result = append(result, kgo.RecordHeader{Key: k, Value: v}) } - return result } -// Produce sends a Kafka Message to Kafka +// ProduceBinary sends a binary message to Kafka synchronously func (p *Producer) ProduceBinary(topic string, key string, data []byte, headers map[string][]byte) error { - km := kafka.Message{ - TopicPartition: kafka.TopicPartition{Topic: &topic, Partition: kafka.PartitionAny}, - Key: []byte(key), - Value: data, + record := &kgo.Record{ + Topic: topic, + Key: []byte(key), + Value: data, + Headers: makeHeaders(headers), } - if headers != nil { - km.Headers = p.makeHeaders(headers) - } - deliveryChan := make(chan kafka.Event) + p.pendingMu.Lock() + p.pending++ + p.pendingMu.Unlock() - err := p.producer.Produce(&km, deliveryChan) - if err != nil { + result := p.client.ProduceSync(p.ctx, record) + + p.pendingMu.Lock() + p.pending-- + p.pendingMu.Unlock() + + if err := result.FirstErr(); err != nil { + logger.Error().Err(err).Msgf("Delivery failed to topic %s", topic) return errors.WithStack(err) } - for e := range deliveryChan { - switch ev := e.(type) { - case *kafka.Message: - // The message delivery report, indicating success or - // permanent failure after retries have been exhausted. - // Application level retries won't help since the client - // is already configured to do that. - m := ev - if m.TopicPartition.Error != nil { - logger.Error().Msgf("Delivery failed: %v\n", m.TopicPartition.Error) - return m.TopicPartition.Error - } else { - logger.Info().Msgf("Delivered message to topic %s [%d] at offset %v\n", - *m.TopicPartition.Topic, m.TopicPartition.Partition, m.TopicPartition.Offset) - return nil - } - default: - logger.Debug().Msgf("Ignored event: %s\n", ev) - } - } + + r := result[0].Record + logger.Info().Msgf("Delivered message to topic %s [%d] at offset %d", r.Topic, r.Partition, r.Offset) return nil } -// ProduceBatch is a stub for async producer -func (p *Producer) ProduceBatch(topic string, key string, payload [][]byte) error { - // stub - return nil -} - -// ProduceToChannel is a stub for async producer +// ProduceToChannel sends a message asynchronously func (p *Producer) ProduceToChannel(topic string, key string, payload interface{}) error { v, err := json.Marshal(payload) if err != nil { return errors.WithStack(err) } - km := kafka.Message{ - TopicPartition: kafka.TopicPartition{Topic: &topic, Partition: kafka.PartitionAny}, - Key: []byte(key), - Value: v, + record := &kgo.Record{ + Topic: topic, + Key: []byte(key), + Value: v, } - p.producer.ProduceChannel() <- &km - return nil -} + p.pendingMu.Lock() + p.pending++ + p.pendingMu.Unlock() -// ProduceBinaryToChannel producing binary to a channel. -func (p *Producer) ProduceBinaryToChannel(topic string, key string, payload []byte) error { - km := kafka.Message{ - TopicPartition: kafka.TopicPartition{Topic: &topic, Partition: kafka.PartitionAny}, - Key: []byte(key), - Value: payload, - } + p.client.Produce(p.ctx, record, func(r *kgo.Record, err error) { + p.pendingMu.Lock() + p.pending-- + p.pendingMu.Unlock() - p.producer.ProduceChannel() <- &km - return nil -} - -// Produce sends a Kafka Message to Kafka -func (p *Producer) ProduceSignalBatch(topic string, key string, payload interface{}) error { - // stub - return nil -} - -// ReadEvents is a stub for async producer -func (p *Producer) ReadEvents() { - for e := range p.producer.Events() { - switch ev := e.(type) { - case kafka.Error: - // Generic client instance-level errors, such as - // broker connection failures, authentication issues, etc. - // - // These errors should generally be considered informational - // as the underlying client will automatically try to - // recover from any errors encountered, the application - // does not need to take action on them. - logger.Error().Msgf("Error: %v\n", ev) - default: - logger.Debug().Msgf("Ignored event: %s\n", ev) + if err != nil { + logger.Error().Err(err).Msgf("Async delivery failed to topic %s", topic) } + }) + + return nil +} + +// ProduceBinaryToChannel sends binary data asynchronously +func (p *Producer) ProduceBinaryToChannel(topic string, key string, payload []byte) error { + record := &kgo.Record{ + Topic: topic, + Key: []byte(key), + Value: payload, } + + p.pendingMu.Lock() + p.pending++ + p.pendingMu.Unlock() + + p.client.Produce(p.ctx, record, func(r *kgo.Record, err error) { + p.pendingMu.Lock() + p.pending-- + p.pendingMu.Unlock() + + if err != nil { + logger.Error().Err(err).Msgf("Async delivery failed to topic %s", topic) + } + }) + + return nil +} + +// ProduceSignalBatch is a stub for batch signal production +func (p *Producer) ProduceSignalBatch(topic string, key string, payload interface{}) error { + // stub - implement if needed + return nil +} + +// ReadEvents is a no-op for franz-go (callbacks handle events) +func (p *Producer) ReadEvents() { + // franz-go uses callbacks, this is kept for interface compatibility } // Close the Kafka Producer func (p *Producer) Close() { - p.producer.Flush(0) - p.producer.Close() - p.producer = nil + p.Flush(5000) + p.cancel() + p.client.Close() } diff --git a/pkg/kafka/producer_test.go b/pkg/kafka/producer_test.go index 089003d..008e9c8 100644 --- a/pkg/kafka/producer_test.go +++ b/pkg/kafka/producer_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/confluentinc/confluent-kafka-go/v2/kafka" ) diff --git a/pkg/kafka/unhandled_msg.go b/pkg/kafka/unhandled_msg.go index 479726c..0373b68 100644 --- a/pkg/kafka/unhandled_msg.go +++ b/pkg/kafka/unhandled_msg.go @@ -1,7 +1,7 @@ package kafka import ( - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/pkg/errors" ) diff --git a/pkg/kafka/unhandled_msg_test.go b/pkg/kafka/unhandled_msg_test.go index 72e25bd..0ea11c3 100644 --- a/pkg/kafka/unhandled_msg_test.go +++ b/pkg/kafka/unhandled_msg_test.go @@ -3,9 +3,9 @@ package kafka_test import ( "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/kafka" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/kafka" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestErrUnhandledMessage(t *testing.T) { diff --git a/pkg/kafka/util.go b/pkg/kafka/util.go new file mode 100644 index 0000000..728f7e8 --- /dev/null +++ b/pkg/kafka/util.go @@ -0,0 +1,8 @@ +package kafka + +import "time" + +// durationMs converts milliseconds to time.Duration +func durationMs(ms int) time.Duration { + return time.Duration(ms) * time.Millisecond +} diff --git a/pkg/logger/limiter_test.go b/pkg/logger/limiter_test.go index 17cf4a4..e47e826 100644 --- a/pkg/logger/limiter_test.go +++ b/pkg/logger/limiter_test.go @@ -6,8 +6,8 @@ import ( "testing" "time" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestLimiter(t *testing.T) { diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index a94e3d6..1596e5a 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -9,7 +9,7 @@ import ( "strings" "sync" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/rs/zerolog" "github.com/rs/zerolog/diode" "github.com/rs/zerolog/pkgerrors" diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go index 47fbe1d..664a1c8 100644 --- a/pkg/logger/logger_test.go +++ b/pkg/logger/logger_test.go @@ -7,7 +7,7 @@ import ( "os" "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/rs/zerolog" "github.com/rs/zerolog/diode" diff --git a/pkg/logger/mask_test.go b/pkg/logger/mask_test.go index 12dff66..1efc086 100644 --- a/pkg/logger/mask_test.go +++ b/pkg/logger/mask_test.go @@ -3,7 +3,7 @@ package logger import ( "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestMaskEmailEasy(t *testing.T) { diff --git a/pkg/loggerdataresp/data_error_resp.go b/pkg/loggerdataresp/data_error_resp.go index 445f165..bbeff20 100644 --- a/pkg/loggerdataresp/data_error_resp.go +++ b/pkg/loggerdataresp/data_error_resp.go @@ -6,14 +6,14 @@ import ( "net/http" "strings" - "fiskerinc.com/modules/common/staticerrors" - er "fiskerinc.com/modules/errors" - e "fiskerinc.com/modules/mongo/error" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common/staticerrors" + er "github.com/fiskerinc/cloud-services/pkg/errors" + e "github.com/fiskerinc/cloud-services/pkg/mongo/error" + "github.com/fiskerinc/cloud-services/pkg/validator" "go.mongodb.org/mongo-driver/mongo" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils" cKafka "github.com/confluentinc/confluent-kafka-go/v2/kafka" "github.com/go-pg/pg/v10" "github.com/pkg/errors" diff --git a/pkg/loggerdataresp/data_error_resp_test.go b/pkg/loggerdataresp/data_error_resp_test.go index fa19a33..fc16afc 100644 --- a/pkg/loggerdataresp/data_error_resp_test.go +++ b/pkg/loggerdataresp/data_error_resp_test.go @@ -8,9 +8,9 @@ import ( "net/http/httptest" "testing" - "fiskerinc.com/modules/testhelper" - ve "fiskerinc.com/modules/validator" - "fiskerinc.com/modules/loggerdataresp" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + ve "github.com/fiskerinc/cloud-services/pkg/validator" + "github.com/fiskerinc/cloud-services/pkg/loggerdataresp" ) func TestBadDataErrorResp(t *testing.T) { diff --git a/pkg/manifestsender/tbox_config_manifest_sender.go b/pkg/manifestsender/tbox_config_manifest_sender.go index 0b3319a..af810a3 100644 --- a/pkg/manifestsender/tbox_config_manifest_sender.go +++ b/pkg/manifestsender/tbox_config_manifest_sender.go @@ -6,12 +6,12 @@ import ( "errors" - "fiskerinc.com/modules/carcommand" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/common/carupdatestatus" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/whereami" + "github.com/fiskerinc/cloud-services/pkg/carcommand" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/common/carupdatestatus" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/whereami" ) // ProcessConfigUpdate handles all steps on the config only update diff --git a/pkg/manifestsender/tbox_manifest_sender.go b/pkg/manifestsender/tbox_manifest_sender.go index cb24442..18020b1 100644 --- a/pkg/manifestsender/tbox_manifest_sender.go +++ b/pkg/manifestsender/tbox_manifest_sender.go @@ -4,17 +4,17 @@ import ( "errors" "fmt" - "fiskerinc.com/modules/carcommand" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/common/manifestfingerprintparams" - "fiskerinc.com/modules/db/queries" - q "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/grpc/sms" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redis" - uhelpers "fiskerinc.com/modules/usecase_helpers" - "fiskerinc.com/modules/validator" - vconfig "fiskerinc.com/modules/vehicleconfig" + "github.com/fiskerinc/cloud-services/pkg/carcommand" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/common/manifestfingerprintparams" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + q "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/grpc/sms" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redis" + uhelpers "github.com/fiskerinc/cloud-services/pkg/usecase_helpers" + "github.com/fiskerinc/cloud-services/pkg/validator" + vconfig "github.com/fiskerinc/cloud-services/pkg/vehicleconfig" errorspkg "github.com/pkg/errors" ) diff --git a/pkg/mongo/client.go b/pkg/mongo/client.go index 5156231..27f5ee4 100644 --- a/pkg/mongo/client.go +++ b/pkg/mongo/client.go @@ -5,8 +5,8 @@ import ( "sync" "time" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/pkg/errors" "go.mongodb.org/mongo-driver/mongo" diff --git a/pkg/mongo/client_test.go b/pkg/mongo/client_test.go index 7f2b8ba..478251d 100644 --- a/pkg/mongo/client_test.go +++ b/pkg/mongo/client_test.go @@ -3,8 +3,8 @@ package mongo_test import ( "testing" - "fiskerinc.com/modules/mongo" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/mongo" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestNewClient(t *testing.T) { diff --git a/pkg/mongo/collection.go b/pkg/mongo/collection.go index 67ca706..2b137d2 100644 --- a/pkg/mongo/collection.go +++ b/pkg/mongo/collection.go @@ -3,8 +3,8 @@ package mongo import ( "context" - "fiskerinc.com/modules/db/queries" - e "fiskerinc.com/modules/mongo/error" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + e "github.com/fiskerinc/cloud-services/pkg/mongo/error" "github.com/pkg/errors" "go.mongodb.org/mongo-driver/mongo" diff --git a/pkg/mongo/collection_test.go b/pkg/mongo/collection_test.go index 58e3ccc..fd77176 100644 --- a/pkg/mongo/collection_test.go +++ b/pkg/mongo/collection_test.go @@ -4,14 +4,14 @@ import ( "errors" "testing" - e "fiskerinc.com/modules/mongo/error" + e "github.com/fiskerinc/cloud-services/pkg/mongo/error" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/mongo" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/mongo" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "go.mongodb.org/mongo-driver/bson" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" ) func TestNewCollection(t *testing.T) { diff --git a/pkg/mongo/dtc_codes.go b/pkg/mongo/dtc_codes.go index db22e25..3470bca 100644 --- a/pkg/mongo/dtc_codes.go +++ b/pkg/mongo/dtc_codes.go @@ -7,8 +7,8 @@ import ( "sync" "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/pkg/errors" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" diff --git a/pkg/mongo/fleets.go b/pkg/mongo/fleets.go index 92fd764..da5a026 100644 --- a/pkg/mongo/fleets.go +++ b/pkg/mongo/fleets.go @@ -3,11 +3,11 @@ package mongo import ( "strings" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "go.mongodb.org/mongo-driver/bson/primitive" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" "github.com/pkg/errors" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" diff --git a/pkg/mongo/fleets_test.go b/pkg/mongo/fleets_test.go index 2a88f58..ba58033 100644 --- a/pkg/mongo/fleets_test.go +++ b/pkg/mongo/fleets_test.go @@ -3,11 +3,11 @@ package mongo_test import ( "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/mongo" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/mongo" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" ) func TestNewFleetsCollection(t *testing.T) { diff --git a/pkg/mongo/mock.go b/pkg/mongo/mock.go index 8f703d5..b8bdeb3 100644 --- a/pkg/mongo/mock.go +++ b/pkg/mongo/mock.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/pkg/errors" "go.mongodb.org/mongo-driver/mongo" diff --git a/pkg/mongo/vehicles.go b/pkg/mongo/vehicles.go index ba197ff..68c693f 100644 --- a/pkg/mongo/vehicles.go +++ b/pkg/mongo/vehicles.go @@ -3,10 +3,10 @@ package mongo import ( "strings" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" ) diff --git a/pkg/mongo/vehicles_test.go b/pkg/mongo/vehicles_test.go index 3925916..8806e46 100644 --- a/pkg/mongo/vehicles_test.go +++ b/pkg/mongo/vehicles_test.go @@ -3,11 +3,11 @@ package mongo_test import ( "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/mongo" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/mongo" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" ) func TestNewVehiclesCollection(t *testing.T) { diff --git a/pkg/ota_api/client/e_c_u/get_dtcs_vin_responses.go b/pkg/ota_api/client/e_c_u/get_dtcs_vin_responses.go index 910e066..419427b 100644 --- a/pkg/ota_api/client/e_c_u/get_dtcs_vin_responses.go +++ b/pkg/ota_api/client/e_c_u/get_dtcs_vin_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetDtcsVinReader is a Reader for the GetDtcsVin structure. diff --git a/pkg/ota_api/client/fisker_o_t_a_client_client.go b/pkg/ota_api/client/fisker_o_t_a_client_client.go index eca9dea..55bab55 100644 --- a/pkg/ota_api/client/fisker_o_t_a_client_client.go +++ b/pkg/ota_api/client/fisker_o_t_a_client_client.go @@ -10,8 +10,8 @@ import ( httptransport "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/client/e_c_u" - "fiskerinc.com/modules/ota_api/client/operations" + "github.com/fiskerinc/cloud-services/pkg/ota_api/client/e_c_u" + "github.com/fiskerinc/cloud-services/pkg/ota_api/client/operations" ) // Default fisker o t a client HTTP client. diff --git a/pkg/ota_api/client/operations/delete_apitoken_responses.go b/pkg/ota_api/client/operations/delete_apitoken_responses.go index 88ba40a..12bcf2c 100644 --- a/pkg/ota_api/client/operations/delete_apitoken_responses.go +++ b/pkg/ota_api/client/operations/delete_apitoken_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteApitokenReader is a Reader for the DeleteApitoken structure. diff --git a/pkg/ota_api/client/operations/delete_carupdate_responses.go b/pkg/ota_api/client/operations/delete_carupdate_responses.go index 0f56b70..f6ac9d5 100644 --- a/pkg/ota_api/client/operations/delete_carupdate_responses.go +++ b/pkg/ota_api/client/operations/delete_carupdate_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteCarupdateReader is a Reader for the DeleteCarupdate structure. diff --git a/pkg/ota_api/client/operations/delete_flashpack_version_parameters.go b/pkg/ota_api/client/operations/delete_flashpack_version_parameters.go index eb8ed1b..a8ed944 100644 --- a/pkg/ota_api/client/operations/delete_flashpack_version_parameters.go +++ b/pkg/ota_api/client/operations/delete_flashpack_version_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewDeleteFlashpackVersionParams creates a new DeleteFlashpackVersionParams object, diff --git a/pkg/ota_api/client/operations/delete_flashpack_version_responses.go b/pkg/ota_api/client/operations/delete_flashpack_version_responses.go index ff28df7..add2bf4 100644 --- a/pkg/ota_api/client/operations/delete_flashpack_version_responses.go +++ b/pkg/ota_api/client/operations/delete_flashpack_version_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteFlashpackVersionReader is a Reader for the DeleteFlashpackVersion structure. diff --git a/pkg/ota_api/client/operations/delete_fleet_name_filter_id_responses.go b/pkg/ota_api/client/operations/delete_fleet_name_filter_id_responses.go index 9b5de19..945c58a 100644 --- a/pkg/ota_api/client/operations/delete_fleet_name_filter_id_responses.go +++ b/pkg/ota_api/client/operations/delete_fleet_name_filter_id_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteFleetNameFilterIDReader is a Reader for the DeleteFleetNameFilterID structure. diff --git a/pkg/ota_api/client/operations/delete_fleet_name_responses.go b/pkg/ota_api/client/operations/delete_fleet_name_responses.go index 3e906b7..27a6602 100644 --- a/pkg/ota_api/client/operations/delete_fleet_name_responses.go +++ b/pkg/ota_api/client/operations/delete_fleet_name_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteFleetNameReader is a Reader for the DeleteFleetName structure. diff --git a/pkg/ota_api/client/operations/delete_issues_id_responses.go b/pkg/ota_api/client/operations/delete_issues_id_responses.go index 65eb170..2756c45 100644 --- a/pkg/ota_api/client/operations/delete_issues_id_responses.go +++ b/pkg/ota_api/client/operations/delete_issues_id_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteIssuesIDReader is a Reader for the DeleteIssuesID structure. diff --git a/pkg/ota_api/client/operations/delete_manifest_responses.go b/pkg/ota_api/client/operations/delete_manifest_responses.go index 2f3b7f0..38b7ade 100644 --- a/pkg/ota_api/client/operations/delete_manifest_responses.go +++ b/pkg/ota_api/client/operations/delete_manifest_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteManifestReader is a Reader for the DeleteManifest structure. diff --git a/pkg/ota_api/client/operations/delete_manifest_sums_version_responses.go b/pkg/ota_api/client/operations/delete_manifest_sums_version_responses.go index 926fa5d..280b66c 100644 --- a/pkg/ota_api/client/operations/delete_manifest_sums_version_responses.go +++ b/pkg/ota_api/client/operations/delete_manifest_sums_version_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteManifestSumsVersionReader is a Reader for the DeleteManifestSumsVersion structure. diff --git a/pkg/ota_api/client/operations/delete_manifest_sums_version_rxswins_rxswin_responses.go b/pkg/ota_api/client/operations/delete_manifest_sums_version_rxswins_rxswin_responses.go index ef20e6a..7ff8480 100644 --- a/pkg/ota_api/client/operations/delete_manifest_sums_version_rxswins_rxswin_responses.go +++ b/pkg/ota_api/client/operations/delete_manifest_sums_version_rxswins_rxswin_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteManifestSumsVersionRxswinsRxswinReader is a Reader for the DeleteManifestSumsVersionRxswinsRxswin structure. diff --git a/pkg/ota_api/client/operations/delete_subscription_responses.go b/pkg/ota_api/client/operations/delete_subscription_responses.go index 54156cb..14df393 100644 --- a/pkg/ota_api/client/operations/delete_subscription_responses.go +++ b/pkg/ota_api/client/operations/delete_subscription_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteSubscriptionReader is a Reader for the DeleteSubscription structure. diff --git a/pkg/ota_api/client/operations/delete_subscriptionconfig_responses.go b/pkg/ota_api/client/operations/delete_subscriptionconfig_responses.go index 26f739b..3220bcd 100644 --- a/pkg/ota_api/client/operations/delete_subscriptionconfig_responses.go +++ b/pkg/ota_api/client/operations/delete_subscriptionconfig_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteSubscriptionconfigReader is a Reader for the DeleteSubscriptionconfig structure. diff --git a/pkg/ota_api/client/operations/delete_subscriptionfeature_responses.go b/pkg/ota_api/client/operations/delete_subscriptionfeature_responses.go index caeb944..522668a 100644 --- a/pkg/ota_api/client/operations/delete_subscriptionfeature_responses.go +++ b/pkg/ota_api/client/operations/delete_subscriptionfeature_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteSubscriptionfeatureReader is a Reader for the DeleteSubscriptionfeature structure. diff --git a/pkg/ota_api/client/operations/delete_subscriptionpackage_responses.go b/pkg/ota_api/client/operations/delete_subscriptionpackage_responses.go index 8f98648..a0f190f 100644 --- a/pkg/ota_api/client/operations/delete_subscriptionpackage_responses.go +++ b/pkg/ota_api/client/operations/delete_subscriptionpackage_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteSubscriptionpackageReader is a Reader for the DeleteSubscriptionpackage structure. diff --git a/pkg/ota_api/client/operations/delete_supplier_email_responses.go b/pkg/ota_api/client/operations/delete_supplier_email_responses.go index 3861d9d..4508b1d 100644 --- a/pkg/ota_api/client/operations/delete_supplier_email_responses.go +++ b/pkg/ota_api/client/operations/delete_supplier_email_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteSupplierEmailReader is a Reader for the DeleteSupplierEmail structure. diff --git a/pkg/ota_api/client/operations/delete_vehicle_vin_filter_id_responses.go b/pkg/ota_api/client/operations/delete_vehicle_vin_filter_id_responses.go index 9f26a5e..00d8cc0 100644 --- a/pkg/ota_api/client/operations/delete_vehicle_vin_filter_id_responses.go +++ b/pkg/ota_api/client/operations/delete_vehicle_vin_filter_id_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteVehicleVinFilterIDReader is a Reader for the DeleteVehicleVinFilterID structure. diff --git a/pkg/ota_api/client/operations/delete_vehicle_vin_responses.go b/pkg/ota_api/client/operations/delete_vehicle_vin_responses.go index 4fcabbd..7e311b4 100644 --- a/pkg/ota_api/client/operations/delete_vehicle_vin_responses.go +++ b/pkg/ota_api/client/operations/delete_vehicle_vin_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteVehicleVinReader is a Reader for the DeleteVehicleVin structure. diff --git a/pkg/ota_api/client/operations/delete_vehiclecommand_immobilize_responses.go b/pkg/ota_api/client/operations/delete_vehiclecommand_immobilize_responses.go index 6a800f8..175b6ab 100644 --- a/pkg/ota_api/client/operations/delete_vehiclecommand_immobilize_responses.go +++ b/pkg/ota_api/client/operations/delete_vehiclecommand_immobilize_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // DeleteVehiclecommandImmobilizeReader is a Reader for the DeleteVehiclecommandImmobilize structure. diff --git a/pkg/ota_api/client/operations/get_apicalls_responses.go b/pkg/ota_api/client/operations/get_apicalls_responses.go index ae2c776..b8686ed 100644 --- a/pkg/ota_api/client/operations/get_apicalls_responses.go +++ b/pkg/ota_api/client/operations/get_apicalls_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetApicallsReader is a Reader for the GetApicalls structure. diff --git a/pkg/ota_api/client/operations/get_apitokens_responses.go b/pkg/ota_api/client/operations/get_apitokens_responses.go index e5c3130..8cd3476 100644 --- a/pkg/ota_api/client/operations/get_apitokens_responses.go +++ b/pkg/ota_api/client/operations/get_apitokens_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetApitokensReader is a Reader for the GetApitokens structure. diff --git a/pkg/ota_api/client/operations/get_can_signals_dbc_responses.go b/pkg/ota_api/client/operations/get_can_signals_dbc_responses.go index d2cdbeb..a185e32 100644 --- a/pkg/ota_api/client/operations/get_can_signals_dbc_responses.go +++ b/pkg/ota_api/client/operations/get_can_signals_dbc_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetCanSignalsDbcReader is a Reader for the GetCanSignalsDbc structure. diff --git a/pkg/ota_api/client/operations/get_can_signals_export_responses.go b/pkg/ota_api/client/operations/get_can_signals_export_responses.go index 9ff0004..4adde57 100644 --- a/pkg/ota_api/client/operations/get_can_signals_export_responses.go +++ b/pkg/ota_api/client/operations/get_can_signals_export_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetCanSignalsExportReader is a Reader for the GetCanSignalsExport structure. diff --git a/pkg/ota_api/client/operations/get_can_signals_list_responses.go b/pkg/ota_api/client/operations/get_can_signals_list_responses.go index 15dd881..8da2e2d 100644 --- a/pkg/ota_api/client/operations/get_can_signals_list_responses.go +++ b/pkg/ota_api/client/operations/get_can_signals_list_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetCanSignalsListReader is a Reader for the GetCanSignalsList structure. diff --git a/pkg/ota_api/client/operations/get_cansignals_vin_responses.go b/pkg/ota_api/client/operations/get_cansignals_vin_responses.go index 2a779ed..646c4c8 100644 --- a/pkg/ota_api/client/operations/get_cansignals_vin_responses.go +++ b/pkg/ota_api/client/operations/get_cansignals_vin_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetCansignalsVinReader is a Reader for the GetCansignalsVin structure. diff --git a/pkg/ota_api/client/operations/get_car_config_vin_responses.go b/pkg/ota_api/client/operations/get_car_config_vin_responses.go index 340bf64..58d5091 100644 --- a/pkg/ota_api/client/operations/get_car_config_vin_responses.go +++ b/pkg/ota_api/client/operations/get_car_config_vin_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetCarConfigVinReader is a Reader for the GetCarConfigVin structure. diff --git a/pkg/ota_api/client/operations/get_carslocations_responses.go b/pkg/ota_api/client/operations/get_carslocations_responses.go index 5856ba7..6354dc2 100644 --- a/pkg/ota_api/client/operations/get_carslocations_responses.go +++ b/pkg/ota_api/client/operations/get_carslocations_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetCarslocationsReader is a Reader for the GetCarslocations structure. diff --git a/pkg/ota_api/client/operations/get_carstate_responses.go b/pkg/ota_api/client/operations/get_carstate_responses.go index a897144..63ffc4a 100644 --- a/pkg/ota_api/client/operations/get_carstate_responses.go +++ b/pkg/ota_api/client/operations/get_carstate_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetCarstateReader is a Reader for the GetCarstate structure. diff --git a/pkg/ota_api/client/operations/get_carupdates_responses.go b/pkg/ota_api/client/operations/get_carupdates_responses.go index 830e25f..fd923bc 100644 --- a/pkg/ota_api/client/operations/get_carupdates_responses.go +++ b/pkg/ota_api/client/operations/get_carupdates_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetCarupdatesReader is a Reader for the GetCarupdates structure. diff --git a/pkg/ota_api/client/operations/get_carupdateslog_responses.go b/pkg/ota_api/client/operations/get_carupdateslog_responses.go index 545fb1e..c8afb66 100644 --- a/pkg/ota_api/client/operations/get_carupdateslog_responses.go +++ b/pkg/ota_api/client/operations/get_carupdateslog_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetCarupdateslogReader is a Reader for the GetCarupdateslog structure. diff --git a/pkg/ota_api/client/operations/get_carupdatesstatuses_responses.go b/pkg/ota_api/client/operations/get_carupdatesstatuses_responses.go index 5018c19..c1af722 100644 --- a/pkg/ota_api/client/operations/get_carupdatesstatuses_responses.go +++ b/pkg/ota_api/client/operations/get_carupdatesstatuses_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetCarupdatesstatusesReader is a Reader for the GetCarupdatesstatuses structure. diff --git a/pkg/ota_api/client/operations/get_dashboard_embedded_dashboards_responses.go b/pkg/ota_api/client/operations/get_dashboard_embedded_dashboards_responses.go index 60906da..7effe7f 100644 --- a/pkg/ota_api/client/operations/get_dashboard_embedded_dashboards_responses.go +++ b/pkg/ota_api/client/operations/get_dashboard_embedded_dashboards_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetDashboardEmbeddedDashboardsReader is a Reader for the GetDashboardEmbeddedDashboards structure. diff --git a/pkg/ota_api/client/operations/get_dashboard_guest_token_responses.go b/pkg/ota_api/client/operations/get_dashboard_guest_token_responses.go index fa695ae..6162252 100644 --- a/pkg/ota_api/client/operations/get_dashboard_guest_token_responses.go +++ b/pkg/ota_api/client/operations/get_dashboard_guest_token_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetDashboardGuestTokenReader is a Reader for the GetDashboardGuestToken structure. diff --git a/pkg/ota_api/client/operations/get_ditto_carstate_responses.go b/pkg/ota_api/client/operations/get_ditto_carstate_responses.go index 3c736f6..1652ebb 100644 --- a/pkg/ota_api/client/operations/get_ditto_carstate_responses.go +++ b/pkg/ota_api/client/operations/get_ditto_carstate_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetDittoCarstateReader is a Reader for the GetDittoCarstate structure. diff --git a/pkg/ota_api/client/operations/get_ecu_stats_responses.go b/pkg/ota_api/client/operations/get_ecu_stats_responses.go index f5c5233..0b602cf 100644 --- a/pkg/ota_api/client/operations/get_ecu_stats_responses.go +++ b/pkg/ota_api/client/operations/get_ecu_stats_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetEcuStatsReader is a Reader for the GetEcuStats structure. diff --git a/pkg/ota_api/client/operations/get_ecu_stats_vin_dbc_responses.go b/pkg/ota_api/client/operations/get_ecu_stats_vin_dbc_responses.go index a5410b6..5e5553f 100644 --- a/pkg/ota_api/client/operations/get_ecu_stats_vin_dbc_responses.go +++ b/pkg/ota_api/client/operations/get_ecu_stats_vin_dbc_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetEcuStatsVinDbcReader is a Reader for the GetEcuStatsVinDbc structure. diff --git a/pkg/ota_api/client/operations/get_flashpack_version_ecu_mappings_model_trim_year_flashpack_responses.go b/pkg/ota_api/client/operations/get_flashpack_version_ecu_mappings_model_trim_year_flashpack_responses.go index c714aea..8df2008 100644 --- a/pkg/ota_api/client/operations/get_flashpack_version_ecu_mappings_model_trim_year_flashpack_responses.go +++ b/pkg/ota_api/client/operations/get_flashpack_version_ecu_mappings_model_trim_year_flashpack_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetFlashpackVersionEcuMappingsModelTrimYearFlashpackReader is a Reader for the GetFlashpackVersionEcuMappingsModelTrimYearFlashpack structure. diff --git a/pkg/ota_api/client/operations/get_flashpack_version_info_vin_responses.go b/pkg/ota_api/client/operations/get_flashpack_version_info_vin_responses.go index 2035f10..512f0a3 100644 --- a/pkg/ota_api/client/operations/get_flashpack_version_info_vin_responses.go +++ b/pkg/ota_api/client/operations/get_flashpack_version_info_vin_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetFlashpackVersionInfoVinReader is a Reader for the GetFlashpackVersionInfoVin structure. diff --git a/pkg/ota_api/client/operations/get_flashpack_versions_model_trim_year_responses.go b/pkg/ota_api/client/operations/get_flashpack_versions_model_trim_year_responses.go index b764ec6..2e638d9 100644 --- a/pkg/ota_api/client/operations/get_flashpack_versions_model_trim_year_responses.go +++ b/pkg/ota_api/client/operations/get_flashpack_versions_model_trim_year_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetFlashpackVersionsModelTrimYearReader is a Reader for the GetFlashpackVersionsModelTrimYear structure. diff --git a/pkg/ota_api/client/operations/get_fleet_name_filters_responses.go b/pkg/ota_api/client/operations/get_fleet_name_filters_responses.go index 20c90db..098727b 100644 --- a/pkg/ota_api/client/operations/get_fleet_name_filters_responses.go +++ b/pkg/ota_api/client/operations/get_fleet_name_filters_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetFleetNameFiltersReader is a Reader for the GetFleetNameFilters structure. diff --git a/pkg/ota_api/client/operations/get_fleet_name_responses.go b/pkg/ota_api/client/operations/get_fleet_name_responses.go index 84d1d4d..1bd7435 100644 --- a/pkg/ota_api/client/operations/get_fleet_name_responses.go +++ b/pkg/ota_api/client/operations/get_fleet_name_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetFleetNameReader is a Reader for the GetFleetName structure. diff --git a/pkg/ota_api/client/operations/get_fleet_name_vehicles_responses.go b/pkg/ota_api/client/operations/get_fleet_name_vehicles_responses.go index 395e53d..08cefe8 100644 --- a/pkg/ota_api/client/operations/get_fleet_name_vehicles_responses.go +++ b/pkg/ota_api/client/operations/get_fleet_name_vehicles_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetFleetNameVehiclesReader is a Reader for the GetFleetNameVehicles structure. diff --git a/pkg/ota_api/client/operations/get_fleets_responses.go b/pkg/ota_api/client/operations/get_fleets_responses.go index 78000d3..7b4d27e 100644 --- a/pkg/ota_api/client/operations/get_fleets_responses.go +++ b/pkg/ota_api/client/operations/get_fleets_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetFleetsReader is a Reader for the GetFleets structure. diff --git a/pkg/ota_api/client/operations/get_issue_id_responses.go b/pkg/ota_api/client/operations/get_issue_id_responses.go index b3a47b2..dfe8039 100644 --- a/pkg/ota_api/client/operations/get_issue_id_responses.go +++ b/pkg/ota_api/client/operations/get_issue_id_responses.go @@ -16,7 +16,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetIssueIDReader is a Reader for the GetIssueID structure. diff --git a/pkg/ota_api/client/operations/get_issues_responses.go b/pkg/ota_api/client/operations/get_issues_responses.go index 4c182e2..9e73699 100644 --- a/pkg/ota_api/client/operations/get_issues_responses.go +++ b/pkg/ota_api/client/operations/get_issues_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetIssuesReader is a Reader for the GetIssues structure. diff --git a/pkg/ota_api/client/operations/get_manifest_responses.go b/pkg/ota_api/client/operations/get_manifest_responses.go index 6e2e061..f4f3cfb 100644 --- a/pkg/ota_api/client/operations/get_manifest_responses.go +++ b/pkg/ota_api/client/operations/get_manifest_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetManifestReader is a Reader for the GetManifest structure. diff --git a/pkg/ota_api/client/operations/get_manifest_sums_responses.go b/pkg/ota_api/client/operations/get_manifest_sums_responses.go index b27e662..c018213 100644 --- a/pkg/ota_api/client/operations/get_manifest_sums_responses.go +++ b/pkg/ota_api/client/operations/get_manifest_sums_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetManifestSumsReader is a Reader for the GetManifestSums structure. diff --git a/pkg/ota_api/client/operations/get_manifest_sums_version_rxswins_responses.go b/pkg/ota_api/client/operations/get_manifest_sums_version_rxswins_responses.go index 36e2355..2516fe8 100644 --- a/pkg/ota_api/client/operations/get_manifest_sums_version_rxswins_responses.go +++ b/pkg/ota_api/client/operations/get_manifest_sums_version_rxswins_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetManifestSumsVersionRxswinsReader is a Reader for the GetManifestSumsVersionRxswins structure. diff --git a/pkg/ota_api/client/operations/get_manifestmigrate_version_parameters.go b/pkg/ota_api/client/operations/get_manifestmigrate_version_parameters.go index bbab1cc..e2a356e 100644 --- a/pkg/ota_api/client/operations/get_manifestmigrate_version_parameters.go +++ b/pkg/ota_api/client/operations/get_manifestmigrate_version_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewGetManifestmigrateVersionParams creates a new GetManifestmigrateVersionParams object, diff --git a/pkg/ota_api/client/operations/get_manifestmigrate_version_responses.go b/pkg/ota_api/client/operations/get_manifestmigrate_version_responses.go index e7796f2..7d457dc 100644 --- a/pkg/ota_api/client/operations/get_manifestmigrate_version_responses.go +++ b/pkg/ota_api/client/operations/get_manifestmigrate_version_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetManifestmigrateVersionReader is a Reader for the GetManifestmigrateVersion structure. diff --git a/pkg/ota_api/client/operations/get_manifests_manifest_id_vehicles_responses.go b/pkg/ota_api/client/operations/get_manifests_manifest_id_vehicles_responses.go index 1956a53..543d70a 100644 --- a/pkg/ota_api/client/operations/get_manifests_manifest_id_vehicles_responses.go +++ b/pkg/ota_api/client/operations/get_manifests_manifest_id_vehicles_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetManifestsManifestIDVehiclesReader is a Reader for the GetManifestsManifestIDVehicles structure. diff --git a/pkg/ota_api/client/operations/get_manifests_responses.go b/pkg/ota_api/client/operations/get_manifests_responses.go index 7ac9702..8bfbaf8 100644 --- a/pkg/ota_api/client/operations/get_manifests_responses.go +++ b/pkg/ota_api/client/operations/get_manifests_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetManifestsReader is a Reader for the GetManifests structure. diff --git a/pkg/ota_api/client/operations/get_subscriptionconfigs_responses.go b/pkg/ota_api/client/operations/get_subscriptionconfigs_responses.go index d5165df..b0e3be4 100644 --- a/pkg/ota_api/client/operations/get_subscriptionconfigs_responses.go +++ b/pkg/ota_api/client/operations/get_subscriptionconfigs_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetSubscriptionconfigsReader is a Reader for the GetSubscriptionconfigs structure. diff --git a/pkg/ota_api/client/operations/get_subscriptionfeature_responses.go b/pkg/ota_api/client/operations/get_subscriptionfeature_responses.go index 902256f..c3dab11 100644 --- a/pkg/ota_api/client/operations/get_subscriptionfeature_responses.go +++ b/pkg/ota_api/client/operations/get_subscriptionfeature_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetSubscriptionfeatureReader is a Reader for the GetSubscriptionfeature structure. diff --git a/pkg/ota_api/client/operations/get_subscriptionfeatures_responses.go b/pkg/ota_api/client/operations/get_subscriptionfeatures_responses.go index ccc81f3..38a95ec 100644 --- a/pkg/ota_api/client/operations/get_subscriptionfeatures_responses.go +++ b/pkg/ota_api/client/operations/get_subscriptionfeatures_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetSubscriptionfeaturesReader is a Reader for the GetSubscriptionfeatures structure. diff --git a/pkg/ota_api/client/operations/get_subscriptionpackage_responses.go b/pkg/ota_api/client/operations/get_subscriptionpackage_responses.go index 132a9bc..1d00399 100644 --- a/pkg/ota_api/client/operations/get_subscriptionpackage_responses.go +++ b/pkg/ota_api/client/operations/get_subscriptionpackage_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetSubscriptionpackageReader is a Reader for the GetSubscriptionpackage structure. diff --git a/pkg/ota_api/client/operations/get_subscriptionpackages_responses.go b/pkg/ota_api/client/operations/get_subscriptionpackages_responses.go index 18e3b3e..88bbbf7 100644 --- a/pkg/ota_api/client/operations/get_subscriptionpackages_responses.go +++ b/pkg/ota_api/client/operations/get_subscriptionpackages_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetSubscriptionpackagesReader is a Reader for the GetSubscriptionpackages structure. diff --git a/pkg/ota_api/client/operations/get_suppliers_responses.go b/pkg/ota_api/client/operations/get_suppliers_responses.go index ff6c97a..e75b08e 100644 --- a/pkg/ota_api/client/operations/get_suppliers_responses.go +++ b/pkg/ota_api/client/operations/get_suppliers_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetSuppliersReader is a Reader for the GetSuppliers structure. diff --git a/pkg/ota_api/client/operations/get_vehicle_vin_filters_responses.go b/pkg/ota_api/client/operations/get_vehicle_vin_filters_responses.go index 064a737..331ced9 100644 --- a/pkg/ota_api/client/operations/get_vehicle_vin_filters_responses.go +++ b/pkg/ota_api/client/operations/get_vehicle_vin_filters_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetVehicleVinFiltersReader is a Reader for the GetVehicleVinFilters structure. diff --git a/pkg/ota_api/client/operations/get_vehicle_vin_fleets_responses.go b/pkg/ota_api/client/operations/get_vehicle_vin_fleets_responses.go index 88f49b3..24a3a5a 100644 --- a/pkg/ota_api/client/operations/get_vehicle_vin_fleets_responses.go +++ b/pkg/ota_api/client/operations/get_vehicle_vin_fleets_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetVehicleVinFleetsReader is a Reader for the GetVehicleVinFleets structure. diff --git a/pkg/ota_api/client/operations/get_vehicle_vin_responses.go b/pkg/ota_api/client/operations/get_vehicle_vin_responses.go index 20fe614..babf2eb 100644 --- a/pkg/ota_api/client/operations/get_vehicle_vin_responses.go +++ b/pkg/ota_api/client/operations/get_vehicle_vin_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetVehicleVinReader is a Reader for the GetVehicleVin structure. diff --git a/pkg/ota_api/client/operations/get_vehicle_vin_trex_logs_link_responses.go b/pkg/ota_api/client/operations/get_vehicle_vin_trex_logs_link_responses.go index b97cd12..f776ca3 100644 --- a/pkg/ota_api/client/operations/get_vehicle_vin_trex_logs_link_responses.go +++ b/pkg/ota_api/client/operations/get_vehicle_vin_trex_logs_link_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetVehicleVinTrexLogsLinkReader is a Reader for the GetVehicleVinTrexLogsLink structure. diff --git a/pkg/ota_api/client/operations/get_vehicle_vin_trex_logs_responses.go b/pkg/ota_api/client/operations/get_vehicle_vin_trex_logs_responses.go index 8bc0f22..83ceef9 100644 --- a/pkg/ota_api/client/operations/get_vehicle_vin_trex_logs_responses.go +++ b/pkg/ota_api/client/operations/get_vehicle_vin_trex_logs_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetVehicleVinTrexLogsReader is a Reader for the GetVehicleVinTrexLogs structure. diff --git a/pkg/ota_api/client/operations/get_vehicle_vin_version_logs_responses.go b/pkg/ota_api/client/operations/get_vehicle_vin_version_logs_responses.go index ed01108..c65a2a6 100644 --- a/pkg/ota_api/client/operations/get_vehicle_vin_version_logs_responses.go +++ b/pkg/ota_api/client/operations/get_vehicle_vin_version_logs_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetVehicleVinVersionLogsReader is a Reader for the GetVehicleVinVersionLogs structure. diff --git a/pkg/ota_api/client/operations/get_vehicle_vin_version_responses.go b/pkg/ota_api/client/operations/get_vehicle_vin_version_responses.go index dcd6f16..393aef0 100644 --- a/pkg/ota_api/client/operations/get_vehicle_vin_version_responses.go +++ b/pkg/ota_api/client/operations/get_vehicle_vin_version_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetVehicleVinVersionReader is a Reader for the GetVehicleVinVersion structure. diff --git a/pkg/ota_api/client/operations/get_vehiclecommand_immobilize_responses.go b/pkg/ota_api/client/operations/get_vehiclecommand_immobilize_responses.go index 436109d..7ffd5e4 100644 --- a/pkg/ota_api/client/operations/get_vehiclecommand_immobilize_responses.go +++ b/pkg/ota_api/client/operations/get_vehiclecommand_immobilize_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetVehiclecommandImmobilizeReader is a Reader for the GetVehiclecommandImmobilize structure. diff --git a/pkg/ota_api/client/operations/get_vehicleecus_responses.go b/pkg/ota_api/client/operations/get_vehicleecus_responses.go index 10d55f5..b0167dd 100644 --- a/pkg/ota_api/client/operations/get_vehicleecus_responses.go +++ b/pkg/ota_api/client/operations/get_vehicleecus_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetVehicleecusReader is a Reader for the GetVehicleecus structure. diff --git a/pkg/ota_api/client/operations/get_vehiclemodels_responses.go b/pkg/ota_api/client/operations/get_vehiclemodels_responses.go index 814a576..d102e5a 100644 --- a/pkg/ota_api/client/operations/get_vehiclemodels_responses.go +++ b/pkg/ota_api/client/operations/get_vehiclemodels_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetVehiclemodelsReader is a Reader for the GetVehiclemodels structure. diff --git a/pkg/ota_api/client/operations/get_vehicles_responses.go b/pkg/ota_api/client/operations/get_vehicles_responses.go index fc46d38..543787e 100644 --- a/pkg/ota_api/client/operations/get_vehicles_responses.go +++ b/pkg/ota_api/client/operations/get_vehicles_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetVehiclesReader is a Reader for the GetVehicles structure. diff --git a/pkg/ota_api/client/operations/get_vehicleyears_responses.go b/pkg/ota_api/client/operations/get_vehicleyears_responses.go index 4a11d5f..65cf688 100644 --- a/pkg/ota_api/client/operations/get_vehicleyears_responses.go +++ b/pkg/ota_api/client/operations/get_vehicleyears_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // GetVehicleyearsReader is a Reader for the GetVehicleyears structure. diff --git a/pkg/ota_api/client/operations/post_apitoken_parameters.go b/pkg/ota_api/client/operations/post_apitoken_parameters.go index a008ccb..03e3141 100644 --- a/pkg/ota_api/client/operations/post_apitoken_parameters.go +++ b/pkg/ota_api/client/operations/post_apitoken_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostApitokenParams creates a new PostApitokenParams object, diff --git a/pkg/ota_api/client/operations/post_apitoken_responses.go b/pkg/ota_api/client/operations/post_apitoken_responses.go index 44c522b..c26c295 100644 --- a/pkg/ota_api/client/operations/post_apitoken_responses.go +++ b/pkg/ota_api/client/operations/post_apitoken_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostApitokenReader is a Reader for the PostApitoken structure. diff --git a/pkg/ota_api/client/operations/post_car_config_vin_responses.go b/pkg/ota_api/client/operations/post_car_config_vin_responses.go index bbf41d0..d80fb9f 100644 --- a/pkg/ota_api/client/operations/post_car_config_vin_responses.go +++ b/pkg/ota_api/client/operations/post_car_config_vin_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostCarConfigVinReader is a Reader for the PostCarConfigVin structure. diff --git a/pkg/ota_api/client/operations/post_carsconnected_parameters.go b/pkg/ota_api/client/operations/post_carsconnected_parameters.go index f9ea09f..0ed3dd0 100644 --- a/pkg/ota_api/client/operations/post_carsconnected_parameters.go +++ b/pkg/ota_api/client/operations/post_carsconnected_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostCarsconnectedParams creates a new PostCarsconnectedParams object, diff --git a/pkg/ota_api/client/operations/post_carsconnected_responses.go b/pkg/ota_api/client/operations/post_carsconnected_responses.go index aa27c15..a5d0bfa 100644 --- a/pkg/ota_api/client/operations/post_carsconnected_responses.go +++ b/pkg/ota_api/client/operations/post_carsconnected_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostCarsconnectedReader is a Reader for the PostCarsconnected structure. diff --git a/pkg/ota_api/client/operations/post_carstate_multi_responses.go b/pkg/ota_api/client/operations/post_carstate_multi_responses.go index bb4a4be..3b9becf 100644 --- a/pkg/ota_api/client/operations/post_carstate_multi_responses.go +++ b/pkg/ota_api/client/operations/post_carstate_multi_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostCarstateMultiReader is a Reader for the PostCarstateMulti structure. diff --git a/pkg/ota_api/client/operations/post_carupdate_id_cancel_responses.go b/pkg/ota_api/client/operations/post_carupdate_id_cancel_responses.go index 730c953..d993c40 100644 --- a/pkg/ota_api/client/operations/post_carupdate_id_cancel_responses.go +++ b/pkg/ota_api/client/operations/post_carupdate_id_cancel_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostCarupdateIDCancelReader is a Reader for the PostCarupdateIDCancel structure. diff --git a/pkg/ota_api/client/operations/post_carupdate_id_deploy_responses.go b/pkg/ota_api/client/operations/post_carupdate_id_deploy_responses.go index d4d3987..b0546ed 100644 --- a/pkg/ota_api/client/operations/post_carupdate_id_deploy_responses.go +++ b/pkg/ota_api/client/operations/post_carupdate_id_deploy_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostCarupdateIDDeployReader is a Reader for the PostCarupdateIDDeploy structure. diff --git a/pkg/ota_api/client/operations/post_carupdate_id_vehicle_cancel_responses.go b/pkg/ota_api/client/operations/post_carupdate_id_vehicle_cancel_responses.go index 6d03fc9..d4d6d4d 100644 --- a/pkg/ota_api/client/operations/post_carupdate_id_vehicle_cancel_responses.go +++ b/pkg/ota_api/client/operations/post_carupdate_id_vehicle_cancel_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostCarupdateIDVehicleCancelReader is a Reader for the PostCarupdateIDVehicleCancel structure. diff --git a/pkg/ota_api/client/operations/post_carupdate_parameters.go b/pkg/ota_api/client/operations/post_carupdate_parameters.go index 980fa71..ab4fe94 100644 --- a/pkg/ota_api/client/operations/post_carupdate_parameters.go +++ b/pkg/ota_api/client/operations/post_carupdate_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostCarupdateParams creates a new PostCarupdateParams object, diff --git a/pkg/ota_api/client/operations/post_carupdate_responses.go b/pkg/ota_api/client/operations/post_carupdate_responses.go index a6ff97a..9245631 100644 --- a/pkg/ota_api/client/operations/post_carupdate_responses.go +++ b/pkg/ota_api/client/operations/post_carupdate_responses.go @@ -17,7 +17,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostCarupdateReader is a Reader for the PostCarupdate structure. diff --git a/pkg/ota_api/client/operations/post_flashpack_version_parameters.go b/pkg/ota_api/client/operations/post_flashpack_version_parameters.go index f3b6c70..1f89612 100644 --- a/pkg/ota_api/client/operations/post_flashpack_version_parameters.go +++ b/pkg/ota_api/client/operations/post_flashpack_version_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostFlashpackVersionParams creates a new PostFlashpackVersionParams object, diff --git a/pkg/ota_api/client/operations/post_flashpack_version_responses.go b/pkg/ota_api/client/operations/post_flashpack_version_responses.go index c316781..909e1c0 100644 --- a/pkg/ota_api/client/operations/post_flashpack_version_responses.go +++ b/pkg/ota_api/client/operations/post_flashpack_version_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostFlashpackVersionReader is a Reader for the PostFlashpackVersion structure. diff --git a/pkg/ota_api/client/operations/post_fleet_name_filter_parameters.go b/pkg/ota_api/client/operations/post_fleet_name_filter_parameters.go index b19c6bc..9b63ab2 100644 --- a/pkg/ota_api/client/operations/post_fleet_name_filter_parameters.go +++ b/pkg/ota_api/client/operations/post_fleet_name_filter_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostFleetNameFilterParams creates a new PostFleetNameFilterParams object, diff --git a/pkg/ota_api/client/operations/post_fleet_name_filter_responses.go b/pkg/ota_api/client/operations/post_fleet_name_filter_responses.go index 16a05d5..46a5847 100644 --- a/pkg/ota_api/client/operations/post_fleet_name_filter_responses.go +++ b/pkg/ota_api/client/operations/post_fleet_name_filter_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostFleetNameFilterReader is a Reader for the PostFleetNameFilter structure. diff --git a/pkg/ota_api/client/operations/post_fleet_name_vehicles_add_parameters.go b/pkg/ota_api/client/operations/post_fleet_name_vehicles_add_parameters.go index bae99c9..789b7c3 100644 --- a/pkg/ota_api/client/operations/post_fleet_name_vehicles_add_parameters.go +++ b/pkg/ota_api/client/operations/post_fleet_name_vehicles_add_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostFleetNameVehiclesAddParams creates a new PostFleetNameVehiclesAddParams object, diff --git a/pkg/ota_api/client/operations/post_fleet_name_vehicles_add_responses.go b/pkg/ota_api/client/operations/post_fleet_name_vehicles_add_responses.go index 6f81e24..77137bd 100644 --- a/pkg/ota_api/client/operations/post_fleet_name_vehicles_add_responses.go +++ b/pkg/ota_api/client/operations/post_fleet_name_vehicles_add_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostFleetNameVehiclesAddReader is a Reader for the PostFleetNameVehiclesAdd structure. diff --git a/pkg/ota_api/client/operations/post_fleet_name_vehicles_delete_parameters.go b/pkg/ota_api/client/operations/post_fleet_name_vehicles_delete_parameters.go index e925762..9db8299 100644 --- a/pkg/ota_api/client/operations/post_fleet_name_vehicles_delete_parameters.go +++ b/pkg/ota_api/client/operations/post_fleet_name_vehicles_delete_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostFleetNameVehiclesDeleteParams creates a new PostFleetNameVehiclesDeleteParams object, diff --git a/pkg/ota_api/client/operations/post_fleet_name_vehicles_delete_responses.go b/pkg/ota_api/client/operations/post_fleet_name_vehicles_delete_responses.go index c4e8903..3ee9db9 100644 --- a/pkg/ota_api/client/operations/post_fleet_name_vehicles_delete_responses.go +++ b/pkg/ota_api/client/operations/post_fleet_name_vehicles_delete_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostFleetNameVehiclesDeleteReader is a Reader for the PostFleetNameVehiclesDelete structure. diff --git a/pkg/ota_api/client/operations/post_fleet_parameters.go b/pkg/ota_api/client/operations/post_fleet_parameters.go index 30e002b..1b24ba9 100644 --- a/pkg/ota_api/client/operations/post_fleet_parameters.go +++ b/pkg/ota_api/client/operations/post_fleet_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostFleetParams creates a new PostFleetParams object, diff --git a/pkg/ota_api/client/operations/post_fleet_responses.go b/pkg/ota_api/client/operations/post_fleet_responses.go index 9425585..1e00c44 100644 --- a/pkg/ota_api/client/operations/post_fleet_responses.go +++ b/pkg/ota_api/client/operations/post_fleet_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostFleetReader is a Reader for the PostFleet structure. diff --git a/pkg/ota_api/client/operations/post_fleetupdate_parameters.go b/pkg/ota_api/client/operations/post_fleetupdate_parameters.go index 1f786dd..1e6a88b 100644 --- a/pkg/ota_api/client/operations/post_fleetupdate_parameters.go +++ b/pkg/ota_api/client/operations/post_fleetupdate_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostFleetupdateParams creates a new PostFleetupdateParams object, diff --git a/pkg/ota_api/client/operations/post_fleetupdate_responses.go b/pkg/ota_api/client/operations/post_fleetupdate_responses.go index 8fd8a55..b0906d6 100644 --- a/pkg/ota_api/client/operations/post_fleetupdate_responses.go +++ b/pkg/ota_api/client/operations/post_fleetupdate_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostFleetupdateReader is a Reader for the PostFleetupdate structure. diff --git a/pkg/ota_api/client/operations/post_manifest_parameters.go b/pkg/ota_api/client/operations/post_manifest_parameters.go index 333ff57..9ef3dd8 100644 --- a/pkg/ota_api/client/operations/post_manifest_parameters.go +++ b/pkg/ota_api/client/operations/post_manifest_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostManifestParams creates a new PostManifestParams object, diff --git a/pkg/ota_api/client/operations/post_manifest_responses.go b/pkg/ota_api/client/operations/post_manifest_responses.go index 667ee66..d8fa5ef 100644 --- a/pkg/ota_api/client/operations/post_manifest_responses.go +++ b/pkg/ota_api/client/operations/post_manifest_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostManifestReader is a Reader for the PostManifest structure. diff --git a/pkg/ota_api/client/operations/post_manifest_sums_parameters.go b/pkg/ota_api/client/operations/post_manifest_sums_parameters.go index f7382e8..146b40e 100644 --- a/pkg/ota_api/client/operations/post_manifest_sums_parameters.go +++ b/pkg/ota_api/client/operations/post_manifest_sums_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostManifestSumsParams creates a new PostManifestSumsParams object, diff --git a/pkg/ota_api/client/operations/post_manifest_sums_responses.go b/pkg/ota_api/client/operations/post_manifest_sums_responses.go index dc45343..5253ced 100644 --- a/pkg/ota_api/client/operations/post_manifest_sums_responses.go +++ b/pkg/ota_api/client/operations/post_manifest_sums_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostManifestSumsReader is a Reader for the PostManifestSums structure. diff --git a/pkg/ota_api/client/operations/post_manifest_sums_version_rxswins_parameters.go b/pkg/ota_api/client/operations/post_manifest_sums_version_rxswins_parameters.go index f0788c1..6b3da6e 100644 --- a/pkg/ota_api/client/operations/post_manifest_sums_version_rxswins_parameters.go +++ b/pkg/ota_api/client/operations/post_manifest_sums_version_rxswins_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostManifestSumsVersionRxswinsParams creates a new PostManifestSumsVersionRxswinsParams object, diff --git a/pkg/ota_api/client/operations/post_manifest_sums_version_rxswins_responses.go b/pkg/ota_api/client/operations/post_manifest_sums_version_rxswins_responses.go index 1db6509..80d12d8 100644 --- a/pkg/ota_api/client/operations/post_manifest_sums_version_rxswins_responses.go +++ b/pkg/ota_api/client/operations/post_manifest_sums_version_rxswins_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostManifestSumsVersionRxswinsReader is a Reader for the PostManifestSumsVersionRxswins structure. diff --git a/pkg/ota_api/client/operations/post_manifestecu_parameters.go b/pkg/ota_api/client/operations/post_manifestecu_parameters.go index bf7b0fe..e7fe1cb 100644 --- a/pkg/ota_api/client/operations/post_manifestecu_parameters.go +++ b/pkg/ota_api/client/operations/post_manifestecu_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostManifestecuParams creates a new PostManifestecuParams object, diff --git a/pkg/ota_api/client/operations/post_manifestecu_responses.go b/pkg/ota_api/client/operations/post_manifestecu_responses.go index 913f892..97915d9 100644 --- a/pkg/ota_api/client/operations/post_manifestecu_responses.go +++ b/pkg/ota_api/client/operations/post_manifestecu_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostManifestecuReader is a Reader for the PostManifestecu structure. diff --git a/pkg/ota_api/client/operations/post_manifestfile_responses.go b/pkg/ota_api/client/operations/post_manifestfile_responses.go index d2396b0..66c8baa 100644 --- a/pkg/ota_api/client/operations/post_manifestfile_responses.go +++ b/pkg/ota_api/client/operations/post_manifestfile_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostManifestfileReader is a Reader for the PostManifestfile structure. diff --git a/pkg/ota_api/client/operations/post_manifestmigrate_manifest_id_responses.go b/pkg/ota_api/client/operations/post_manifestmigrate_manifest_id_responses.go index fc32924..4961fd0 100644 --- a/pkg/ota_api/client/operations/post_manifestmigrate_manifest_id_responses.go +++ b/pkg/ota_api/client/operations/post_manifestmigrate_manifest_id_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostManifestmigrateManifestIDReader is a Reader for the PostManifestmigrateManifestID structure. diff --git a/pkg/ota_api/client/operations/post_manifestmigrate_parameters.go b/pkg/ota_api/client/operations/post_manifestmigrate_parameters.go index 1f998d6..5e10521 100644 --- a/pkg/ota_api/client/operations/post_manifestmigrate_parameters.go +++ b/pkg/ota_api/client/operations/post_manifestmigrate_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostManifestmigrateParams creates a new PostManifestmigrateParams object, diff --git a/pkg/ota_api/client/operations/post_manifestmigrate_responses.go b/pkg/ota_api/client/operations/post_manifestmigrate_responses.go index 1dc962b..e84beae 100644 --- a/pkg/ota_api/client/operations/post_manifestmigrate_responses.go +++ b/pkg/ota_api/client/operations/post_manifestmigrate_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostManifestmigrateReader is a Reader for the PostManifestmigrate structure. diff --git a/pkg/ota_api/client/operations/post_manufacture_certs_responses.go b/pkg/ota_api/client/operations/post_manufacture_certs_responses.go index 4ab53fa..844e5d4 100644 --- a/pkg/ota_api/client/operations/post_manufacture_certs_responses.go +++ b/pkg/ota_api/client/operations/post_manufacture_certs_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostManufactureCertsReader is a Reader for the PostManufactureCerts structure. diff --git a/pkg/ota_api/client/operations/post_sms_parameters.go b/pkg/ota_api/client/operations/post_sms_parameters.go index 12af32a..d5cae09 100644 --- a/pkg/ota_api/client/operations/post_sms_parameters.go +++ b/pkg/ota_api/client/operations/post_sms_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostSmsParams creates a new PostSmsParams object, diff --git a/pkg/ota_api/client/operations/post_sms_responses.go b/pkg/ota_api/client/operations/post_sms_responses.go index e5c0ac5..24ff339 100644 --- a/pkg/ota_api/client/operations/post_sms_responses.go +++ b/pkg/ota_api/client/operations/post_sms_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostSmsReader is a Reader for the PostSms structure. diff --git a/pkg/ota_api/client/operations/post_subscriptionconfig_parameters.go b/pkg/ota_api/client/operations/post_subscriptionconfig_parameters.go index b53ea0e..59fb3f0 100644 --- a/pkg/ota_api/client/operations/post_subscriptionconfig_parameters.go +++ b/pkg/ota_api/client/operations/post_subscriptionconfig_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostSubscriptionconfigParams creates a new PostSubscriptionconfigParams object, diff --git a/pkg/ota_api/client/operations/post_subscriptionconfig_responses.go b/pkg/ota_api/client/operations/post_subscriptionconfig_responses.go index daf5c08..ce2cb79 100644 --- a/pkg/ota_api/client/operations/post_subscriptionconfig_responses.go +++ b/pkg/ota_api/client/operations/post_subscriptionconfig_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostSubscriptionconfigReader is a Reader for the PostSubscriptionconfig structure. diff --git a/pkg/ota_api/client/operations/post_subscriptionfeature_parameters.go b/pkg/ota_api/client/operations/post_subscriptionfeature_parameters.go index 9d31a99..c74a07a 100644 --- a/pkg/ota_api/client/operations/post_subscriptionfeature_parameters.go +++ b/pkg/ota_api/client/operations/post_subscriptionfeature_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostSubscriptionfeatureParams creates a new PostSubscriptionfeatureParams object, diff --git a/pkg/ota_api/client/operations/post_subscriptionfeature_responses.go b/pkg/ota_api/client/operations/post_subscriptionfeature_responses.go index 37c2f25..80d9e49 100644 --- a/pkg/ota_api/client/operations/post_subscriptionfeature_responses.go +++ b/pkg/ota_api/client/operations/post_subscriptionfeature_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostSubscriptionfeatureReader is a Reader for the PostSubscriptionfeature structure. diff --git a/pkg/ota_api/client/operations/post_subscriptionpackage_parameters.go b/pkg/ota_api/client/operations/post_subscriptionpackage_parameters.go index 47fc90a..4f11749 100644 --- a/pkg/ota_api/client/operations/post_subscriptionpackage_parameters.go +++ b/pkg/ota_api/client/operations/post_subscriptionpackage_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostSubscriptionpackageParams creates a new PostSubscriptionpackageParams object, diff --git a/pkg/ota_api/client/operations/post_subscriptionpackage_responses.go b/pkg/ota_api/client/operations/post_subscriptionpackage_responses.go index 107ddbb..11fff4e 100644 --- a/pkg/ota_api/client/operations/post_subscriptionpackage_responses.go +++ b/pkg/ota_api/client/operations/post_subscriptionpackage_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostSubscriptionpackageReader is a Reader for the PostSubscriptionpackage structure. diff --git a/pkg/ota_api/client/operations/post_subscriptionpackagefeature_parameters.go b/pkg/ota_api/client/operations/post_subscriptionpackagefeature_parameters.go index 8cc19cf..4ac29f3 100644 --- a/pkg/ota_api/client/operations/post_subscriptionpackagefeature_parameters.go +++ b/pkg/ota_api/client/operations/post_subscriptionpackagefeature_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostSubscriptionpackagefeatureParams creates a new PostSubscriptionpackagefeatureParams object, diff --git a/pkg/ota_api/client/operations/post_subscriptionpackagefeature_responses.go b/pkg/ota_api/client/operations/post_subscriptionpackagefeature_responses.go index 514458d..7a07eb7 100644 --- a/pkg/ota_api/client/operations/post_subscriptionpackagefeature_responses.go +++ b/pkg/ota_api/client/operations/post_subscriptionpackagefeature_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostSubscriptionpackagefeatureReader is a Reader for the PostSubscriptionpackagefeature structure. diff --git a/pkg/ota_api/client/operations/post_supplier_activate_email_responses.go b/pkg/ota_api/client/operations/post_supplier_activate_email_responses.go index cc0757e..5336bbd 100644 --- a/pkg/ota_api/client/operations/post_supplier_activate_email_responses.go +++ b/pkg/ota_api/client/operations/post_supplier_activate_email_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostSupplierActivateEmailReader is a Reader for the PostSupplierActivateEmail structure. diff --git a/pkg/ota_api/client/operations/post_tags_parameters.go b/pkg/ota_api/client/operations/post_tags_parameters.go index 3f8bb04..931ffcc 100644 --- a/pkg/ota_api/client/operations/post_tags_parameters.go +++ b/pkg/ota_api/client/operations/post_tags_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostTagsParams creates a new PostTagsParams object, diff --git a/pkg/ota_api/client/operations/post_tags_responses.go b/pkg/ota_api/client/operations/post_tags_responses.go index 288ef30..6797fac 100644 --- a/pkg/ota_api/client/operations/post_tags_responses.go +++ b/pkg/ota_api/client/operations/post_tags_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostTagsReader is a Reader for the PostTags structure. diff --git a/pkg/ota_api/client/operations/post_vehicle_parameters.go b/pkg/ota_api/client/operations/post_vehicle_parameters.go index b76885b..fb4d874 100644 --- a/pkg/ota_api/client/operations/post_vehicle_parameters.go +++ b/pkg/ota_api/client/operations/post_vehicle_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostVehicleParams creates a new PostVehicleParams object, diff --git a/pkg/ota_api/client/operations/post_vehicle_paths_parameters.go b/pkg/ota_api/client/operations/post_vehicle_paths_parameters.go index cb35481..85ed711 100644 --- a/pkg/ota_api/client/operations/post_vehicle_paths_parameters.go +++ b/pkg/ota_api/client/operations/post_vehicle_paths_parameters.go @@ -16,7 +16,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostVehiclePathsParams creates a new PostVehiclePathsParams object, diff --git a/pkg/ota_api/client/operations/post_vehicle_paths_responses.go b/pkg/ota_api/client/operations/post_vehicle_paths_responses.go index 271e1fc..401137b 100644 --- a/pkg/ota_api/client/operations/post_vehicle_paths_responses.go +++ b/pkg/ota_api/client/operations/post_vehicle_paths_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostVehiclePathsReader is a Reader for the PostVehiclePaths structure. diff --git a/pkg/ota_api/client/operations/post_vehicle_responses.go b/pkg/ota_api/client/operations/post_vehicle_responses.go index e8e7346..beb46e6 100644 --- a/pkg/ota_api/client/operations/post_vehicle_responses.go +++ b/pkg/ota_api/client/operations/post_vehicle_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostVehicleReader is a Reader for the PostVehicle structure. diff --git a/pkg/ota_api/client/operations/post_vehicle_vin_filter_parameters.go b/pkg/ota_api/client/operations/post_vehicle_vin_filter_parameters.go index ddd1e3d..b907ab7 100644 --- a/pkg/ota_api/client/operations/post_vehicle_vin_filter_parameters.go +++ b/pkg/ota_api/client/operations/post_vehicle_vin_filter_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostVehicleVinFilterParams creates a new PostVehicleVinFilterParams object, diff --git a/pkg/ota_api/client/operations/post_vehicle_vin_filter_responses.go b/pkg/ota_api/client/operations/post_vehicle_vin_filter_responses.go index 8b3869b..dac2845 100644 --- a/pkg/ota_api/client/operations/post_vehicle_vin_filter_responses.go +++ b/pkg/ota_api/client/operations/post_vehicle_vin_filter_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostVehicleVinFilterReader is a Reader for the PostVehicleVinFilter structure. diff --git a/pkg/ota_api/client/operations/post_vehiclecommand_immobilize_parameters.go b/pkg/ota_api/client/operations/post_vehiclecommand_immobilize_parameters.go index 13a9b4f..ba6c8be 100644 --- a/pkg/ota_api/client/operations/post_vehiclecommand_immobilize_parameters.go +++ b/pkg/ota_api/client/operations/post_vehiclecommand_immobilize_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostVehiclecommandImmobilizeParams creates a new PostVehiclecommandImmobilizeParams object, diff --git a/pkg/ota_api/client/operations/post_vehiclecommand_immobilize_responses.go b/pkg/ota_api/client/operations/post_vehiclecommand_immobilize_responses.go index ed60fb5..85a3149 100644 --- a/pkg/ota_api/client/operations/post_vehiclecommand_immobilize_responses.go +++ b/pkg/ota_api/client/operations/post_vehiclecommand_immobilize_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostVehiclecommandImmobilizeReader is a Reader for the PostVehiclecommandImmobilize structure. diff --git a/pkg/ota_api/client/operations/post_vehiclecommand_parameters.go b/pkg/ota_api/client/operations/post_vehiclecommand_parameters.go index 6689a9c..0322c1b 100644 --- a/pkg/ota_api/client/operations/post_vehiclecommand_parameters.go +++ b/pkg/ota_api/client/operations/post_vehiclecommand_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostVehiclecommandParams creates a new PostVehiclecommandParams object, diff --git a/pkg/ota_api/client/operations/post_vehiclecommand_responses.go b/pkg/ota_api/client/operations/post_vehiclecommand_responses.go index a53fb76..363c94d 100644 --- a/pkg/ota_api/client/operations/post_vehiclecommand_responses.go +++ b/pkg/ota_api/client/operations/post_vehiclecommand_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostVehiclecommandReader is a Reader for the PostVehiclecommand structure. diff --git a/pkg/ota_api/client/operations/post_vehiclediagnosticcommand_parameters.go b/pkg/ota_api/client/operations/post_vehiclediagnosticcommand_parameters.go index 3411f70..e35978b 100644 --- a/pkg/ota_api/client/operations/post_vehiclediagnosticcommand_parameters.go +++ b/pkg/ota_api/client/operations/post_vehiclediagnosticcommand_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPostVehiclediagnosticcommandParams creates a new PostVehiclediagnosticcommandParams object, diff --git a/pkg/ota_api/client/operations/post_vehiclediagnosticcommand_responses.go b/pkg/ota_api/client/operations/post_vehiclediagnosticcommand_responses.go index 298bb03..db26761 100644 --- a/pkg/ota_api/client/operations/post_vehiclediagnosticcommand_responses.go +++ b/pkg/ota_api/client/operations/post_vehiclediagnosticcommand_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PostVehiclediagnosticcommandReader is a Reader for the PostVehiclediagnosticcommand structure. diff --git a/pkg/ota_api/client/operations/put_apitoken_parameters.go b/pkg/ota_api/client/operations/put_apitoken_parameters.go index 2ffd860..0071698 100644 --- a/pkg/ota_api/client/operations/put_apitoken_parameters.go +++ b/pkg/ota_api/client/operations/put_apitoken_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPutApitokenParams creates a new PutApitokenParams object, diff --git a/pkg/ota_api/client/operations/put_apitoken_responses.go b/pkg/ota_api/client/operations/put_apitoken_responses.go index 81f030f..7e6ff49 100644 --- a/pkg/ota_api/client/operations/put_apitoken_responses.go +++ b/pkg/ota_api/client/operations/put_apitoken_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PutApitokenReader is a Reader for the PutApitoken structure. diff --git a/pkg/ota_api/client/operations/put_customer_ota_emails_parameters.go b/pkg/ota_api/client/operations/put_customer_ota_emails_parameters.go index e0c6550..8db7365 100644 --- a/pkg/ota_api/client/operations/put_customer_ota_emails_parameters.go +++ b/pkg/ota_api/client/operations/put_customer_ota_emails_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPutCustomerOtaEmailsParams creates a new PutCustomerOtaEmailsParams object, diff --git a/pkg/ota_api/client/operations/put_customer_ota_emails_responses.go b/pkg/ota_api/client/operations/put_customer_ota_emails_responses.go index 7a9d356..6ef96dd 100644 --- a/pkg/ota_api/client/operations/put_customer_ota_emails_responses.go +++ b/pkg/ota_api/client/operations/put_customer_ota_emails_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PutCustomerOtaEmailsReader is a Reader for the PutCustomerOtaEmails structure. diff --git a/pkg/ota_api/client/operations/put_fleet_name_filter_id_parameters.go b/pkg/ota_api/client/operations/put_fleet_name_filter_id_parameters.go index 389acba..66f40ca 100644 --- a/pkg/ota_api/client/operations/put_fleet_name_filter_id_parameters.go +++ b/pkg/ota_api/client/operations/put_fleet_name_filter_id_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPutFleetNameFilterIDParams creates a new PutFleetNameFilterIDParams object, diff --git a/pkg/ota_api/client/operations/put_fleet_name_filter_id_responses.go b/pkg/ota_api/client/operations/put_fleet_name_filter_id_responses.go index bd6841b..b7182f8 100644 --- a/pkg/ota_api/client/operations/put_fleet_name_filter_id_responses.go +++ b/pkg/ota_api/client/operations/put_fleet_name_filter_id_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PutFleetNameFilterIDReader is a Reader for the PutFleetNameFilterID structure. diff --git a/pkg/ota_api/client/operations/put_fleet_name_parameters.go b/pkg/ota_api/client/operations/put_fleet_name_parameters.go index a4733e7..fa10d7d 100644 --- a/pkg/ota_api/client/operations/put_fleet_name_parameters.go +++ b/pkg/ota_api/client/operations/put_fleet_name_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPutFleetNameParams creates a new PutFleetNameParams object, diff --git a/pkg/ota_api/client/operations/put_fleet_name_responses.go b/pkg/ota_api/client/operations/put_fleet_name_responses.go index 0249686..c3ab486 100644 --- a/pkg/ota_api/client/operations/put_fleet_name_responses.go +++ b/pkg/ota_api/client/operations/put_fleet_name_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PutFleetNameReader is a Reader for the PutFleetName structure. diff --git a/pkg/ota_api/client/operations/put_manifest_parameters.go b/pkg/ota_api/client/operations/put_manifest_parameters.go index 43f2bec..6432140 100644 --- a/pkg/ota_api/client/operations/put_manifest_parameters.go +++ b/pkg/ota_api/client/operations/put_manifest_parameters.go @@ -16,7 +16,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPutManifestParams creates a new PutManifestParams object, diff --git a/pkg/ota_api/client/operations/put_manifest_responses.go b/pkg/ota_api/client/operations/put_manifest_responses.go index 1bdc65b..0617d79 100644 --- a/pkg/ota_api/client/operations/put_manifest_responses.go +++ b/pkg/ota_api/client/operations/put_manifest_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PutManifestReader is a Reader for the PutManifest structure. diff --git a/pkg/ota_api/client/operations/put_manifests_archive_parameters.go b/pkg/ota_api/client/operations/put_manifests_archive_parameters.go index d6c80db..9f83f9c 100644 --- a/pkg/ota_api/client/operations/put_manifests_archive_parameters.go +++ b/pkg/ota_api/client/operations/put_manifests_archive_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPutManifestsArchiveParams creates a new PutManifestsArchiveParams object, diff --git a/pkg/ota_api/client/operations/put_manifests_archive_responses.go b/pkg/ota_api/client/operations/put_manifests_archive_responses.go index 02da8ca..2cd011b 100644 --- a/pkg/ota_api/client/operations/put_manifests_archive_responses.go +++ b/pkg/ota_api/client/operations/put_manifests_archive_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PutManifestsArchiveReader is a Reader for the PutManifestsArchive structure. diff --git a/pkg/ota_api/client/operations/put_manifests_id_sums_parameters.go b/pkg/ota_api/client/operations/put_manifests_id_sums_parameters.go index 4db0ab3..2070e66 100644 --- a/pkg/ota_api/client/operations/put_manifests_id_sums_parameters.go +++ b/pkg/ota_api/client/operations/put_manifests_id_sums_parameters.go @@ -16,7 +16,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPutManifestsIDSumsParams creates a new PutManifestsIDSumsParams object, diff --git a/pkg/ota_api/client/operations/put_manifests_id_sums_responses.go b/pkg/ota_api/client/operations/put_manifests_id_sums_responses.go index db03f5d..8b07d5c 100644 --- a/pkg/ota_api/client/operations/put_manifests_id_sums_responses.go +++ b/pkg/ota_api/client/operations/put_manifests_id_sums_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PutManifestsIDSumsReader is a Reader for the PutManifestsIDSums structure. diff --git a/pkg/ota_api/client/operations/put_subscriptionconfig_parameters.go b/pkg/ota_api/client/operations/put_subscriptionconfig_parameters.go index da55cfb..2bcb7c6 100644 --- a/pkg/ota_api/client/operations/put_subscriptionconfig_parameters.go +++ b/pkg/ota_api/client/operations/put_subscriptionconfig_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPutSubscriptionconfigParams creates a new PutSubscriptionconfigParams object, diff --git a/pkg/ota_api/client/operations/put_subscriptionconfig_responses.go b/pkg/ota_api/client/operations/put_subscriptionconfig_responses.go index ae8af63..ac93257 100644 --- a/pkg/ota_api/client/operations/put_subscriptionconfig_responses.go +++ b/pkg/ota_api/client/operations/put_subscriptionconfig_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PutSubscriptionconfigReader is a Reader for the PutSubscriptionconfig structure. diff --git a/pkg/ota_api/client/operations/put_subscriptionfeature_parameters.go b/pkg/ota_api/client/operations/put_subscriptionfeature_parameters.go index 73a5834..aeb60ef 100644 --- a/pkg/ota_api/client/operations/put_subscriptionfeature_parameters.go +++ b/pkg/ota_api/client/operations/put_subscriptionfeature_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPutSubscriptionfeatureParams creates a new PutSubscriptionfeatureParams object, diff --git a/pkg/ota_api/client/operations/put_subscriptionfeature_responses.go b/pkg/ota_api/client/operations/put_subscriptionfeature_responses.go index 18a9ef3..e773500 100644 --- a/pkg/ota_api/client/operations/put_subscriptionfeature_responses.go +++ b/pkg/ota_api/client/operations/put_subscriptionfeature_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PutSubscriptionfeatureReader is a Reader for the PutSubscriptionfeature structure. diff --git a/pkg/ota_api/client/operations/put_subscriptionpackage_parameters.go b/pkg/ota_api/client/operations/put_subscriptionpackage_parameters.go index f889130..1d9a898 100644 --- a/pkg/ota_api/client/operations/put_subscriptionpackage_parameters.go +++ b/pkg/ota_api/client/operations/put_subscriptionpackage_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPutSubscriptionpackageParams creates a new PutSubscriptionpackageParams object, diff --git a/pkg/ota_api/client/operations/put_subscriptionpackage_responses.go b/pkg/ota_api/client/operations/put_subscriptionpackage_responses.go index f201def..1da61ba 100644 --- a/pkg/ota_api/client/operations/put_subscriptionpackage_responses.go +++ b/pkg/ota_api/client/operations/put_subscriptionpackage_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PutSubscriptionpackageReader is a Reader for the PutSubscriptionpackage structure. diff --git a/pkg/ota_api/client/operations/put_supplier_email_parameters.go b/pkg/ota_api/client/operations/put_supplier_email_parameters.go index 6a95f31..2b2dd0c 100644 --- a/pkg/ota_api/client/operations/put_supplier_email_parameters.go +++ b/pkg/ota_api/client/operations/put_supplier_email_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPutSupplierEmailParams creates a new PutSupplierEmailParams object, diff --git a/pkg/ota_api/client/operations/put_supplier_email_responses.go b/pkg/ota_api/client/operations/put_supplier_email_responses.go index f6329c3..ec3944d 100644 --- a/pkg/ota_api/client/operations/put_supplier_email_responses.go +++ b/pkg/ota_api/client/operations/put_supplier_email_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PutSupplierEmailReader is a Reader for the PutSupplierEmail structure. diff --git a/pkg/ota_api/client/operations/put_tags_parameters.go b/pkg/ota_api/client/operations/put_tags_parameters.go index 0c76e1e..417c543 100644 --- a/pkg/ota_api/client/operations/put_tags_parameters.go +++ b/pkg/ota_api/client/operations/put_tags_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPutTagsParams creates a new PutTagsParams object, diff --git a/pkg/ota_api/client/operations/put_tags_responses.go b/pkg/ota_api/client/operations/put_tags_responses.go index 2445a8b..fc98645 100644 --- a/pkg/ota_api/client/operations/put_tags_responses.go +++ b/pkg/ota_api/client/operations/put_tags_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PutTagsReader is a Reader for the PutTags structure. diff --git a/pkg/ota_api/client/operations/put_vehicle_vin_filter_id_parameters.go b/pkg/ota_api/client/operations/put_vehicle_vin_filter_id_parameters.go index a4baacd..ad1297e 100644 --- a/pkg/ota_api/client/operations/put_vehicle_vin_filter_id_parameters.go +++ b/pkg/ota_api/client/operations/put_vehicle_vin_filter_id_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPutVehicleVinFilterIDParams creates a new PutVehicleVinFilterIDParams object, diff --git a/pkg/ota_api/client/operations/put_vehicle_vin_filter_id_responses.go b/pkg/ota_api/client/operations/put_vehicle_vin_filter_id_responses.go index 82bd6da..e7f3e99 100644 --- a/pkg/ota_api/client/operations/put_vehicle_vin_filter_id_responses.go +++ b/pkg/ota_api/client/operations/put_vehicle_vin_filter_id_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PutVehicleVinFilterIDReader is a Reader for the PutVehicleVinFilterID structure. diff --git a/pkg/ota_api/client/operations/put_vehicle_vin_parameters.go b/pkg/ota_api/client/operations/put_vehicle_vin_parameters.go index 50eee1b..496af4c 100644 --- a/pkg/ota_api/client/operations/put_vehicle_vin_parameters.go +++ b/pkg/ota_api/client/operations/put_vehicle_vin_parameters.go @@ -15,7 +15,7 @@ import ( cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // NewPutVehicleVinParams creates a new PutVehicleVinParams object, diff --git a/pkg/ota_api/client/operations/put_vehicle_vin_responses.go b/pkg/ota_api/client/operations/put_vehicle_vin_responses.go index 17a6373..c72d0eb 100644 --- a/pkg/ota_api/client/operations/put_vehicle_vin_responses.go +++ b/pkg/ota_api/client/operations/put_vehicle_vin_responses.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" - "fiskerinc.com/modules/ota_api/models" + "github.com/fiskerinc/cloud-services/pkg/ota_api/models" ) // PutVehicleVinReader is a Reader for the PutVehicleVin structure. diff --git a/pkg/redis/conn.go b/pkg/redis/conn.go index c7015a8..062a9c5 100644 --- a/pkg/redis/conn.go +++ b/pkg/redis/conn.go @@ -6,7 +6,7 @@ import ( "strings" "sync" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/gomodule/redigo/redis" "github.com/pkg/errors" ) diff --git a/pkg/redis/conn_test.go b/pkg/redis/conn_test.go index ad9f5e0..88648ee 100644 --- a/pkg/redis/conn_test.go +++ b/pkg/redis/conn_test.go @@ -3,8 +3,8 @@ package redis import ( "testing" - m "fiskerinc.com/modules/common" - "fiskerinc.com/modules/testhelper" + m "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/gomodule/redigo/redis" ) diff --git a/pkg/redis/keys.go b/pkg/redis/keys.go index fd765fe..9d8edc5 100644 --- a/pkg/redis/keys.go +++ b/pkg/redis/keys.go @@ -5,7 +5,7 @@ import ( "runtime/debug" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/google/uuid" ) diff --git a/pkg/redis/mock.go b/pkg/redis/mock.go index 71624a6..0638a2a 100644 --- a/pkg/redis/mock.go +++ b/pkg/redis/mock.go @@ -5,7 +5,7 @@ import ( "strconv" "time" - mock_redis "fiskerinc.com/modules/redis/mock" + mock_redis "github.com/fiskerinc/cloud-services/pkg/redis/mock" "github.com/golang/mock/gomock" "github.com/gomodule/redigo/redis" diff --git a/pkg/redis/pool.go b/pkg/redis/pool.go index 6962758..1c81c46 100644 --- a/pkg/redis/pool.go +++ b/pkg/redis/pool.go @@ -4,8 +4,8 @@ import ( "fmt" "time" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/gomodule/redigo/redis" "github.com/pkg/errors" diff --git a/pkg/redis/pool_test.go b/pkg/redis/pool_test.go index 6ac3fb6..c1d6c67 100644 --- a/pkg/redis/pool_test.go +++ b/pkg/redis/pool_test.go @@ -3,7 +3,7 @@ package redis import ( "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestRedisGetPool(t *testing.T) { diff --git a/pkg/redis/pubsub.go b/pkg/redis/pubsub.go index 2d955fd..c5e323a 100644 --- a/pkg/redis/pubsub.go +++ b/pkg/redis/pubsub.go @@ -4,7 +4,7 @@ import ( "context" "sync" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/gomodule/redigo/redis" "github.com/pkg/errors" ) diff --git a/pkg/redis/pubsub_test.go b/pkg/redis/pubsub_test.go index 5489ffa..624d2d1 100644 --- a/pkg/redis/pubsub_test.go +++ b/pkg/redis/pubsub_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestInitPubsub(t *testing.T) { diff --git a/pkg/redis/queues.go b/pkg/redis/queues.go index a665bc7..e580c09 100644 --- a/pkg/redis/queues.go +++ b/pkg/redis/queues.go @@ -9,8 +9,8 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/scheduler" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/scheduler" ) const ( diff --git a/pkg/redis/queues_test.go b/pkg/redis/queues_test.go index e4b33eb..7d3b966 100644 --- a/pkg/redis/queues_test.go +++ b/pkg/redis/queues_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/pkg/errors" ) diff --git a/pkg/redis/redisutils/cache_set.go b/pkg/redis/redisutils/cache_set.go index 4cabd4f..6337461 100644 --- a/pkg/redis/redisutils/cache_set.go +++ b/pkg/redis/redisutils/cache_set.go @@ -3,7 +3,7 @@ package redisutils import ( "time" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/redis" "github.com/pkg/errors" ) diff --git a/pkg/redis/redisutils/cache_set_mock.go b/pkg/redis/redisutils/cache_set_mock.go index b211df2..f2ee58b 100644 --- a/pkg/redis/redisutils/cache_set_mock.go +++ b/pkg/redis/redisutils/cache_set_mock.go @@ -1,7 +1,7 @@ package redisutils import ( - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/redis" "time" ) diff --git a/pkg/redis/redisutils/cache_set_test.go b/pkg/redis/redisutils/cache_set_test.go index 2710907..11fbf91 100644 --- a/pkg/redis/redisutils/cache_set_test.go +++ b/pkg/redis/redisutils/cache_set_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - "fiskerinc.com/modules/redis/redisutils" - "fiskerinc.com/modules/redis/tester" + "github.com/fiskerinc/cloud-services/pkg/redis/redisutils" + "github.com/fiskerinc/cloud-services/pkg/redis/tester" "github.com/stretchr/testify/assert" ) diff --git a/pkg/redis/redisutils/check_set.go b/pkg/redis/redisutils/check_set.go index 6bd8c38..111cc15 100644 --- a/pkg/redis/redisutils/check_set.go +++ b/pkg/redis/redisutils/check_set.go @@ -1,7 +1,7 @@ package redisutils import ( - re "fiskerinc.com/modules/redis" + re "github.com/fiskerinc/cloud-services/pkg/redis" "github.com/gomodule/redigo/redis" "github.com/pkg/errors" ) diff --git a/pkg/redis/set_test.go b/pkg/redis/set_test.go index 20eb524..8ad0823 100644 --- a/pkg/redis/set_test.go +++ b/pkg/redis/set_test.go @@ -3,7 +3,7 @@ package redis import ( "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestInitSet(t *testing.T) { diff --git a/pkg/redis/tester/mock_client.go b/pkg/redis/tester/mock_client.go index acddad4..85664b0 100644 --- a/pkg/redis/tester/mock_client.go +++ b/pkg/redis/tester/mock_client.go @@ -7,8 +7,8 @@ import ( "strings" "sync" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/redis" "github.com/pkg/errors" ) diff --git a/pkg/redis/tester/mock_client_pool.go b/pkg/redis/tester/mock_client_pool.go index f80ed17..fa19263 100644 --- a/pkg/redis/tester/mock_client_pool.go +++ b/pkg/redis/tester/mock_client_pool.go @@ -3,7 +3,7 @@ package tester import ( "sync" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/redis" ) func NewMockClientPool(args ...interface{}) redis.ClientPoolInterface { diff --git a/pkg/redis/tester/mock_client_test.go b/pkg/redis/tester/mock_client_test.go index de66be4..8ac5451 100644 --- a/pkg/redis/tester/mock_client_test.go +++ b/pkg/redis/tester/mock_client_test.go @@ -3,8 +3,8 @@ package tester import ( "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" "github.com/stretchr/testify/assert" ) diff --git a/pkg/redis/tester/mock_vehicles_cache.go b/pkg/redis/tester/mock_vehicles_cache.go index 5bbebe2..913ae93 100644 --- a/pkg/redis/tester/mock_vehicles_cache.go +++ b/pkg/redis/tester/mock_vehicles_cache.go @@ -1,6 +1,6 @@ package tester -import "fiskerinc.com/modules/cache" +import "github.com/fiskerinc/cloud-services/pkg/cache" type MockVehiclesCache struct{} diff --git a/pkg/redis/tester/redis_test_case.go b/pkg/redis/tester/redis_test_case.go index 86aad5c..8f42db4 100644 --- a/pkg/redis/tester/redis_test_case.go +++ b/pkg/redis/tester/redis_test_case.go @@ -5,8 +5,8 @@ import ( "regexp" "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) type ExpiringCacheResult struct { diff --git a/pkg/redisv2/conn.go b/pkg/redisv2/conn.go index e5db697..2c063ab 100644 --- a/pkg/redisv2/conn.go +++ b/pkg/redisv2/conn.go @@ -6,7 +6,7 @@ import ( "sync" "time" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/pkg/errors" "github.com/redis/go-redis/v9" ) diff --git a/pkg/redisv2/connection.go b/pkg/redisv2/connection.go index 0d43f59..37e991a 100644 --- a/pkg/redisv2/connection.go +++ b/pkg/redisv2/connection.go @@ -3,7 +3,7 @@ package redisv2 import ( "fmt" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/redis/go-redis/v9" ) diff --git a/pkg/redisv2/keys.go b/pkg/redisv2/keys.go index 3c71186..b60b051 100644 --- a/pkg/redisv2/keys.go +++ b/pkg/redisv2/keys.go @@ -5,7 +5,7 @@ import ( "runtime/debug" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/google/uuid" ) diff --git a/pkg/redisv2/tester/mock_client.go b/pkg/redisv2/tester/mock_client.go index acddad4..85664b0 100644 --- a/pkg/redisv2/tester/mock_client.go +++ b/pkg/redisv2/tester/mock_client.go @@ -7,8 +7,8 @@ import ( "strings" "sync" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/redis" "github.com/pkg/errors" ) diff --git a/pkg/redisv2/tester/mock_client_pool.go b/pkg/redisv2/tester/mock_client_pool.go index f80ed17..fa19263 100644 --- a/pkg/redisv2/tester/mock_client_pool.go +++ b/pkg/redisv2/tester/mock_client_pool.go @@ -3,7 +3,7 @@ package tester import ( "sync" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/redis" ) func NewMockClientPool(args ...interface{}) redis.ClientPoolInterface { diff --git a/pkg/redisv2/tester/mock_client_test.go b/pkg/redisv2/tester/mock_client_test.go index de66be4..8ac5451 100644 --- a/pkg/redisv2/tester/mock_client_test.go +++ b/pkg/redisv2/tester/mock_client_test.go @@ -3,8 +3,8 @@ package tester import ( "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" "github.com/stretchr/testify/assert" ) diff --git a/pkg/redisv2/tester/mock_vehicles_cache.go b/pkg/redisv2/tester/mock_vehicles_cache.go index 5bbebe2..913ae93 100644 --- a/pkg/redisv2/tester/mock_vehicles_cache.go +++ b/pkg/redisv2/tester/mock_vehicles_cache.go @@ -1,6 +1,6 @@ package tester -import "fiskerinc.com/modules/cache" +import "github.com/fiskerinc/cloud-services/pkg/cache" type MockVehiclesCache struct{} diff --git a/pkg/redisv2/tester/redis_test_case.go b/pkg/redisv2/tester/redis_test_case.go index 86aad5c..8f42db4 100644 --- a/pkg/redisv2/tester/redis_test_case.go +++ b/pkg/redisv2/tester/redis_test_case.go @@ -5,8 +5,8 @@ import ( "regexp" "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) type ExpiringCacheResult struct { diff --git a/pkg/remotefileupload/aws.go b/pkg/remotefileupload/aws.go index 4619d17..2694310 100644 --- a/pkg/remotefileupload/aws.go +++ b/pkg/remotefileupload/aws.go @@ -6,8 +6,8 @@ import ( "net/http" "path/filepath" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" diff --git a/pkg/remotefileupload/aws_test.go b/pkg/remotefileupload/aws_test.go index 27bb37e..f6cc043 100644 --- a/pkg/remotefileupload/aws_test.go +++ b/pkg/remotefileupload/aws_test.go @@ -3,10 +3,10 @@ package remotefileupload_test import ( "testing" - "fiskerinc.com/modules/remotefileupload" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/remotefileupload" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestNewAWSUploaderIntegration(t *testing.T) { diff --git a/pkg/remotefileupload/azure.go b/pkg/remotefileupload/azure.go index feb38d1..8ba4e4e 100644 --- a/pkg/remotefileupload/azure.go +++ b/pkg/remotefileupload/azure.go @@ -7,8 +7,8 @@ import ( "io" "net/url" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/appendblob" diff --git a/pkg/remotefileupload/azure_test.go b/pkg/remotefileupload/azure_test.go index ebb7a0f..b153b81 100644 --- a/pkg/remotefileupload/azure_test.go +++ b/pkg/remotefileupload/azure_test.go @@ -5,10 +5,10 @@ import ( "testing" "time" - "fiskerinc.com/modules/remotefileupload" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/remotefileupload" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) // In order to run the integration tests on the uploader, the go routine that makes the upload needs to be not a goroutine diff --git a/pkg/remotefileupload/backup.go b/pkg/remotefileupload/backup.go index 21f760c..84f07bd 100644 --- a/pkg/remotefileupload/backup.go +++ b/pkg/remotefileupload/backup.go @@ -8,9 +8,9 @@ import ( "sync" "time" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/elptr" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/appendblob" diff --git a/pkg/remotefileupload/batchuploader.go b/pkg/remotefileupload/batchuploader.go index 5c649c4..faadb5c 100644 --- a/pkg/remotefileupload/batchuploader.go +++ b/pkg/remotefileupload/batchuploader.go @@ -8,8 +8,8 @@ import ( "sync" "time" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/appendblob" @@ -17,7 +17,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas" "github.com/pkg/errors" - "fiskerinc.com/modules/utils/elptr" + "github.com/fiskerinc/cloud-services/pkg/utils/elptr" ) // Comments on other versions I created diff --git a/pkg/remotefileupload/csvtoparquet.go b/pkg/remotefileupload/csvtoparquet.go index 89238ea..7ed8a32 100644 --- a/pkg/remotefileupload/csvtoparquet.go +++ b/pkg/remotefileupload/csvtoparquet.go @@ -7,7 +7,7 @@ import ( "strconv" "strings" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" ) var ( diff --git a/pkg/remotefileupload/parquet.go b/pkg/remotefileupload/parquet.go index 5945d9f..822c07f 100644 --- a/pkg/remotefileupload/parquet.go +++ b/pkg/remotefileupload/parquet.go @@ -3,7 +3,7 @@ package remotefileupload import ( "context" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" az "github.com/Azure/azure-storage-blob-go/azblob" pqAZ "github.com/xitongsys/parquet-go-source/azblob" "github.com/xitongsys/parquet-go/source" diff --git a/pkg/s3/mock/s3_mock.go b/pkg/s3/mock/s3_mock.go index 4127704..7858c8a 100644 --- a/pkg/s3/mock/s3_mock.go +++ b/pkg/s3/mock/s3_mock.go @@ -5,7 +5,7 @@ import ( "io" "io/ioutil" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" ) var downloadurl = envtool.GetEnv("OTA_UPDATE_DOWNLOAD_URL", "https://upload-dev.fiskerdps.com") diff --git a/pkg/s3/s3.go b/pkg/s3/s3.go index 6d67bb2..15bcb8b 100644 --- a/pkg/s3/s3.go +++ b/pkg/s3/s3.go @@ -4,7 +4,7 @@ import ( "fmt" "io" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" diff --git a/pkg/scheduler/bucket_test.go b/pkg/scheduler/bucket_test.go index 3347821..d8d587f 100644 --- a/pkg/scheduler/bucket_test.go +++ b/pkg/scheduler/bucket_test.go @@ -3,7 +3,7 @@ package scheduler import ( "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestBucketProcess(t *testing.T) { diff --git a/pkg/security/ecu_files_decrypt.go b/pkg/security/ecu_files_decrypt.go index 3c65f6c..2d9560a 100644 --- a/pkg/security/ecu_files_decrypt.go +++ b/pkg/security/ecu_files_decrypt.go @@ -1,6 +1,6 @@ package security -import "fiskerinc.com/modules/common" +import "github.com/fiskerinc/cloud-services/pkg/common" func DecryptKeys(ecu *common.UpdateManifestECU) error { keys := ecu.ECCKeys diff --git a/pkg/security/encrypt_test.go b/pkg/security/encrypt_test.go index 487e440..6d2c296 100644 --- a/pkg/security/encrypt_test.go +++ b/pkg/security/encrypt_test.go @@ -3,8 +3,8 @@ package security_test import ( "testing" - "fiskerinc.com/modules/security" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/security" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/stretchr/testify/assert" ) diff --git a/pkg/security/encryptor.go b/pkg/security/encryptor.go index 77583d5..866437f 100644 --- a/pkg/security/encryptor.go +++ b/pkg/security/encryptor.go @@ -3,7 +3,7 @@ package security import ( "sync" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" ) var envOnce sync.Once diff --git a/pkg/security/salter.go b/pkg/security/salter.go index ef3f665..efe5e2e 100644 --- a/pkg/security/salter.go +++ b/pkg/security/salter.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" redigo "github.com/gomodule/redigo/redis" "github.com/pkg/errors" diff --git a/pkg/security/salter_test.go b/pkg/security/salter_test.go index 378e66a..3639cec 100644 --- a/pkg/security/salter_test.go +++ b/pkg/security/salter_test.go @@ -3,9 +3,9 @@ package security_test import ( "testing" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/security" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/security" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestSalter(t *testing.T) { diff --git a/pkg/smtpclient/smtp_test.go b/pkg/smtpclient/smtp_test.go index e241fb3..42a560a 100644 --- a/pkg/smtpclient/smtp_test.go +++ b/pkg/smtpclient/smtp_test.go @@ -3,8 +3,8 @@ package smtpclient_test import ( "testing" - "fiskerinc.com/modules/smtpclient" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/smtpclient" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestSMTP(t *testing.T) { diff --git a/pkg/superset/cache_acc_token.go b/pkg/superset/cache_acc_token.go index 117be17..03bd98c 100644 --- a/pkg/superset/cache_acc_token.go +++ b/pkg/superset/cache_acc_token.go @@ -3,7 +3,7 @@ package superset import ( "fmt" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/redis" "github.com/pkg/errors" ) diff --git a/pkg/superset/dashboards.go b/pkg/superset/dashboards.go index d4443a7..5abd9bb 100644 --- a/pkg/superset/dashboards.go +++ b/pkg/superset/dashboards.go @@ -7,7 +7,7 @@ import ( "net/url" "strconv" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/pkg/errors" ) diff --git a/pkg/superset/guest_token.go b/pkg/superset/guest_token.go index e8fe83e..f745d03 100644 --- a/pkg/superset/guest_token.go +++ b/pkg/superset/guest_token.go @@ -6,8 +6,8 @@ import ( "io/ioutil" "net/http" - "fiskerinc.com/modules/httpclient" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/httpclient" + "github.com/fiskerinc/cloud-services/pkg/redis" "github.com/pkg/errors" ) diff --git a/pkg/superset/guest_token_test.go b/pkg/superset/guest_token_test.go index ad537b8..326cb32 100644 --- a/pkg/superset/guest_token_test.go +++ b/pkg/superset/guest_token_test.go @@ -6,11 +6,11 @@ import ( "net/http" "testing" - "fiskerinc.com/modules/httpclient" - "fiskerinc.com/modules/httpclient/mock" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/redis/tester" - "fiskerinc.com/modules/superset" + "github.com/fiskerinc/cloud-services/pkg/httpclient" + "github.com/fiskerinc/cloud-services/pkg/httpclient/mock" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/redis/tester" + "github.com/fiskerinc/cloud-services/pkg/superset" "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) diff --git a/pkg/superset/login.go b/pkg/superset/login.go index f34e3f0..766230c 100644 --- a/pkg/superset/login.go +++ b/pkg/superset/login.go @@ -5,8 +5,8 @@ import ( "io/ioutil" "net/http" - "fiskerinc.com/modules/httpclient" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/httpclient" + "github.com/fiskerinc/cloud-services/pkg/redis" "github.com/pkg/errors" ) diff --git a/pkg/superset/login_test.go b/pkg/superset/login_test.go index bf1f8c9..5b9e8a8 100644 --- a/pkg/superset/login_test.go +++ b/pkg/superset/login_test.go @@ -6,11 +6,11 @@ import ( "net/http" "testing" - "fiskerinc.com/modules/httpclient" - "fiskerinc.com/modules/httpclient/mock" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/redis/tester" - "fiskerinc.com/modules/superset" + "github.com/fiskerinc/cloud-services/pkg/httpclient" + "github.com/fiskerinc/cloud-services/pkg/httpclient/mock" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/redis/tester" + "github.com/fiskerinc/cloud-services/pkg/superset" "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) diff --git a/pkg/superset/superset.go b/pkg/superset/superset.go index f0c7ca3..fb5f739 100644 --- a/pkg/superset/superset.go +++ b/pkg/superset/superset.go @@ -1,6 +1,6 @@ package superset -import "fiskerinc.com/modules/utils/envtool" +import "github.com/fiskerinc/cloud-services/pkg/utils/envtool" var ( host = envtool.GetEnv("SUPERSET_URL", "REPLACE_ME") diff --git a/pkg/testhelper/json_compare_test.go b/pkg/testhelper/json_compare_test.go index 3f5416a..08dc128 100644 --- a/pkg/testhelper/json_compare_test.go +++ b/pkg/testhelper/json_compare_test.go @@ -3,7 +3,7 @@ package testhelper_test import ( "testing" - th "fiskerinc.com/modules/testhelper" + th "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestJSONCompare(t *testing.T) { diff --git a/pkg/testhelper/testhelper.go b/pkg/testhelper/testhelper.go index a8b828b..238a1a0 100644 --- a/pkg/testhelper/testhelper.go +++ b/pkg/testhelper/testhelper.go @@ -13,7 +13,7 @@ import ( "testing" "time" - "fiskerinc.com/modules/common/dbbasemodel" + "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" "github.com/julienschmidt/httprouter" ) diff --git a/pkg/testrunner/test_case.go b/pkg/testrunner/test_case.go index 628977a..66dc14e 100644 --- a/pkg/testrunner/test_case.go +++ b/pkg/testrunner/test_case.go @@ -1,10 +1,10 @@ package testrunner import ( - dbtc "fiskerinc.com/modules/db/queries/mocks" - htc "fiskerinc.com/modules/httpclient/tester" - ktc "fiskerinc.com/modules/kafka/mock" - rtc "fiskerinc.com/modules/redis/tester" + dbtc "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks" + htc "github.com/fiskerinc/cloud-services/pkg/httpclient/tester" + ktc "github.com/fiskerinc/cloud-services/pkg/kafka/mock" + rtc "github.com/fiskerinc/cloud-services/pkg/redis/tester" ) type TestCase struct { diff --git a/pkg/timezone/timezone.go b/pkg/timezone/timezone.go index c8cbd0d..c9184e9 100644 --- a/pkg/timezone/timezone.go +++ b/pkg/timezone/timezone.go @@ -13,8 +13,8 @@ import ( "github.com/pkg/errors" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" ) type Timezone struct { diff --git a/pkg/timezone/timezone_test.go b/pkg/timezone/timezone_test.go index c60f3f4..12092e9 100644 --- a/pkg/timezone/timezone_test.go +++ b/pkg/timezone/timezone_test.go @@ -10,11 +10,11 @@ import ( "testing" "time" - "fiskerinc.com/modules/redis" - "fiskerinc.com/modules/redis/tester" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/timezone" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/redis" + "github.com/fiskerinc/cloud-services/pkg/redis/tester" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/timezone" + "github.com/fiskerinc/cloud-services/pkg/validator" ) var someTime, _ = time.Parse(time.RFC3339Nano, "2023-12-27T18:09:41.0701652Z") diff --git a/pkg/tmobile/client.go b/pkg/tmobile/client.go index 0149518..0a280ab 100644 --- a/pkg/tmobile/client.go +++ b/pkg/tmobile/client.go @@ -12,13 +12,13 @@ import ( "sync" "time" - "fiskerinc.com/modules/grpc/sms" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/randomvalues" + "github.com/fiskerinc/cloud-services/pkg/grpc/sms" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/randomvalues" "github.com/pkg/errors" errorsO "errors" - tmtg "fiskerinc.com/modules/tmobtokengen" + tmtg "github.com/fiskerinc/cloud-services/pkg/tmobtokengen" ) const ( diff --git a/pkg/tmobile/client_it_test.go b/pkg/tmobile/client_it_test.go index a0d7dcf..fd31eef 100644 --- a/pkg/tmobile/client_it_test.go +++ b/pkg/tmobile/client_it_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "fiskerinc.com/modules/grpc/sms" + "github.com/fiskerinc/cloud-services/pkg/grpc/sms" ) var CLIENT_ID = "" diff --git a/pkg/tmobile/client_mini.go b/pkg/tmobile/client_mini.go index 5e01888..7cd6b39 100644 --- a/pkg/tmobile/client_mini.go +++ b/pkg/tmobile/client_mini.go @@ -12,9 +12,9 @@ import ( errorsO "errors" - "fiskerinc.com/modules/grpc/sms" - tmtg "fiskerinc.com/modules/tmobtokengen" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/grpc/sms" + tmtg "github.com/fiskerinc/cloud-services/pkg/tmobtokengen" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/pkg/errors" ) diff --git a/pkg/tmobile/client_simulator.go b/pkg/tmobile/client_simulator.go index 37c5df9..bc02082 100644 --- a/pkg/tmobile/client_simulator.go +++ b/pkg/tmobile/client_simulator.go @@ -7,7 +7,7 @@ import ( "sync" "time" - "fiskerinc.com/modules/grpc/sms" + "github.com/fiskerinc/cloud-services/pkg/grpc/sms" ) // Want to make a client simulator that has a delay for each message, along with a chance of failure @@ -139,8 +139,8 @@ var _ TMobClienter = new(tmobileSimulator) // 10ms 6.25% 87.50% 10ms 6.25% runtime.read // 10ms 6.25% 93.75% 10ms 6.25% runtime.usleep // 10ms 6.25% 100% 10ms 6.25% runtime.write1 -// 0 0% 100% 20ms 12.50% fiskerinc.com/modules/tmobile.(*SMSClient).SendSMS -// 0 0% 100% 10ms 6.25% fiskerinc.com/modules/tmobile.(*tmobileSimulator).SendSMS +// 0 0% 100% 20ms 12.50% github.com/fiskerinc/cloud-services/pkg/tmobile.(*SMSClient).SendSMS +// 0 0% 100% 10ms 6.25% github.com/fiskerinc/cloud-services/pkg/tmobile.(*tmobileSimulator).SendSMS // with default //BenchmarkSMSWrapper-16 1 5110808221 ns/op 2923960 B/op 43176 allocs/op no major difference // flat flat% sum% cum cum% @@ -150,7 +150,7 @@ var _ TMobClienter = new(tmobileSimulator) // 10ms 9.09% 81.82% 10ms 9.09% runtime.kevent // 10ms 9.09% 90.91% 10ms 9.09% runtime.stackpoolalloc // 10ms 9.09% 100% 10ms 9.09% runtime.usleep -// 0 0% 100% 10ms 9.09% fiskerinc.com/modules/tmobile.BenchmarkSMSWrapper +// 0 0% 100% 10ms 9.09% github.com/fiskerinc/cloud-services/pkg/tmobile.BenchmarkSMSWrapper // 0 0% 100% 10ms 9.09% runtime.copystack // 0 0% 100% 10ms 9.09% runtime.findObject // 0 0% 100% 60ms 54.55% runtime.findRunnable diff --git a/pkg/tmobile/client_test.go b/pkg/tmobile/client_test.go index b7cd089..6a5e487 100644 --- a/pkg/tmobile/client_test.go +++ b/pkg/tmobile/client_test.go @@ -11,8 +11,8 @@ import ( "testing" "time" - "fiskerinc.com/modules/grpc/sms" - "fiskerinc.com/modules/tmobtokengen" + "github.com/fiskerinc/cloud-services/pkg/grpc/sms" + "github.com/fiskerinc/cloud-services/pkg/tmobtokengen" ) type mockTG struct { diff --git a/pkg/tmobile/iccid_filter.go b/pkg/tmobile/iccid_filter.go index 672c44d..ef595c1 100644 --- a/pkg/tmobile/iccid_filter.go +++ b/pkg/tmobile/iccid_filter.go @@ -3,7 +3,7 @@ package tmobile import ( "strings" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" ) type ICCIDFilter struct { diff --git a/pkg/tmobile/iccid_filter_test.go b/pkg/tmobile/iccid_filter_test.go index 1d45ea7..0e267d2 100644 --- a/pkg/tmobile/iccid_filter_test.go +++ b/pkg/tmobile/iccid_filter_test.go @@ -4,7 +4,7 @@ import ( "os" "testing" - "fiskerinc.com/modules/tmobile" + "github.com/fiskerinc/cloud-services/pkg/tmobile" ) func TestICCIDFilter(t *testing.T){ diff --git a/pkg/tmobile/model.go b/pkg/tmobile/model.go index 5ef99db..457ddc2 100644 --- a/pkg/tmobile/model.go +++ b/pkg/tmobile/model.go @@ -5,9 +5,9 @@ import ( "strings" "time" - "fiskerinc.com/modules/grpc/sms" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/grpc/sms" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" ) var FISKER_TMOBILE_ACCOUNT_ID = envtool.GetEnv("FISKER_TMOBILE_ACCOUNT_ID", "500556839") diff --git a/pkg/tmobile/model_test.go b/pkg/tmobile/model_test.go index 22a8ef1..0d863c7 100644 --- a/pkg/tmobile/model_test.go +++ b/pkg/tmobile/model_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "fiskerinc.com/modules/grpc/sms" + "github.com/fiskerinc/cloud-services/pkg/grpc/sms" ) func TestSendSMSRequest_ExpireIfNotDelivered(t *testing.T) { diff --git a/pkg/tmobile/queue.go b/pkg/tmobile/queue.go index d768064..a2e86b3 100644 --- a/pkg/tmobile/queue.go +++ b/pkg/tmobile/queue.go @@ -5,11 +5,11 @@ import ( "encoding/json" "time" - "fiskerinc.com/modules/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" - "fiskerinc.com/modules/kafka" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/kafka" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/pkg/errors" "google.golang.org/protobuf/proto" ) diff --git a/pkg/tmobile/util.go b/pkg/tmobile/util.go index a7c88e3..09a0b23 100644 --- a/pkg/tmobile/util.go +++ b/pkg/tmobile/util.go @@ -4,7 +4,7 @@ import ( "os" "strings" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/pkg/errors" ) diff --git a/pkg/tmobile/wrapper.go b/pkg/tmobile/wrapper.go index e529854..56e15a6 100644 --- a/pkg/tmobile/wrapper.go +++ b/pkg/tmobile/wrapper.go @@ -4,9 +4,9 @@ import ( "context" "time" - "fiskerinc.com/modules/grpc/sms" - "fiskerinc.com/modules/kafka" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/grpc/sms" + "github.com/fiskerinc/cloud-services/pkg/kafka" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/pkg/errors" ) diff --git a/pkg/towmanparklocation/parklocation.go b/pkg/towmanparklocation/parklocation.go index 9df9e13..5701674 100644 --- a/pkg/towmanparklocation/parklocation.go +++ b/pkg/towmanparklocation/parklocation.go @@ -4,10 +4,10 @@ import ( "sync" "time" - "fiskerinc.com/modules/cachev2" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redisv2" + "github.com/fiskerinc/cloud-services/pkg/cachev2" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redisv2" ) func (plt *ParkLocationTracker) VCUGearSig(vin string, newState int) { diff --git a/pkg/towmanparklocation/parklocation_test.go b/pkg/towmanparklocation/parklocation_test.go index 88517de..0d02906 100644 --- a/pkg/towmanparklocation/parklocation_test.go +++ b/pkg/towmanparklocation/parklocation_test.go @@ -4,9 +4,9 @@ package towmanparklocation import ( "testing" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/redisv2" - "fiskerinc.com/modules/utils/localtests" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/redisv2" + "github.com/fiskerinc/cloud-services/pkg/utils/localtests" ) diff --git a/pkg/tracer/tracer.go b/pkg/tracer/tracer.go index ac4f037..3f1bd44 100644 --- a/pkg/tracer/tracer.go +++ b/pkg/tracer/tracer.go @@ -3,7 +3,7 @@ package tracer import ( "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" ) var agentAvailable bool diff --git a/pkg/usecase_helpers/ecu_keys.go b/pkg/usecase_helpers/ecu_keys.go index 51fac58..2a29a8f 100644 --- a/pkg/usecase_helpers/ecu_keys.go +++ b/pkg/usecase_helpers/ecu_keys.go @@ -1,8 +1,8 @@ package usecase_helpers import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" ) func NewECUKeys(eccKeys queries.EccKeysInterface) *EcuKeys { diff --git a/pkg/usecase_helpers/ecus.go b/pkg/usecase_helpers/ecus.go index 3334c05..1883884 100644 --- a/pkg/usecase_helpers/ecus.go +++ b/pkg/usecase_helpers/ecus.go @@ -1,8 +1,8 @@ package usecase_helpers import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" ) // PopulateECUsCurrentVersion adds CurrentVersion field in provided ecus list of diff --git a/pkg/usecase_helpers/ecus_test.go b/pkg/usecase_helpers/ecus_test.go index 35223d2..3e18768 100644 --- a/pkg/usecase_helpers/ecus_test.go +++ b/pkg/usecase_helpers/ecus_test.go @@ -3,9 +3,9 @@ package usecase_helpers import ( "testing" - m "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/db/queries/mocks" + m "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks" "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) diff --git a/pkg/usecase_helpers/get_cds.go b/pkg/usecase_helpers/get_cds.go index 8fb1ff8..013e293 100644 --- a/pkg/usecase_helpers/get_cds.go +++ b/pkg/usecase_helpers/get_cds.go @@ -6,12 +6,12 @@ import ( "fmt" "strings" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" - "fiskerinc.com/modules/utils/vod" - vconfig "fiskerinc.com/modules/vehicleconfig" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/utils/vod" + vconfig "github.com/fiskerinc/cloud-services/pkg/vehicleconfig" "github.com/go-pg/pg/v10" "github.com/pkg/errors" ) diff --git a/pkg/usecase_helpers/get_cds_test.go b/pkg/usecase_helpers/get_cds_test.go index b838956..dea253d 100644 --- a/pkg/usecase_helpers/get_cds_test.go +++ b/pkg/usecase_helpers/get_cds_test.go @@ -5,9 +5,9 @@ import ( "os" "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/stretchr/testify/assert" ) diff --git a/pkg/usecase_helpers/update_notifier.go b/pkg/usecase_helpers/update_notifier.go index b513f57..d7e578b 100644 --- a/pkg/usecase_helpers/update_notifier.go +++ b/pkg/usecase_helpers/update_notifier.go @@ -5,12 +5,12 @@ import ( "net/http" "strings" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/common/carupdatestatus" - "fiskerinc.com/modules/db/queries" - e "fiskerinc.com/modules/errors" - "fiskerinc.com/modules/grpc/kafka_grpc" - "fiskerinc.com/modules/kafka" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/common/carupdatestatus" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + e "github.com/fiskerinc/cloud-services/pkg/errors" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/kafka" "github.com/pkg/errors" "google.golang.org/protobuf/proto" ) diff --git a/pkg/userconsent/mock.go b/pkg/userconsent/mock.go index e23767f..f3225a1 100644 --- a/pkg/userconsent/mock.go +++ b/pkg/userconsent/mock.go @@ -4,9 +4,9 @@ import ( "net/http" "net/http/httptest" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/validator" - "fiskerinc.com/modules/utils" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/validator" + "github.com/fiskerinc/cloud-services/pkg/utils" ) type UserConsentServiceMock struct{} diff --git a/pkg/userconsent/userconsent.go b/pkg/userconsent/userconsent.go index 328864c..8fe24c9 100644 --- a/pkg/userconsent/userconsent.go +++ b/pkg/userconsent/userconsent.go @@ -6,9 +6,9 @@ import ( "net/http" "sync" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/pkg/errors" ) diff --git a/pkg/utils/app/app.go b/pkg/utils/app/app.go index bfd3494..7785957 100644 --- a/pkg/utils/app/app.go +++ b/pkg/utils/app/app.go @@ -5,7 +5,7 @@ import ( "os/signal" "syscall" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" ) // Setup primes application while enabling proper diff --git a/pkg/utils/auth_helper.go b/pkg/utils/auth_helper.go index 3eebaae..b6f5695 100644 --- a/pkg/utils/auth_helper.go +++ b/pkg/utils/auth_helper.go @@ -4,7 +4,7 @@ import ( "context" "net/http" - c "fiskerinc.com/modules/common/context" + c "github.com/fiskerinc/cloud-services/pkg/common/context" ) // auth_helper gives tool to our authorization services so that future middleware components and actual endpoints will have access diff --git a/pkg/utils/auth_helper_test.go b/pkg/utils/auth_helper_test.go index 6b55536..654a828 100644 --- a/pkg/utils/auth_helper_test.go +++ b/pkg/utils/auth_helper_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "fiskerinc.com/modules/utils" + "github.com/fiskerinc/cloud-services/pkg/utils" ) func TestWriteProviderToContext(t *testing.T) { diff --git a/pkg/utils/certificate_parser.go b/pkg/utils/certificate_parser.go index 6dba9fe..33c4362 100644 --- a/pkg/utils/certificate_parser.go +++ b/pkg/utils/certificate_parser.go @@ -5,8 +5,8 @@ import ( "net/http" "strings" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/validator" ) // ParseVINFromRequest retrieves VIN from "Ssl-Client-Subject-Dn" diff --git a/pkg/utils/certificate_parser_test.go b/pkg/utils/certificate_parser_test.go index e9eb56f..7ad2a12 100644 --- a/pkg/utils/certificate_parser_test.go +++ b/pkg/utils/certificate_parser_test.go @@ -4,7 +4,7 @@ import ( "net/http/httptest" "testing" - "fiskerinc.com/modules/utils" + "github.com/fiskerinc/cloud-services/pkg/utils" ) func TestParseVINFromRequestBothFormats(t *testing.T) { diff --git a/pkg/utils/envtool/env_test.go b/pkg/utils/envtool/env_test.go index cd00a5b..3d06a28 100644 --- a/pkg/utils/envtool/env_test.go +++ b/pkg/utils/envtool/env_test.go @@ -5,7 +5,7 @@ import ( "strconv" "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) var defaultValue = "DEFAULT_VALUE" diff --git a/pkg/utils/hexadecimal_test.go b/pkg/utils/hexadecimal_test.go index 92408e2..182bb79 100644 --- a/pkg/utils/hexadecimal_test.go +++ b/pkg/utils/hexadecimal_test.go @@ -3,7 +3,7 @@ package utils import ( "testing" - th "fiskerinc.com/modules/testhelper" + th "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestHexadecimal(t *testing.T) { diff --git a/pkg/utils/json_resp.go b/pkg/utils/json_resp.go index fc27cb0..d325fda 100644 --- a/pkg/utils/json_resp.go +++ b/pkg/utils/json_resp.go @@ -5,7 +5,7 @@ import ( "io/ioutil" "net/http" - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" ) // LinkResult makes result with link and timestamp diff --git a/pkg/utils/json_resp_test.go b/pkg/utils/json_resp_test.go index 0f2703e..b07ec15 100644 --- a/pkg/utils/json_resp_test.go +++ b/pkg/utils/json_resp_test.go @@ -7,9 +7,9 @@ import ( "testing" "time" - m "fiskerinc.com/modules/common" - th "fiskerinc.com/modules/testhelper" - u "fiskerinc.com/modules/utils" + m "github.com/fiskerinc/cloud-services/pkg/common" + th "github.com/fiskerinc/cloud-services/pkg/testhelper" + u "github.com/fiskerinc/cloud-services/pkg/utils" ) func TestRespError(t *testing.T) { diff --git a/pkg/utils/json_rpc_resp.go b/pkg/utils/json_rpc_resp.go index 58c0254..6c31290 100644 --- a/pkg/utils/json_rpc_resp.go +++ b/pkg/utils/json_rpc_resp.go @@ -5,7 +5,7 @@ import ( "fmt" "net/http" - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" ) const ErrCodeJSONRPCParse = -32700 diff --git a/pkg/utils/localtests/localtests.go b/pkg/utils/localtests/localtests.go new file mode 100644 index 0000000..9531690 --- /dev/null +++ b/pkg/utils/localtests/localtests.go @@ -0,0 +1,11 @@ +// Package localtests provides test utilities for local development. +package localtests + +import "os" + +// SkipIfNotLocal skips the test if not running locally +func SkipIfNotLocal(t interface{ Skip(...any) }) { + if os.Getenv("LOCAL_TESTS") != "true" { + t.Skip("Skipping local-only test") + } +} diff --git a/pkg/utils/mt19937/bernoulli_distribution.go b/pkg/utils/mt19937/bernoulli_distribution.go index fa11b96..4471829 100644 --- a/pkg/utils/mt19937/bernoulli_distribution.go +++ b/pkg/utils/mt19937/bernoulli_distribution.go @@ -1,7 +1,7 @@ package mt19937 import ( - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" ) type bernoulli_distribution struct { diff --git a/pkg/utils/mt19937/discrete_distribution.go b/pkg/utils/mt19937/discrete_distribution.go index 8c3114f..3d4c119 100644 --- a/pkg/utils/mt19937/discrete_distribution.go +++ b/pkg/utils/mt19937/discrete_distribution.go @@ -1,6 +1,6 @@ package mt19937 -import "fiskerinc.com/modules/logger" +import "github.com/fiskerinc/cloud-services/pkg/logger" type aliasTableUnit struct { weight float64 diff --git a/pkg/utils/multipart_parser_test.go b/pkg/utils/multipart_parser_test.go index 7076d89..9d56cb1 100644 --- a/pkg/utils/multipart_parser_test.go +++ b/pkg/utils/multipart_parser_test.go @@ -9,7 +9,7 @@ import ( "net/http" "strings" "testing" - "fiskerinc.com/modules/utils" + "github.com/fiskerinc/cloud-services/pkg/utils" ) func TestMultipartParser(t *testing.T) { diff --git a/pkg/utils/quadkey/quadkey_test.go b/pkg/utils/quadkey/quadkey_test.go index a310fcd..a5b70e5 100644 --- a/pkg/utils/quadkey/quadkey_test.go +++ b/pkg/utils/quadkey/quadkey_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "fiskerinc.com/modules/utils/quadkey" + "github.com/fiskerinc/cloud-services/pkg/utils/quadkey" "github.com/stretchr/testify/assert" ) diff --git a/pkg/utils/querystring/querystring_test.go b/pkg/utils/querystring/querystring_test.go index 17216fe..8fc4cd3 100644 --- a/pkg/utils/querystring/querystring_test.go +++ b/pkg/utils/querystring/querystring_test.go @@ -3,7 +3,7 @@ package querystring_test import ( "testing" - "fiskerinc.com/modules/utils/querystring" + "github.com/fiskerinc/cloud-services/pkg/utils/querystring" "github.com/stretchr/testify/assert" ) diff --git a/pkg/utils/randomvalues/randomvalues.go b/pkg/utils/randomvalues/randomvalues.go index 38b4328..07d2a15 100644 --- a/pkg/utils/randomvalues/randomvalues.go +++ b/pkg/utils/randomvalues/randomvalues.go @@ -9,7 +9,7 @@ import ( "sync" "time" - "fiskerinc.com/modules/utils/mt19937" + "github.com/fiskerinc/cloud-services/pkg/utils/mt19937" ) type Generator struct { diff --git a/pkg/utils/randomvalues/randomvalues_test.go b/pkg/utils/randomvalues/randomvalues_test.go index af66f01..762f8bc 100644 --- a/pkg/utils/randomvalues/randomvalues_test.go +++ b/pkg/utils/randomvalues/randomvalues_test.go @@ -5,8 +5,8 @@ import ( "fmt" "testing" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/utils/randomvalues" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/utils/randomvalues" ) func TestRandomString(t *testing.T) { diff --git a/pkg/utils/urlhelper/urlhelper_test.go b/pkg/utils/urlhelper/urlhelper_test.go index 2b26fb9..ecc4141 100644 --- a/pkg/utils/urlhelper/urlhelper_test.go +++ b/pkg/utils/urlhelper/urlhelper_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func checkQueryString(t *testing.T, querystring string, data map[string]string) { diff --git a/pkg/utils/vin_parser.go b/pkg/utils/vin_parser.go index f6c4591..d27d3b4 100644 --- a/pkg/utils/vin_parser.go +++ b/pkg/utils/vin_parser.go @@ -1,11 +1,11 @@ package utils import ( - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/validator" - "fiskerinc.com/modules/vindecoder" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/validator" + "github.com/fiskerinc/cloud-services/pkg/vindecoder" "github.com/pkg/errors" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" ) var ( diff --git a/pkg/utils/vin_parser_test.go b/pkg/utils/vin_parser_test.go index f5d90cf..8f34de6 100644 --- a/pkg/utils/vin_parser_test.go +++ b/pkg/utils/vin_parser_test.go @@ -3,7 +3,7 @@ package utils import ( "testing" - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) diff --git a/pkg/utils/whereami/where_am_i.go b/pkg/utils/whereami/where_am_i.go index fa1f496..4fb9a79 100644 --- a/pkg/utils/whereami/where_am_i.go +++ b/pkg/utils/whereami/where_am_i.go @@ -1,6 +1,6 @@ package whereami -import "fiskerinc.com/modules/utils/envtool" +import "github.com/fiskerinc/cloud-services/pkg/utils/envtool" var ( Environment serviceEnvironment = serviceEnvironment(envtool.GetEnv("APP_SERVICE_ENVIRONMENT", "")) diff --git a/pkg/utils/whereami/where_am_i_test.go b/pkg/utils/whereami/where_am_i_test.go index c2cff83..d59ef13 100644 --- a/pkg/utils/whereami/where_am_i_test.go +++ b/pkg/utils/whereami/where_am_i_test.go @@ -3,7 +3,7 @@ package whereami_test import ( "testing" - "fiskerinc.com/modules/utils/whereami" + "github.com/fiskerinc/cloud-services/pkg/utils/whereami" ) func TestNoENV(t *testing.T){ diff --git a/pkg/utils/xml_resp.go b/pkg/utils/xml_resp.go index b695491..3e1372f 100644 --- a/pkg/utils/xml_resp.go +++ b/pkg/utils/xml_resp.go @@ -4,7 +4,7 @@ import ( "encoding/xml" "net/http" - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" ) // ErrorXMLResult makes error result diff --git a/pkg/validator/can_id_test.go b/pkg/validator/can_id_test.go index d2512a0..48c9d28 100644 --- a/pkg/validator/can_id_test.go +++ b/pkg/validator/can_id_test.go @@ -3,8 +3,8 @@ package validator_test import ( "testing" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/validator" ) type TestCANID struct { diff --git a/pkg/validator/certificate_serial.go b/pkg/validator/certificate_serial.go index 7a9b565..7e4c362 100644 --- a/pkg/validator/certificate_serial.go +++ b/pkg/validator/certificate_serial.go @@ -3,7 +3,7 @@ package validator import ( "regexp" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/go-playground/validator/v10" "github.com/pkg/errors" ) diff --git a/pkg/validator/certificate_serial_test.go b/pkg/validator/certificate_serial_test.go index 91316bb..ba47473 100644 --- a/pkg/validator/certificate_serial_test.go +++ b/pkg/validator/certificate_serial_test.go @@ -3,8 +3,8 @@ package validator_test import ( "testing" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/validator" ) type TestCertSerial struct { diff --git a/pkg/validator/ecu.go b/pkg/validator/ecu.go index 50bbfdc..f24b80e 100644 --- a/pkg/validator/ecu.go +++ b/pkg/validator/ecu.go @@ -1,7 +1,7 @@ package validator import ( - "fiskerinc.com/modules/common" + "github.com/fiskerinc/cloud-services/pkg/common" "github.com/go-playground/validator/v10" ) diff --git a/pkg/validator/ecu_test.go b/pkg/validator/ecu_test.go index c97dbd6..a340261 100644 --- a/pkg/validator/ecu_test.go +++ b/pkg/validator/ecu_test.go @@ -3,8 +3,8 @@ package validator_test import ( "testing" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/validator" ) type TestECU struct { diff --git a/pkg/validator/email_test.go b/pkg/validator/email_test.go index 032b313..8cc832b 100644 --- a/pkg/validator/email_test.go +++ b/pkg/validator/email_test.go @@ -3,7 +3,7 @@ package validator_test import ( "testing" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/validator" "github.com/pkg/errors" "github.com/stretchr/testify/assert" diff --git a/pkg/validator/fleet_name_test.go b/pkg/validator/fleet_name_test.go index 5bd5c49..68aa7a3 100644 --- a/pkg/validator/fleet_name_test.go +++ b/pkg/validator/fleet_name_test.go @@ -3,8 +3,8 @@ package validator_test import ( "testing" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/validator" ) type TestFleet struct { diff --git a/pkg/validator/pg_order_by_test.go b/pkg/validator/pg_order_by_test.go index 9c0bb43..c3b5e11 100644 --- a/pkg/validator/pg_order_by_test.go +++ b/pkg/validator/pg_order_by_test.go @@ -3,8 +3,8 @@ package validator_test import ( "testing" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/validator" ) type TestPageQueryOptions struct { diff --git a/pkg/validator/validator_test.go b/pkg/validator/validator_test.go index 5e6015b..1b9aa82 100644 --- a/pkg/validator/validator_test.go +++ b/pkg/validator/validator_test.go @@ -4,9 +4,9 @@ import ( "fmt" "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/validator" v "github.com/go-playground/validator/v10" ) diff --git a/pkg/validator/vin.go b/pkg/validator/vin.go index 7077eb3..f86a59c 100644 --- a/pkg/validator/vin.go +++ b/pkg/validator/vin.go @@ -6,7 +6,7 @@ import ( "github.com/go-playground/validator/v10" "github.com/pkg/errors" - "fiskerinc.com/modules/vindecoder" + "github.com/fiskerinc/cloud-services/pkg/vindecoder" ) func validateVIN(fl validator.FieldLevel) bool { diff --git a/pkg/validator/vin_check_test.go b/pkg/validator/vin_check_test.go index 52f7a5a..2175415 100644 --- a/pkg/validator/vin_check_test.go +++ b/pkg/validator/vin_check_test.go @@ -4,8 +4,8 @@ import ( "fmt" "testing" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/validator" ) func TestValidateVinCheckDigit(t *testing.T) { diff --git a/pkg/validator/vin_test.go b/pkg/validator/vin_test.go index 0b6ccba..9e454f3 100644 --- a/pkg/validator/vin_test.go +++ b/pkg/validator/vin_test.go @@ -3,8 +3,8 @@ package validator_test import ( "testing" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/validator" ) type TestCar struct { diff --git a/pkg/vehicleconfig/config.go b/pkg/vehicleconfig/config.go index d97900f..e946baa 100644 --- a/pkg/vehicleconfig/config.go +++ b/pkg/vehicleconfig/config.go @@ -6,10 +6,10 @@ import ( "io" "net/http" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/httpclient" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/httpclient" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/pkg/errors" ) diff --git a/pkg/vehicleconfig/mocks.go b/pkg/vehicleconfig/mocks.go index 345f249..8e70676 100644 --- a/pkg/vehicleconfig/mocks.go +++ b/pkg/vehicleconfig/mocks.go @@ -1,6 +1,6 @@ package vehicleconfig -import "fiskerinc.com/modules/common" +import "github.com/fiskerinc/cloud-services/pkg/common" var ecus = []string{"AGS", "ADB", diff --git a/pkg/vehicleconfig/sap.go b/pkg/vehicleconfig/sap.go index 58681e2..e1108f9 100644 --- a/pkg/vehicleconfig/sap.go +++ b/pkg/vehicleconfig/sap.go @@ -13,10 +13,10 @@ import ( "github.com/pkg/errors" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/common/staticerrors" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/common/staticerrors" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" ) func NewSAPService() SAPServiceInterface { diff --git a/pkg/vindecoder/vin_check_test.go b/pkg/vindecoder/vin_check_test.go index f90e40f..a050c8c 100644 --- a/pkg/vindecoder/vin_check_test.go +++ b/pkg/vindecoder/vin_check_test.go @@ -4,8 +4,8 @@ import ( "strings" "testing" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/vindecoder" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/vindecoder" ) var vwGer2013Valid = "WVWBN7AN6DE546002" diff --git a/scripts/generate-dbc.sh b/scripts/generate-dbc.sh new file mode 100755 index 0000000..f3729cc --- /dev/null +++ b/scripts/generate-dbc.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# Generate DBC Go packages from DBC files +# Usage: ./scripts/generate-dbc.sh /path/to/dbc/files + +set -e + +DBC_SOURCE="${1:-}" + +if [ -z "$DBC_SOURCE" ]; then + echo "Usage: $0 /path/to/dbc/files" + echo "" + echo "DBC files should be in a directory structure like:" + echo " dbc/" + echo " fm29_frs90.dbc" + echo " fm29_frsd0.dbc" + echo " ..." + exit 1 +fi + +if [ ! -d "$DBC_SOURCE" ]; then + echo "Error: DBC source directory not found: $DBC_SOURCE" + exit 1 +fi + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +echo "Generating DBC packages from: $DBC_SOURCE" +echo "Output directory: $REPO_ROOT/pkg/dbc" + +go run "$REPO_ROOT/pkg/can-go/cmd/cantool" generate "$DBC_SOURCE" "$REPO_ROOT/pkg/dbc" + +echo "Done! Generated packages in pkg/dbc/" diff --git a/services/gateway/Dockerfile b/services/gateway/Dockerfile index 21f3a9b..deabdc3 100644 --- a/services/gateway/Dockerfile +++ b/services/gateway/Dockerfile @@ -1,27 +1,44 @@ -ARG BASE_IMAGE=cloud_base_go -FROM ${BASE_IMAGE} as builder-go +# syntax=docker/dockerfile:1 -WORKDIR /build/gateway_go +# Build stage +FROM golang:1.25-alpine AS builder -COPY ./gateway_go/go.mod ./gateway_go/go.sum ./ -RUN go mod edit -replace fiskerinc.com/modules=../fiskerinc.com/modules \ - && go mod download +RUN apk add --no-cache git ca-certificates tzdata -COPY ./gateway_go ./ -RUN go mod edit -replace fiskerinc.com/modules=../fiskerinc.com/modules \ - && go build -tags musl +WORKDIR /app -FROM alpine:3.17 +# Copy go.work and module files for dependency caching +COPY go.work ./ +COPY pkg/go.mod pkg/go.sum ./pkg/ +COPY pkg/can-go/go.mod pkg/can-go/go.sum ./pkg/can-go/ +COPY services/gateway/go.mod services/gateway/go.sum ./services/gateway/ -RUN apk add --no-cache librdkafka --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community \ - && apk add --no-cache ca-certificates +# Download dependencies (cached layer) +RUN --mount=type=cache,target=/go/pkg/mod \ + go mod download -x -COPY ./modules_go/logger/log_config . -ENV LOG_CONFIG=log_config +# Copy source +COPY pkg/ ./pkg/ +COPY services/gateway/ ./services/gateway/ -COPY ./gateway_go/docs ./docs -COPY --from=builder-go /build/gateway_go/gateway . +# Build static binary +WORKDIR /app/services/gateway +RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.cache/go-build \ + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -ldflags="-s -w" -trimpath -o /gateway . -EXPOSE 8077 +# Runtime stage - distroless for minimal attack surface +FROM gcr.io/distroless/static-debian12:nonroot -CMD ./gateway +COPY --from=builder /gateway /gateway +COPY --from=builder /app/services/gateway/docs /docs +COPY --from=builder /app/pkg/logger/log_config /log_config +COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo + +ENV LOG_CONFIG=/log_config +ENV TZ=UTC + +EXPOSE 8077 11011 + +ENTRYPOINT ["/gateway"] diff --git a/services/gateway/README.md b/services/gateway/README.md new file mode 100644 index 0000000..b6b312a --- /dev/null +++ b/services/gateway/README.md @@ -0,0 +1,54 @@ +# Gateway Service + +WebSocket API gateway for vehicle (TRex), HMI, and mobile app connections. + +## What it does + +- Authenticates WebSocket connections via JWT +- Routes messages to Kafka topics +- Manages connection state in Redis +- Serves Swagger docs + +## Ports + +| Port | Purpose | +|------|---------| +| 8077 | HTTP/WebSocket | +| 11011 | Health check | + +## WebSocket Endpoints + +- `/ws/trex` - Vehicle connections +- `/ws/hmi` - HMI connections +- `/ws/mobile` - Mobile app connections + +## Build + +```bash +# Local +go build . + +# Docker +docker build -t gateway -f Dockerfile ../.. +``` + +## Run + +```bash +# Required env vars +export KAFKA_HOSTS=localhost:9092 +export REDIS_HOST=localhost +export JWK_URL=https://keycloak.example.com/realms/consumer/protocol/openid-connect/certs + +./gateway +``` + +## Configuration + +See `deploy/base/configmap-common.yaml` for all options. + +Key settings: +- `KAFKA_HOSTS` - Kafka bootstrap servers +- `REDIS_HOST/PORT` - Redis for session state +- `JWK_URL` - JWKS endpoint for JWT validation +- `LOG_LEVEL` - debug, info, warn, error diff --git a/services/gateway/controllers/health_check.go b/services/gateway/controllers/health_check.go index acec42e..1b8f9fd 100644 --- a/services/gateway/controllers/health_check.go +++ b/services/gateway/controllers/health_check.go @@ -1,8 +1,8 @@ package controllers import ( - "fiskerinc.com/modules/health" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/health" + "github.com/fiskerinc/cloud-services/pkg/logger" ) func HealthCheck() { diff --git a/services/gateway/go.mod b/services/gateway/go.mod index aaacb19..ee7e782 100644 --- a/services/gateway/go.mod +++ b/services/gateway/go.mod @@ -1,8 +1,8 @@ module github.com/fiskerinc/cloud-services/services/gateway -go 1.24 +go 1.25 -toolchain go1.24.3 +toolchain go1.25.6 require ( github.com/fiskerinc/cloud-services/pkg v0.0.0 @@ -23,8 +23,12 @@ require ( github.com/moby/sys/sequential v0.5.0 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/gomega v1.25.0 // indirect + github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/redis/go-redis/v9 v9.5.1 // indirect github.com/stretchr/objx v0.5.2 // indirect + github.com/twmb/franz-go v1.20.6 // indirect + github.com/twmb/franz-go/pkg/kadm v1.17.2 // indirect + github.com/twmb/franz-go/pkg/kmsg v1.12.0 // indirect ) require ( @@ -34,7 +38,6 @@ require ( github.com/DataDog/datadog-go/v5 v5.3.0 // indirect github.com/DataDog/go-tuf v1.0.2-0.5.2 // indirect github.com/DataDog/sketches-go v1.4.2 // indirect - github.com/Fisker-Inc/project-ai-can-go v1.3.1 // indirect github.com/KyleBanks/depth v1.2.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ReneKroon/ttlcache/v2 v2.11.0 // indirect @@ -69,7 +72,7 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/julienschmidt/httprouter v1.3.0 // indirect - github.com/klauspost/compress v1.17.1 // indirect + github.com/klauspost/compress v1.18.2 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect github.com/lestrrat-go/blackmagic v1.0.1 // indirect @@ -107,14 +110,14 @@ require ( go.uber.org/atomic v1.11.0 // indirect go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect - golang.org/x/crypto v0.32.0 // indirect - golang.org/x/mod v0.20.0 // indirect - golang.org/x/net v0.33.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/crypto v0.45.0 // indirect + golang.org/x/mod v0.29.0 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.8.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/tools v0.38.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gopkg.in/yaml.v3 v3.0.1 // indirect inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect diff --git a/services/gateway/go.sum b/services/gateway/go.sum index ec54dea..cc5baf9 100644 --- a/services/gateway/go.sum +++ b/services/gateway/go.sum @@ -18,8 +18,6 @@ github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/ github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= github.com/DataDog/sketches-go v1.4.2 h1:gppNudE9d19cQ98RYABOetxIhpTCl4m7CnbRZjvVA/o= github.com/DataDog/sketches-go v1.4.2/go.mod h1:xJIXldczJyyjnbDop7ZZcLxJdV3+7Kra7H1KMgpgkLk= -github.com/Fisker-Inc/project-ai-can-go v1.3.1 h1:OjqeBun9kQwZA0VP61dANOtMqsdYoDjwBCnDOE4zZsE= -github.com/Fisker-Inc/project-ai-can-go v1.3.1/go.mod h1:8YrzRtqxRfiXEmvXpcQlUvmfCGLlpn+rJE02HiGUm/I= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= @@ -174,8 +172,7 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/klauspost/compress v1.17.1 h1:NE3C767s2ak2bweCZo3+rdP4U/HoyVXLv/X9f2gPS5g= -github.com/klauspost/compress v1.17.1/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -247,6 +244,7 @@ github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOv github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac= github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -302,6 +300,9 @@ github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= +github.com/twmb/franz-go v1.20.6 h1:TpQTt4QcixJ1cHEmQGPOERvTzo99s8jAutmS7rbSD6w= +github.com/twmb/franz-go/pkg/kadm v1.17.2 h1:g5f1sAxnTkYC6G96pV5u715HWhxd66hWaDZUAQ8xHY8= +github.com/twmb/franz-go/pkg/kmsg v1.12.0 h1:CbatD7ers1KzDNgJqPbKOq0Bz/WLBdsTH75wgzeVaPc= github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94= github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ= github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= @@ -353,8 +354,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -367,8 +367,7 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -387,8 +386,7 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -397,8 +395,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -428,8 +425,7 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -442,8 +438,7 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -461,8 +456,7 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -512,8 +506,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= -gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/gotraceui v0.2.0 h1:dmNsfQ9Vl3GwbiVD7Z8d/osC6WtGGrasyrC2suc4ZIQ= honnef.co/go/gotraceui v0.2.0/go.mod h1:qHo4/W75cA3bX0QQoSvDjbJa4R8mAyyFjbWAj63XElc= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/services/gateway/handlers/static.go b/services/gateway/handlers/static.go index 2f45c9c..512923a 100644 --- a/services/gateway/handlers/static.go +++ b/services/gateway/handlers/static.go @@ -3,7 +3,7 @@ package handlers import ( "net/http" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" ) // DocsHandler serves API docs for the gateway diff --git a/services/gateway/handlers/static_test.go b/services/gateway/handlers/static_test.go index 6c13d14..af93838 100644 --- a/services/gateway/handlers/static_test.go +++ b/services/gateway/handlers/static_test.go @@ -5,11 +5,11 @@ import ( "os" "testing" - "gateway/handlers" + "github.com/fiskerinc/cloud-services/services/gateway/handlers" - "fiskerinc.com/modules/httpclient/tester" - "fiskerinc.com/modules/testhelper" - "fiskerinc.com/modules/testrunner" + "github.com/fiskerinc/cloud-services/pkg/httpclient/tester" + "github.com/fiskerinc/cloud-services/pkg/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testrunner" ) func TestDocsHandler(t *testing.T) { diff --git a/services/gateway/handlers/websocket.go b/services/gateway/handlers/websocket.go index 120f3e3..9590666 100644 --- a/services/gateway/handlers/websocket.go +++ b/services/gateway/handlers/websocket.go @@ -4,11 +4,11 @@ import ( "context" "net/http" - "gateway/services" + "github.com/fiskerinc/cloud-services/services/gateway/services" - "gateway/websocket" + "github.com/fiskerinc/cloud-services/services/gateway/websocket" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) diff --git a/services/gateway/main.go b/services/gateway/main.go index 8621bcb..f07e967 100644 --- a/services/gateway/main.go +++ b/services/gateway/main.go @@ -3,14 +3,14 @@ package main import ( "context" - "gateway/controllers" - "gateway/server" - "gateway/services" - "gateway/sloppy" + "github.com/fiskerinc/cloud-services/services/gateway/controllers" + "github.com/fiskerinc/cloud-services/services/gateway/server" + "github.com/fiskerinc/cloud-services/services/gateway/services" + "github.com/fiskerinc/cloud-services/services/gateway/sloppy" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/tracer" - "fiskerinc.com/modules/utils/app" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/tracer" + "github.com/fiskerinc/cloud-services/pkg/utils/app" ) func init() { diff --git a/services/gateway/server/server_http.go b/services/gateway/server/server_http.go index bab2be2..df5d677 100644 --- a/services/gateway/server/server_http.go +++ b/services/gateway/server/server_http.go @@ -4,10 +4,10 @@ import ( "context" "net/http" - "gateway/handlers" + "github.com/fiskerinc/cloud-services/services/gateway/handlers" - "fiskerinc.com/modules/httphandlers" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/httphandlers" + "github.com/fiskerinc/cloud-services/pkg/logger" httptrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http" ) diff --git a/services/gateway/server/server_redis.go b/services/gateway/server/server_redis.go index 4f70aa3..b761549 100644 --- a/services/gateway/server/server_redis.go +++ b/services/gateway/server/server_redis.go @@ -6,9 +6,9 @@ import ( "io" "time" - "gateway/services" + "github.com/fiskerinc/cloud-services/services/gateway/services" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/logger" ) func logRedisErr(err error) { diff --git a/services/gateway/services/connections.go b/services/gateway/services/connections.go index b8edc29..ff0409b 100644 --- a/services/gateway/services/connections.go +++ b/services/gateway/services/connections.go @@ -3,10 +3,10 @@ package services import ( "sync" - "gateway/websocket" + "github.com/fiskerinc/cloud-services/services/gateway/websocket" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/logger" ) var connections *websocket.Connections diff --git a/services/gateway/services/kafka.go b/services/gateway/services/kafka.go index 6cf619b..590a479 100644 --- a/services/gateway/services/kafka.go +++ b/services/gateway/services/kafka.go @@ -4,8 +4,8 @@ import ( "context" "sync" - "fiskerinc.com/modules/kafka" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/kafka" + "github.com/fiskerinc/cloud-services/pkg/logger" ) var producer kafka.ProducerInterface diff --git a/services/gateway/services/redis.go b/services/gateway/services/redis.go index b46f772..bc8f34d 100644 --- a/services/gateway/services/redis.go +++ b/services/gateway/services/redis.go @@ -3,8 +3,8 @@ package services import ( "sync" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redis" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redis" "github.com/pkg/errors" ) diff --git a/services/gateway/sloppy/db.go b/services/gateway/sloppy/db.go index 69ba7d5..f6a2067 100644 --- a/services/gateway/sloppy/db.go +++ b/services/gateway/sloppy/db.go @@ -3,9 +3,9 @@ package sloppy import ( "sync" - "fiskerinc.com/modules/db" - "fiskerinc.com/modules/db/queries" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/db" + "github.com/fiskerinc/cloud-services/pkg/db/queries" + "github.com/fiskerinc/cloud-services/pkg/logger" ) var ( diff --git a/services/gateway/sloppy/vinBlocker.go b/services/gateway/sloppy/vinBlocker.go index f3783d0..e6bc712 100644 --- a/services/gateway/sloppy/vinBlocker.go +++ b/services/gateway/sloppy/vinBlocker.go @@ -5,8 +5,8 @@ import ( "sync" "time" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils/whereami" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils/whereami" ) var vinblocker *VINBlocker diff --git a/services/gateway/websocket/auth.go b/services/gateway/websocket/auth.go index 93a014e..175a923 100644 --- a/services/gateway/websocket/auth.go +++ b/services/gateway/websocket/auth.go @@ -4,9 +4,9 @@ import ( "fmt" "net/http" - "fiskerinc.com/modules/httpclient" - "fiskerinc.com/modules/jwt" - "fiskerinc.com/modules/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/httpclient" + "github.com/fiskerinc/cloud-services/pkg/jwt" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/pkg/errors" ) diff --git a/services/gateway/websocket/auth_test.go b/services/gateway/websocket/auth_test.go index cfd098d..02007ed 100644 --- a/services/gateway/websocket/auth_test.go +++ b/services/gateway/websocket/auth_test.go @@ -4,9 +4,9 @@ import ( "net/http" "testing" - "fiskerinc.com/modules/httpclient" - "fiskerinc.com/modules/httpclient/mock" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/httpclient" + "github.com/fiskerinc/cloud-services/pkg/httpclient/mock" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestVerifyTokenAuthorized(t *testing.T) { diff --git a/services/gateway/websocket/connections.go b/services/gateway/websocket/connections.go index 9c1cad6..8e4210c 100644 --- a/services/gateway/websocket/connections.go +++ b/services/gateway/websocket/connections.go @@ -5,9 +5,9 @@ import ( "encoding/json" "sync" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/scheduler" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/scheduler" "github.com/pkg/errors" "github.com/robfig/cron" ) diff --git a/services/gateway/websocket/connections_test.go b/services/gateway/websocket/connections_test.go index 5091d1a..18164a7 100644 --- a/services/gateway/websocket/connections_test.go +++ b/services/gateway/websocket/connections_test.go @@ -7,9 +7,9 @@ import ( "testing" "time" - kafka "fiskerinc.com/modules/kafka/mock" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/redis" + kafka "github.com/fiskerinc/cloud-services/pkg/kafka/mock" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/redis" ) func AddRemoveRedisListeners(add bool, id string, pubsub *redis.PubSub, queueRef *redis.Queues) { diff --git a/services/gateway/websocket/helpers_test.go b/services/gateway/websocket/helpers_test.go index eba9f41..a1288b1 100644 --- a/services/gateway/websocket/helpers_test.go +++ b/services/gateway/websocket/helpers_test.go @@ -4,9 +4,9 @@ import ( "net/http" "testing" - "gateway/websocket" + "github.com/fiskerinc/cloud-services/services/gateway/websocket" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/testhelper" ) func TestParseDeviceAndVersionFromRequest(t *testing.T) { diff --git a/services/gateway/websocket/session.go b/services/gateway/websocket/session.go index d4e874c..1a973b3 100644 --- a/services/gateway/websocket/session.go +++ b/services/gateway/websocket/session.go @@ -5,21 +5,21 @@ import ( "context" "encoding/json" "fmt" - "gateway/sloppy" + "github.com/fiskerinc/cloud-services/services/gateway/sloppy" "io" "net" "net/http" "strings" "time" - "fiskerinc.com/modules/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/kafka" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/utils" - "fiskerinc.com/modules/utils/envtool" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/kafka" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/utils" + "github.com/fiskerinc/cloud-services/pkg/utils/envtool" + "github.com/fiskerinc/cloud-services/pkg/validator" "google.golang.org/protobuf/proto" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" diff --git a/services/gateway/websocket/session_hmi.go b/services/gateway/websocket/session_hmi.go index ed9c6bc..cbc1297 100644 --- a/services/gateway/websocket/session_hmi.go +++ b/services/gateway/websocket/session_hmi.go @@ -7,12 +7,12 @@ import ( "net/http" "time" - "fiskerinc.com/modules/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/kafka" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/security" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/kafka" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/security" "github.com/gobwas/ws" "github.com/pkg/errors" "google.golang.org/protobuf/proto" diff --git a/services/gateway/websocket/session_hmi_test.go b/services/gateway/websocket/session_hmi_test.go index da1136c..fb3ac27 100644 --- a/services/gateway/websocket/session_hmi_test.go +++ b/services/gateway/websocket/session_hmi_test.go @@ -9,9 +9,9 @@ import ( "net/http/httptest" "testing" - "fiskerinc.com/modules/common" - kafka "fiskerinc.com/modules/kafka/mock" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + kafka "github.com/fiskerinc/cloud-services/pkg/kafka/mock" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/gobwas/ws" "github.com/gobwas/ws/wsutil" diff --git a/services/gateway/websocket/session_mobile.go b/services/gateway/websocket/session_mobile.go index 4e99524..a2de445 100644 --- a/services/gateway/websocket/session_mobile.go +++ b/services/gateway/websocket/session_mobile.go @@ -6,11 +6,11 @@ import ( "net/http" "time" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/jwt" - "fiskerinc.com/modules/kafka" - "fiskerinc.com/modules/logger" - "fiskerinc.com/modules/validator" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/jwt" + "github.com/fiskerinc/cloud-services/pkg/kafka" + "github.com/fiskerinc/cloud-services/pkg/logger" + "github.com/fiskerinc/cloud-services/pkg/validator" "google.golang.org/protobuf/proto" "github.com/gobwas/ws" diff --git a/services/gateway/websocket/session_mobile_test.go b/services/gateway/websocket/session_mobile_test.go index 2b12821..835f4b0 100644 --- a/services/gateway/websocket/session_mobile_test.go +++ b/services/gateway/websocket/session_mobile_test.go @@ -8,8 +8,8 @@ import ( "net/http/httptest" "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/gobwas/ws" "github.com/gobwas/ws/wsutil" diff --git a/services/gateway/websocket/session_test.go b/services/gateway/websocket/session_test.go index 251e4c6..096f95f 100644 --- a/services/gateway/websocket/session_test.go +++ b/services/gateway/websocket/session_test.go @@ -11,11 +11,11 @@ import ( "testing" "time" - m "fiskerinc.com/modules/common" - "fiskerinc.com/modules/httpclient" - "fiskerinc.com/modules/httpclient/mock" - kafka "fiskerinc.com/modules/kafka/mock" - "fiskerinc.com/modules/testhelper" + m "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/httpclient" + "github.com/fiskerinc/cloud-services/pkg/httpclient/mock" + kafka "github.com/fiskerinc/cloud-services/pkg/kafka/mock" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/gobwas/httphead" "github.com/gobwas/ws" diff --git a/services/gateway/websocket/session_trex.go b/services/gateway/websocket/session_trex.go index d184b95..d8ff60d 100644 --- a/services/gateway/websocket/session_trex.go +++ b/services/gateway/websocket/session_trex.go @@ -6,13 +6,13 @@ import ( "net/http" "time" - "fiskerinc.com/modules/dbc/models" + "github.com/fiskerinc/cloud-services/pkg/dbc/models" "google.golang.org/protobuf/proto" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/grpc/kafka_grpc" - "fiskerinc.com/modules/kafka" - "fiskerinc.com/modules/logger" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" + "github.com/fiskerinc/cloud-services/pkg/kafka" + "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/gobwas/ws" "github.com/gobwas/ws/wsflate" diff --git a/services/gateway/websocket/session_trex_test.go b/services/gateway/websocket/session_trex_test.go index 72f6012..2100dc0 100644 --- a/services/gateway/websocket/session_trex_test.go +++ b/services/gateway/websocket/session_trex_test.go @@ -9,10 +9,10 @@ import ( "net/http/httptest" "testing" - "fiskerinc.com/modules/common" - "fiskerinc.com/modules/grpc/kafka_grpc" - kafka "fiskerinc.com/modules/kafka/mock" - "fiskerinc.com/modules/testhelper" + "github.com/fiskerinc/cloud-services/pkg/common" + "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" + kafka "github.com/fiskerinc/cloud-services/pkg/kafka/mock" + "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/gobwas/ws" "github.com/gobwas/ws/wsutil"