mirror of
https://github.com/kevin-DL/services.git
synced 2026-01-11 19:04:35 +00:00
EV Chargers service (#219)
This commit is contained in:
2
evchargers/.gitignore
vendored
Normal file
2
evchargers/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
evchargers
|
||||
3
evchargers/Dockerfile
Normal file
3
evchargers/Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM alpine
|
||||
ADD evchargers /evchargers
|
||||
ENTRYPOINT [ "/evchargers" ]
|
||||
28
evchargers/Makefile
Normal file
28
evchargers/Makefile
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
GOPATH:=$(shell go env GOPATH)
|
||||
.PHONY: init
|
||||
init:
|
||||
go get -u github.com/golang/protobuf/proto
|
||||
go get -u github.com/golang/protobuf/protoc-gen-go
|
||||
go get github.com/micro/micro/v3/cmd/protoc-gen-micro
|
||||
go get github.com/micro/micro/v3/cmd/protoc-gen-openapi
|
||||
|
||||
.PHONY: api
|
||||
api:
|
||||
protoc --openapi_out=. --proto_path=. proto/evchargers.proto
|
||||
|
||||
.PHONY: proto
|
||||
proto:
|
||||
protoc --proto_path=. --micro_out=. --go_out=:. proto/evchargers.proto
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build -o evchargers *.go
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -v ./... -cover
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
docker build . -t evchargers:latest
|
||||
11
evchargers/README.md
Normal file
11
evchargers/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
Find electric vehicle (EV) chargers wherever you go
|
||||
|
||||
# EV Chargers Service
|
||||
|
||||
This is the E(lectric) V(ehicle) chargers API. Search for EV chargers using
|
||||
- location and distance
|
||||
- bounding box
|
||||
|
||||
and filter the results based on connection type, operator, and more.
|
||||
|
||||
Powered by https://openchargemap.org/
|
||||
643
evchargers/examples.json
Normal file
643
evchargers/examples.json
Normal file
@@ -0,0 +1,643 @@
|
||||
{
|
||||
"search": [
|
||||
{
|
||||
"title": "Search by location",
|
||||
"run_check": true,
|
||||
"request": {
|
||||
"location": {
|
||||
"latitude": 51.53336351319885,
|
||||
"longitude": -0.0252
|
||||
},
|
||||
"distance": 2000,
|
||||
"max_results": 2
|
||||
},
|
||||
"response": {
|
||||
"pois": [
|
||||
{
|
||||
"id": "121442",
|
||||
"data_provider_id": "1",
|
||||
"operator_id": "25",
|
||||
"usage_type_id": "4",
|
||||
"address": {
|
||||
"location": {
|
||||
"latitude": 51.539715,
|
||||
"longitude": -0.02769
|
||||
},
|
||||
"title": "St Marks Gate",
|
||||
"address_line_1": "Opposite 1 St Marks Gate",
|
||||
"address_line_2": "",
|
||||
"town": "London",
|
||||
"state_or_province": "",
|
||||
"access_comments": "",
|
||||
"postcode": "E9 5HT",
|
||||
"country_id": "1",
|
||||
"country": {
|
||||
"id": "1",
|
||||
"title": "United Kingdom",
|
||||
"iso_code": "GB",
|
||||
"continent_code": "EU"
|
||||
}
|
||||
},
|
||||
"connections": [
|
||||
{
|
||||
"connection_type_id": "1",
|
||||
"reference": "",
|
||||
"level": "2",
|
||||
"amps": 16,
|
||||
"voltage": 230,
|
||||
"power": 4,
|
||||
"current": "10",
|
||||
"connection_type": {
|
||||
"id": "1",
|
||||
"title": "Type 1 (J1772)",
|
||||
"formal_name": "SAE J1772-2009",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"connection_type_id": "25",
|
||||
"reference": "",
|
||||
"level": "2",
|
||||
"amps": 32,
|
||||
"voltage": 230,
|
||||
"power": 7,
|
||||
"current": "10",
|
||||
"connection_type": {
|
||||
"id": "25",
|
||||
"title": "Type 2 (Socket Only)",
|
||||
"formal_name": "IEC 62196-2 Type 2",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"connection_type_id": "1",
|
||||
"reference": "",
|
||||
"level": "2",
|
||||
"amps": 16,
|
||||
"voltage": 230,
|
||||
"power": 4,
|
||||
"current": "10",
|
||||
"connection_type": {
|
||||
"id": "1",
|
||||
"title": "Type 1 (J1772)",
|
||||
"formal_name": "SAE J1772-2009",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"connection_type_id": "25",
|
||||
"reference": "",
|
||||
"level": "2",
|
||||
"amps": 32,
|
||||
"voltage": 230,
|
||||
"power": 7,
|
||||
"current": "10",
|
||||
"connection_type": {
|
||||
"id": "25",
|
||||
"title": "Type 2 (Socket Only)",
|
||||
"formal_name": "IEC 62196-2 Type 2",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"connection_type_id": "1",
|
||||
"reference": "",
|
||||
"level": "2",
|
||||
"amps": 16,
|
||||
"voltage": 230,
|
||||
"power": 4,
|
||||
"current": "10",
|
||||
"connection_type": {
|
||||
"id": "1",
|
||||
"title": "Type 1 (J1772)",
|
||||
"formal_name": "SAE J1772-2009",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"connection_type_id": "25",
|
||||
"reference": "",
|
||||
"level": "2",
|
||||
"amps": 32,
|
||||
"voltage": 230,
|
||||
"power": 7,
|
||||
"current": "10",
|
||||
"connection_type": {
|
||||
"id": "25",
|
||||
"title": "Type 2 (Socket Only)",
|
||||
"formal_name": "IEC 62196-2 Type 2",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"num_points": "3",
|
||||
"cost": "£0.059/minute; min £1.18; other tariffs available",
|
||||
"operator": {
|
||||
"id": "25",
|
||||
"title": "Source London",
|
||||
"website": "http://www.sourcelondon.net/",
|
||||
"comments": "Monthly post-payment membership system, with per-minute billing and optional lower tariffs in return for monthly subscription payment.",
|
||||
"is_private_individual": false,
|
||||
"contact_email": "membership@sourcelondon.net",
|
||||
"phone_primary": "",
|
||||
"phone_secondary": "",
|
||||
"fault_report_email": "membership@sourcelondon.net"
|
||||
},
|
||||
"usage_type": {
|
||||
"id": "4",
|
||||
"title": "Public - Membership Required",
|
||||
"is_pay_at_location": false,
|
||||
"is_membership_required": true,
|
||||
"is_access_key_required": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "189330",
|
||||
"data_provider_id": "1",
|
||||
"operator_id": "3296",
|
||||
"usage_type_id": "5",
|
||||
"address": {
|
||||
"location": {
|
||||
"latitude": 51.529694,
|
||||
"longitude": -0.015285153
|
||||
},
|
||||
"title": "McDonald's Bow",
|
||||
"address_line_1": "4 Payne Road",
|
||||
"address_line_2": "",
|
||||
"town": "London",
|
||||
"state_or_province": "England",
|
||||
"access_comments": "",
|
||||
"postcode": "E3 2SP",
|
||||
"country_id": "1",
|
||||
"country": {
|
||||
"id": "1",
|
||||
"title": "United Kingdom",
|
||||
"iso_code": "GB",
|
||||
"continent_code": "EU"
|
||||
}
|
||||
},
|
||||
"connections": [
|
||||
{
|
||||
"connection_type_id": "33",
|
||||
"reference": "",
|
||||
"level": "3",
|
||||
"amps": 0,
|
||||
"voltage": 0,
|
||||
"power": 50,
|
||||
"current": "30",
|
||||
"connection_type": {
|
||||
"id": "33",
|
||||
"title": "CCS (Type 2)",
|
||||
"formal_name": "IEC 62196-3 Configuration FF",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"connection_type_id": "2",
|
||||
"reference": "",
|
||||
"level": "3",
|
||||
"amps": 0,
|
||||
"voltage": 0,
|
||||
"power": 50,
|
||||
"current": "30",
|
||||
"connection_type": {
|
||||
"id": "2",
|
||||
"title": "CHAdeMO",
|
||||
"formal_name": "IEC 62196-3 Configuration AA",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"num_points": "2",
|
||||
"cost": "£0.40/kWh",
|
||||
"operator": {
|
||||
"id": "3296",
|
||||
"title": "InstaVolt Ltd",
|
||||
"website": "http://instavolt.co.uk/",
|
||||
"comments": "Pay-as-you-go, charged per kWh, contactless bank card payment.",
|
||||
"is_private_individual": false,
|
||||
"contact_email": "",
|
||||
"phone_primary": "",
|
||||
"phone_secondary": "",
|
||||
"fault_report_email": ""
|
||||
},
|
||||
"usage_type": {
|
||||
"id": "5",
|
||||
"title": "Public - Pay At Location",
|
||||
"is_pay_at_location": true,
|
||||
"is_membership_required": false,
|
||||
"is_access_key_required": false
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Search by bounding box",
|
||||
"run_check": true,
|
||||
"request": {
|
||||
"box": {
|
||||
"bottom_left": {
|
||||
"latitude":51.52627543859447,
|
||||
"longitude": -0.03635349400295168
|
||||
},
|
||||
"top_right": {
|
||||
"latitude": 51.56717121807993,
|
||||
"longitude":-0.002293530559768285
|
||||
}
|
||||
},
|
||||
"max_results": 2
|
||||
},
|
||||
"response": {
|
||||
"pois": [
|
||||
{
|
||||
"id": "189330",
|
||||
"data_provider_id": "1",
|
||||
"operator_id": "3296",
|
||||
"usage_type_id": "5",
|
||||
"address": {
|
||||
"location": {
|
||||
"latitude": 51.529694,
|
||||
"longitude": -0.015285153
|
||||
},
|
||||
"title": "McDonald's Bow",
|
||||
"address_line_1": "4 Payne Road",
|
||||
"address_line_2": "",
|
||||
"town": "London",
|
||||
"state_or_province": "England",
|
||||
"access_comments": "",
|
||||
"postcode": "E3 2SP",
|
||||
"country_id": "1",
|
||||
"country": {
|
||||
"id": "1",
|
||||
"title": "United Kingdom",
|
||||
"iso_code": "GB",
|
||||
"continent_code": "EU"
|
||||
}
|
||||
},
|
||||
"connections": [
|
||||
{
|
||||
"connection_type_id": "33",
|
||||
"reference": "",
|
||||
"level": "3",
|
||||
"amps": 0,
|
||||
"voltage": 0,
|
||||
"power": 50,
|
||||
"current": "30",
|
||||
"connection_type": {
|
||||
"id": "33",
|
||||
"title": "CCS (Type 2)",
|
||||
"formal_name": "IEC 62196-3 Configuration FF",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"connection_type_id": "2",
|
||||
"reference": "",
|
||||
"level": "3",
|
||||
"amps": 0,
|
||||
"voltage": 0,
|
||||
"power": 50,
|
||||
"current": "30",
|
||||
"connection_type": {
|
||||
"id": "2",
|
||||
"title": "CHAdeMO",
|
||||
"formal_name": "IEC 62196-3 Configuration AA",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"num_points": "2",
|
||||
"cost": "£0.40/kWh",
|
||||
"operator": {
|
||||
"id": "3296",
|
||||
"title": "InstaVolt Ltd",
|
||||
"website": "http://instavolt.co.uk/",
|
||||
"comments": "Pay-as-you-go, charged per kWh, contactless bank card payment.",
|
||||
"is_private_individual": false,
|
||||
"contact_email": "",
|
||||
"phone_primary": "",
|
||||
"phone_secondary": "",
|
||||
"fault_report_email": ""
|
||||
},
|
||||
"usage_type": {
|
||||
"id": "5",
|
||||
"title": "Public - Pay At Location",
|
||||
"is_pay_at_location": true,
|
||||
"is_membership_required": false,
|
||||
"is_access_key_required": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "187765",
|
||||
"data_provider_id": "18",
|
||||
"operator_id": "3",
|
||||
"usage_type_id": "1",
|
||||
"address": {
|
||||
"location": {
|
||||
"latitude": 51.529995,
|
||||
"longitude": -0.009934
|
||||
},
|
||||
"title": "Hunt's Lane",
|
||||
"address_line_1": "Hunt's Lane",
|
||||
"address_line_2": "",
|
||||
"town": "London",
|
||||
"state_or_province": "",
|
||||
"access_comments": "",
|
||||
"postcode": "E15 2QD",
|
||||
"country_id": "1",
|
||||
"country": {
|
||||
"id": "1",
|
||||
"title": "United Kingdom",
|
||||
"iso_code": "GB",
|
||||
"continent_code": "EU"
|
||||
}
|
||||
},
|
||||
"connections": [
|
||||
{
|
||||
"connection_type_id": "25",
|
||||
"reference": "0",
|
||||
"level": "2",
|
||||
"amps": 32,
|
||||
"voltage": 230,
|
||||
"power": 7,
|
||||
"current": "10",
|
||||
"connection_type": {
|
||||
"id": "25",
|
||||
"title": "Type 2 (Socket Only)",
|
||||
"formal_name": "IEC 62196-2 Type 2",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"num_points": "0",
|
||||
"cost": "",
|
||||
"operator": {
|
||||
"id": "3",
|
||||
"title": "POD Point (UK)",
|
||||
"website": "http://www.pod-point.com/",
|
||||
"comments": "Part of Groupe EDF",
|
||||
"is_private_individual": false,
|
||||
"contact_email": "enquiries@pod-point.com",
|
||||
"phone_primary": "020 7247 4114",
|
||||
"phone_secondary": "",
|
||||
"fault_report_email": "enquiries@pod-point.com"
|
||||
},
|
||||
"usage_type": {
|
||||
"id": "1",
|
||||
"title": "Public",
|
||||
"is_pay_at_location": false,
|
||||
"is_membership_required": false,
|
||||
"is_access_key_required": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Search with filters. Fast chargers only",
|
||||
"run_check": true,
|
||||
"request": {
|
||||
"location": {
|
||||
"latitude": 51.53336351319885,
|
||||
"longitude": -0.0252
|
||||
},
|
||||
"distance": 2000,
|
||||
"max_results": 2,
|
||||
"levels": [
|
||||
"3"
|
||||
]
|
||||
},
|
||||
"response": {
|
||||
"pois": [
|
||||
{
|
||||
"id": "189330",
|
||||
"data_provider_id": "1",
|
||||
"operator_id": "3296",
|
||||
"usage_type_id": "5",
|
||||
"address": {
|
||||
"location": {
|
||||
"latitude": 51.529694,
|
||||
"longitude": -0.015285153
|
||||
},
|
||||
"title": "McDonald's Bow",
|
||||
"address_line_1": "4 Payne Road",
|
||||
"address_line_2": "",
|
||||
"town": "London",
|
||||
"state_or_province": "England",
|
||||
"access_comments": "",
|
||||
"postcode": "E3 2SP",
|
||||
"country_id": "1",
|
||||
"country": {
|
||||
"id": "1",
|
||||
"title": "United Kingdom",
|
||||
"iso_code": "GB",
|
||||
"continent_code": "EU"
|
||||
}
|
||||
},
|
||||
"connections": [
|
||||
{
|
||||
"connection_type_id": "33",
|
||||
"reference": "",
|
||||
"level": "3",
|
||||
"amps": 0,
|
||||
"voltage": 0,
|
||||
"power": 50,
|
||||
"current": "30",
|
||||
"connection_type": {
|
||||
"id": "33",
|
||||
"title": "CCS (Type 2)",
|
||||
"formal_name": "IEC 62196-3 Configuration FF",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"connection_type_id": "2",
|
||||
"reference": "",
|
||||
"level": "3",
|
||||
"amps": 0,
|
||||
"voltage": 0,
|
||||
"power": 50,
|
||||
"current": "30",
|
||||
"connection_type": {
|
||||
"id": "2",
|
||||
"title": "CHAdeMO",
|
||||
"formal_name": "IEC 62196-3 Configuration AA",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"num_points": "2",
|
||||
"cost": "£0.40/kWh",
|
||||
"operator": {
|
||||
"id": "3296",
|
||||
"title": "InstaVolt Ltd",
|
||||
"website": "http://instavolt.co.uk/",
|
||||
"comments": "Pay-as-you-go, charged per kWh, contactless bank card payment.",
|
||||
"is_private_individual": false,
|
||||
"contact_email": "",
|
||||
"phone_primary": "",
|
||||
"phone_secondary": "",
|
||||
"fault_report_email": ""
|
||||
},
|
||||
"usage_type": {
|
||||
"id": "5",
|
||||
"title": "Public - Pay At Location",
|
||||
"is_pay_at_location": true,
|
||||
"is_membership_required": false,
|
||||
"is_access_key_required": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "127251",
|
||||
"data_provider_id": "1",
|
||||
"operator_id": "32",
|
||||
"usage_type_id": "4",
|
||||
"address": {
|
||||
"location": {
|
||||
"latitude": 51.543983,
|
||||
"longitude": -0.01991
|
||||
},
|
||||
"title": "Queen Elizabeth Olympic Park",
|
||||
"address_line_1": "Queen Elizabeth Olympic Park",
|
||||
"address_line_2": "",
|
||||
"town": "Copper Box Car Park",
|
||||
"state_or_province": "London",
|
||||
"access_comments": "",
|
||||
"postcode": "E20 2ST",
|
||||
"country_id": "1",
|
||||
"country": {
|
||||
"id": "1",
|
||||
"title": "United Kingdom",
|
||||
"iso_code": "GB",
|
||||
"continent_code": "EU"
|
||||
}
|
||||
},
|
||||
"connections": [
|
||||
{
|
||||
"connection_type_id": "25",
|
||||
"reference": "",
|
||||
"level": "2",
|
||||
"amps": 32,
|
||||
"voltage": 400,
|
||||
"power": 22,
|
||||
"current": "20",
|
||||
"connection_type": {
|
||||
"id": "25",
|
||||
"title": "Type 2 (Socket Only)",
|
||||
"formal_name": "IEC 62196-2 Type 2",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"connection_type_id": "2",
|
||||
"reference": "",
|
||||
"level": "3",
|
||||
"amps": 0,
|
||||
"voltage": 0,
|
||||
"power": 50,
|
||||
"current": "30",
|
||||
"connection_type": {
|
||||
"id": "2",
|
||||
"title": "CHAdeMO",
|
||||
"formal_name": "IEC 62196-3 Configuration AA",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"connection_type_id": "33",
|
||||
"reference": "",
|
||||
"level": "3",
|
||||
"amps": 0,
|
||||
"voltage": 0,
|
||||
"power": 50,
|
||||
"current": "30",
|
||||
"connection_type": {
|
||||
"id": "33",
|
||||
"title": "CCS (Type 2)",
|
||||
"formal_name": "IEC 62196-3 Configuration FF",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"connection_type_id": "1036",
|
||||
"reference": "",
|
||||
"level": "3",
|
||||
"amps": 0,
|
||||
"voltage": 0,
|
||||
"power": 43,
|
||||
"current": "20",
|
||||
"connection_type": {
|
||||
"id": "1036",
|
||||
"title": "Type 2 (Tethered Connector) ",
|
||||
"formal_name": "IEC 62196-2",
|
||||
"is_discontinued": false,
|
||||
"is_obsolete": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"num_points": "2",
|
||||
"cost": "Inclusive; for Polar Plus subscription members only",
|
||||
"operator": {
|
||||
"id": "32",
|
||||
"title": "BP Pulse",
|
||||
"website": "https://www.bppulse.co.uk/",
|
||||
"comments": "Formerly known as Polar Network, or BP Chargemaster",
|
||||
"is_private_individual": false,
|
||||
"contact_email": "polarenquiries@chargemasterplc.com",
|
||||
"phone_primary": "01582 400331",
|
||||
"phone_secondary": "",
|
||||
"fault_report_email": "polarenquiries@chargemasterplc.com"
|
||||
},
|
||||
"usage_type": {
|
||||
"id": "4",
|
||||
"title": "Public - Membership Required",
|
||||
"is_pay_at_location": false,
|
||||
"is_membership_required": true,
|
||||
"is_access_key_required": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"referenceData": [
|
||||
{
|
||||
"title": "Get reference data",
|
||||
"run_check": true,
|
||||
"request": {
|
||||
},
|
||||
"response": {
|
||||
"charger_types": [],
|
||||
"connection_types": [],
|
||||
"current_types": [],
|
||||
"countries": [],
|
||||
"data_providers": [],
|
||||
"operators": [],
|
||||
"status_types": [],
|
||||
"submission_status_types": [],
|
||||
"usage_types": [],
|
||||
"usage_types": [],
|
||||
"user_comment_types": [],
|
||||
"checkin_status_types": []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
2
evchargers/generate.go
Normal file
2
evchargers/generate.go
Normal file
@@ -0,0 +1,2 @@
|
||||
package main
|
||||
//go:generate make proto
|
||||
100
evchargers/handler/data.go
Normal file
100
evchargers/handler/data.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/micro/micro/v3/service/logger"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func (e *Evchargers) loadPOIData(r io.Reader) (int, error) {
|
||||
logger.Infof("Loading reference data")
|
||||
dec := json.NewDecoder(r)
|
||||
t, err := dec.Token()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d, ok := t.(json.Delim)
|
||||
if !ok || d.String() != "[" {
|
||||
return 0, fmt.Errorf("unexpected token %v %+v", ok, t)
|
||||
}
|
||||
ctx := context.Background()
|
||||
count := 0
|
||||
for dec.More() {
|
||||
// process each item in json array and insert into mongodb
|
||||
var p Poi
|
||||
if err := dec.Decode(&p); err != nil {
|
||||
return 0, fmt.Errorf("error unmarshalling charger %s", err)
|
||||
}
|
||||
if len(p.SpatialPosition.Type) == 0 {
|
||||
// blank so reconstruct
|
||||
p.SpatialPosition.Type = "Point"
|
||||
// long, lat not lat, long
|
||||
p.SpatialPosition.Coordinates = []float64{p.Address.Longitude, p.Address.Latitude}
|
||||
}
|
||||
|
||||
t := true
|
||||
_, err := e.mdb.Database("ocm").Collection("poi").ReplaceOne(ctx, bson.D{bson.E{"ID", p.ID}}, p, &options.ReplaceOptions{Upsert: &t})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
count++
|
||||
}
|
||||
return count, nil
|
||||
|
||||
}
|
||||
|
||||
func (e *Evchargers) loadRefData(r io.Reader) error {
|
||||
dec := json.NewDecoder(r)
|
||||
var rd ReferenceData
|
||||
if err := dec.Decode(&rd); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx := context.Background()
|
||||
t := true
|
||||
_, err := e.mdb.Database("ocm").Collection("reference").ReplaceOne(ctx, bson.D{bson.E{"_id", 1}}, rd, &options.ReplaceOptions{Upsert: &t})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Evchargers) refreshDataFromSource() {
|
||||
start := time.Now()
|
||||
logger.Infof("Refreshing data")
|
||||
logger.Infof("Retrieving poi data")
|
||||
rsp, err := http.Get(fmt.Sprintf("https://api.openchargemap.io/v3/poi/?output=json&key=%s&maxresults=10000000", e.conf.OCMKey))
|
||||
if err != nil {
|
||||
logger.Errorf("Error refreshing data %s", err)
|
||||
return
|
||||
}
|
||||
defer rsp.Body.Close()
|
||||
c, err := e.loadPOIData(rsp.Body)
|
||||
if err != nil {
|
||||
logger.Errorf("Error loading data %s", err)
|
||||
return
|
||||
}
|
||||
logger.Infof("Updated %v items of POI data. Took %s", c, time.Since(start))
|
||||
|
||||
start = time.Now()
|
||||
logger.Infof("Retrieving ref data")
|
||||
rsp2, err := http.Get(fmt.Sprintf("https://api.openchargemap.io/v3/referencedata/?output=json&key=%s", e.conf.OCMKey))
|
||||
if err != nil {
|
||||
logger.Errorf("Error refreshing reference data %s", err)
|
||||
return
|
||||
}
|
||||
defer rsp2.Body.Close()
|
||||
if err := e.loadRefData(rsp2.Body); err != nil {
|
||||
logger.Errorf("Error loading reference data %s", err)
|
||||
return
|
||||
}
|
||||
logger.Infof("Updated ref data. Took %s", time.Since(start))
|
||||
|
||||
}
|
||||
55
evchargers/handler/data_test.go
Normal file
55
evchargers/handler/data_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func TestDataLoad(t *testing.T) {
|
||||
t.SkipNow()
|
||||
s := Evchargers{}
|
||||
opts := []*options.ClientOptions{options.Client().ApplyURI("mongodb://127.0.0.1:27017/ocm")}
|
||||
var err error
|
||||
s.mdb, err = mongo.Connect(context.Background(), opts...)
|
||||
if err != nil {
|
||||
t.Fatalf("Error connecting to mongo %s", err)
|
||||
}
|
||||
var r io.Reader
|
||||
r, err = os.Open("test.json")
|
||||
if err != nil {
|
||||
t.Fatalf("Error opening test data %s", err)
|
||||
}
|
||||
c, err := s.loadPOIData(r)
|
||||
if err != nil {
|
||||
t.Fatalf("Err loading data %s", err)
|
||||
}
|
||||
if c != 2 {
|
||||
t.Errorf("Incorrect number of records %d", c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefDataLoad(t *testing.T) {
|
||||
t.SkipNow()
|
||||
s := Evchargers{}
|
||||
opts := []*options.ClientOptions{options.Client().ApplyURI("mongodb://127.0.0.1:27017/ocm")}
|
||||
var err error
|
||||
s.mdb, err = mongo.Connect(context.Background(), opts...)
|
||||
if err != nil {
|
||||
t.Fatalf("Error connecting to mongo %s", err)
|
||||
}
|
||||
var r io.Reader
|
||||
r, err = os.Open("test-reference.json")
|
||||
if err != nil {
|
||||
t.Fatalf("Error opening test data %s", err)
|
||||
}
|
||||
err = s.loadRefData(r)
|
||||
if err != nil {
|
||||
t.Fatalf("Err loading data %s", err)
|
||||
}
|
||||
|
||||
}
|
||||
438
evchargers/handler/evchargers.go
Normal file
438
evchargers/handler/evchargers.go
Normal file
@@ -0,0 +1,438 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/micro/micro/v3/service/errors"
|
||||
"github.com/robfig/cron/v3"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"github.com/micro/micro/v3/service/config"
|
||||
log "github.com/micro/micro/v3/service/logger"
|
||||
|
||||
evchargers "github.com/micro/services/evchargers/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultDistance = int64(5000) // 5km
|
||||
|
||||
)
|
||||
|
||||
var (
|
||||
sphereIndexVersion = int32(3)
|
||||
)
|
||||
|
||||
type Evchargers struct {
|
||||
conf conf
|
||||
mdb *mongo.Client
|
||||
}
|
||||
|
||||
type conf struct {
|
||||
MongoHost string `json:"mongo_host"`
|
||||
CaCrt string `json:"ca_crt"`
|
||||
OCMKey string `json:"ocm_key"`
|
||||
}
|
||||
|
||||
func New() *Evchargers {
|
||||
val, err := config.Get("micro.evchargers")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load config")
|
||||
}
|
||||
var conf conf
|
||||
if err := val.Scan(&conf); err != nil {
|
||||
log.Fatalf("Failed to load config")
|
||||
}
|
||||
if len(conf.MongoHost) == 0 {
|
||||
log.Fatalf("Missing mongodb host")
|
||||
}
|
||||
if len(conf.CaCrt) > 0 {
|
||||
// write the cert to file
|
||||
if err := ioutil.WriteFile(os.TempDir()+"/mongo.crt", []byte(conf.CaCrt), 0644); err != nil {
|
||||
log.Fatalf("Failed to write crt file for mongodb %s", err)
|
||||
}
|
||||
}
|
||||
opts := []*options.ClientOptions{options.Client().ApplyURI(conf.MongoHost)}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
client, err := mongo.Connect(ctx, opts...)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to mongo db %s", err)
|
||||
}
|
||||
ctx2, cancel2 := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel2()
|
||||
if err := client.Ping(ctx2, nil); err != nil {
|
||||
log.Fatalf("Failed to ping mongo db %s", err)
|
||||
}
|
||||
|
||||
// make sure the indexes are set up
|
||||
_, err = client.Database("ocm").Collection("poi").Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{ // bounding box queries
|
||||
Keys: bson.D{{"SpatialPosition.coordinates", "2dsphere"}},
|
||||
Options: &options.IndexOptions{
|
||||
SphereVersion: &sphereIndexVersion,
|
||||
},
|
||||
},
|
||||
{ // distance queries
|
||||
Keys: bson.D{{"SpatialPosition", "2dsphere"}},
|
||||
Options: &options.IndexOptions{
|
||||
SphereVersion: &sphereIndexVersion,
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.M{"DateCreated": -1},
|
||||
},
|
||||
{
|
||||
Keys: bson.M{"DateLastStatusUpdate": -1},
|
||||
},
|
||||
{
|
||||
Keys: bson.M{"ID": -1},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to craete indexes %s", err)
|
||||
}
|
||||
|
||||
ev := &Evchargers{conf: conf, mdb: client}
|
||||
if len(conf.OCMKey) > 0 {
|
||||
c := cron.New()
|
||||
// 4am every Sunday for refresh
|
||||
c.AddFunc("0 4 * * 0", ev.refreshDataFromSource)
|
||||
c.Start()
|
||||
}
|
||||
|
||||
return ev
|
||||
}
|
||||
|
||||
func (e *Evchargers) Search(ctx context.Context, request *evchargers.SearchRequest, response *evchargers.SearchResponse) error {
|
||||
|
||||
toInt := func(in []string) []interface{} {
|
||||
res := make([]interface{}, len(in))
|
||||
for i, v := range in {
|
||||
res[i], _ = strconv.Atoi(v)
|
||||
}
|
||||
return res
|
||||
}
|
||||
filters := bson.D{}
|
||||
if len(request.ConnectionTypes) > 0 {
|
||||
vals := bson.A{}
|
||||
vals = append(vals, toInt(request.ConnectionTypes)...)
|
||||
filters = append(filters, bson.E{"Connections.ConnectionTypeID", bson.D{{"$in", vals}}})
|
||||
}
|
||||
|
||||
if request.Location != nil {
|
||||
distance := defaultDistance
|
||||
if request.Distance > 0 {
|
||||
distance = request.Distance
|
||||
}
|
||||
filters = append(filters, bson.E{"SpatialPosition", bson.M{"$nearSphere": bson.M{"$geometry": bson.M{
|
||||
"type": "Point",
|
||||
"coordinates": []float64{float64(request.Location.Longitude), float64(request.Location.Latitude)},
|
||||
},
|
||||
"$maxDistance": distance,
|
||||
},
|
||||
}})
|
||||
} else if request.Box != nil && request.Box.BottomLeft != nil {
|
||||
filters = append(filters, bson.E{"SpatialPosition.coordinates", bson.M{"$geoWithin": bson.M{"$box": bson.A{
|
||||
[]float32{request.Box.BottomLeft.Longitude, request.Box.BottomLeft.Latitude},
|
||||
[]float32{request.Box.TopRight.Longitude, request.Box.TopRight.Latitude},
|
||||
}}}})
|
||||
}
|
||||
|
||||
if len(request.CountryId) > 0 {
|
||||
i, _ := strconv.Atoi(request.CountryId)
|
||||
filters = append(filters, bson.E{"AddressInfo.CountryID", i})
|
||||
}
|
||||
|
||||
if len(request.Levels) > 0 {
|
||||
vals := bson.A{}
|
||||
vals = append(vals, toInt(request.Levels)...)
|
||||
filters = append(filters, bson.E{"Connections.LevelID", bson.D{{"$in", vals}}})
|
||||
}
|
||||
|
||||
if request.MinPower > 0 {
|
||||
filters = append(filters, bson.E{"Connections.PowerKW", bson.D{{"$gte", request.MinPower}}})
|
||||
}
|
||||
|
||||
if len(request.Operators) > 0 {
|
||||
vals := bson.A{}
|
||||
vals = append(vals, toInt(request.Operators)...)
|
||||
filters = append(filters, bson.E{"OperatorID", bson.D{{"$in", vals}}})
|
||||
}
|
||||
|
||||
if len(request.UsageTypes) > 0 {
|
||||
vals := bson.A{}
|
||||
vals = append(vals, toInt(request.UsageTypes)...)
|
||||
filters = append(filters, bson.E{"UsageTypeID", bson.D{{"$in", vals}}})
|
||||
}
|
||||
|
||||
maxLim := int64(100)
|
||||
max := options.FindOptions{
|
||||
Limit: &maxLim,
|
||||
}
|
||||
if request.MaxResults > 0 {
|
||||
max.Limit = &request.MaxResults
|
||||
}
|
||||
crs, err := e.mdb.Database("ocm").Collection("poi").Find(ctx, filters, &max)
|
||||
if err != nil {
|
||||
log.Errorf("Error querying %s", err)
|
||||
return errors.InternalServerError("evchargers.search", "Failed to query ev chargers")
|
||||
}
|
||||
defer crs.Close(ctx)
|
||||
for crs.Next(ctx) {
|
||||
var result Poi
|
||||
if err := crs.Decode(&result); err != nil {
|
||||
log.Errorf("Error decoding result %s", err)
|
||||
return errors.InternalServerError("evchargers.search", "Failed to query ev chargers")
|
||||
}
|
||||
poi := &evchargers.Poi{
|
||||
Id: strconv.Itoa(int(result.ID)),
|
||||
DataProviderId: strconv.Itoa(int(result.DataProviderID)),
|
||||
OperatorId: strconv.Itoa(int(result.OperatorID)),
|
||||
UsageTypeId: strconv.Itoa(int(result.UsageTypeID)),
|
||||
Address: &evchargers.Address{
|
||||
Location: &evchargers.Coordinates{
|
||||
Latitude: float32(result.Address.Latitude),
|
||||
Longitude: float32(result.Address.Longitude),
|
||||
},
|
||||
Title: result.Address.Title,
|
||||
AddressLine_1: result.Address.AddressLine1,
|
||||
AddressLine_2: result.Address.AddressLine2,
|
||||
Town: result.Address.Town,
|
||||
StateOrProvince: result.Address.StateOrProvince,
|
||||
AccessComments: result.Address.AccessComments,
|
||||
Postcode: result.Address.Postcode,
|
||||
CountryId: strconv.Itoa(int(result.Address.CountryID)),
|
||||
},
|
||||
Connections: marshalConnections(result.Connections),
|
||||
NumPoints: int64(result.NumberOfPoints),
|
||||
Cost: result.Cost,
|
||||
}
|
||||
if true { // verbose
|
||||
poi.Operator = marshalOperator(result.OperatorInfo)
|
||||
poi.UsageType = marshalUsageType(result.UsageType)
|
||||
poi.Address.Country = marshalCountry(result.Address.Country)
|
||||
}
|
||||
response.Pois = append(response.Pois, poi)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func marshalCountry(in Country) *evchargers.Country {
|
||||
return &evchargers.Country{
|
||||
Id: strconv.Itoa(int(in.ID)),
|
||||
Title: in.Title,
|
||||
IsoCode: in.ISOCode,
|
||||
ContinentCode: in.ContinentCode,
|
||||
}
|
||||
}
|
||||
|
||||
func marshalConnections(in []Connection) []*evchargers.Connection {
|
||||
res := make([]*evchargers.Connection, len(in))
|
||||
for i, v := range in {
|
||||
res[i] = &evchargers.Connection{
|
||||
ConnectionTypeId: strconv.Itoa(int(v.TypeID)),
|
||||
ConnectionType: &evchargers.ConnectionType{
|
||||
Id: strconv.Itoa(int(v.Type.ID)),
|
||||
Title: v.Type.Title,
|
||||
FormalName: v.Type.FormalName,
|
||||
IsDiscontinued: v.Type.IsDiscontinued,
|
||||
IsObsolete: v.Type.IsObsolete,
|
||||
},
|
||||
Reference: v.Reference,
|
||||
Level: strconv.Itoa(int(v.LevelID)),
|
||||
Amps: float32(v.Amps),
|
||||
Voltage: float32(v.Voltage),
|
||||
Power: float32(v.Power),
|
||||
Current: strconv.Itoa(int(v.CurrentTypeID)),
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func marshalDataProvider(in DataProvider) *evchargers.DataProvider {
|
||||
return &evchargers.DataProvider{
|
||||
Id: strconv.Itoa(int(in.ID)),
|
||||
Title: in.Title,
|
||||
Website: in.WebsiteURL,
|
||||
Comments: in.Comments,
|
||||
DataProviderStatusType: marshalDataProviderStatus(in.DataProviderStatus),
|
||||
License: in.License,
|
||||
}
|
||||
}
|
||||
|
||||
func marshalDataProviderStatus(in DataProviderStatus) *evchargers.DataProviderStatusType {
|
||||
return &evchargers.DataProviderStatusType{
|
||||
Id: strconv.Itoa(int(in.ID)),
|
||||
Title: in.Title,
|
||||
IsProviderEnabled: in.IsProviderEnabled,
|
||||
}
|
||||
}
|
||||
|
||||
func marshalOperator(in Operator) *evchargers.Operator {
|
||||
return &evchargers.Operator{
|
||||
Id: strconv.Itoa(int(in.ID)),
|
||||
Title: in.Title,
|
||||
Website: in.WebsiteURL,
|
||||
Comments: in.Comments,
|
||||
IsPrivateIndividual: in.IsPrivateIndividual,
|
||||
ContactEmail: in.ContactEmail,
|
||||
PhonePrimary: in.PhonePrimary,
|
||||
PhoneSecondary: in.PhoneSecondary,
|
||||
FaultReportEmail: in.FaultReportEmail,
|
||||
}
|
||||
}
|
||||
|
||||
func marshalUsageType(in UsageType) *evchargers.UsageType {
|
||||
return &evchargers.UsageType{
|
||||
Id: strconv.Itoa(int(in.ID)),
|
||||
Title: in.Title,
|
||||
IsPayAtLocation: in.IsPayAtLocation,
|
||||
IsMembershipRequired: in.IsMembershipRequired,
|
||||
IsAccessKeyRequired: in.IsAccessKeyRequired,
|
||||
}
|
||||
}
|
||||
|
||||
func marshalCheckinStatusType(in CheckinStatusType) *evchargers.CheckinStatusType {
|
||||
return &evchargers.CheckinStatusType{
|
||||
Id: strconv.Itoa(int(in.ID)),
|
||||
Title: in.Title,
|
||||
IsPositive: in.IsPositive,
|
||||
IsAutomated: in.IsAutomatedCheckin,
|
||||
}
|
||||
}
|
||||
|
||||
func marshalUserCommentType(in UserCommentType) *evchargers.UserCommentType {
|
||||
return &evchargers.UserCommentType{
|
||||
Id: strconv.Itoa(int(in.ID)),
|
||||
Title: in.Title,
|
||||
}
|
||||
}
|
||||
|
||||
func marshalStatusType(in StatusType) *evchargers.StatusType {
|
||||
return &evchargers.StatusType{
|
||||
Id: strconv.Itoa(int(in.ID)),
|
||||
Title: in.Title,
|
||||
IsOperational: in.IsOperational,
|
||||
}
|
||||
}
|
||||
|
||||
func marshalCurrentType(in CurrentType) *evchargers.CurrentType {
|
||||
return &evchargers.CurrentType{
|
||||
Id: strconv.Itoa(int(in.ID)),
|
||||
Title: in.Title,
|
||||
Description: in.Description,
|
||||
}
|
||||
}
|
||||
|
||||
func marshalConnectionType(in ConnectionType) *evchargers.ConnectionType {
|
||||
return &evchargers.ConnectionType{
|
||||
Id: strconv.Itoa(int(in.ID)),
|
||||
Title: in.Title,
|
||||
FormalName: in.FormalName,
|
||||
IsDiscontinued: in.IsDiscontinued,
|
||||
IsObsolete: in.IsObsolete,
|
||||
}
|
||||
}
|
||||
|
||||
func marshalChargerType(in ChargerType) *evchargers.ChargerType {
|
||||
return &evchargers.ChargerType{
|
||||
Id: strconv.Itoa(int(in.ID)),
|
||||
Title: in.Title,
|
||||
Comments: in.Comments,
|
||||
IsFastChargeCapable: in.IsFastChargeCapable,
|
||||
}
|
||||
}
|
||||
|
||||
func marshalSubmissionStatusType(in SubmissionStatusType) *evchargers.SubmissionStatusType {
|
||||
return &evchargers.SubmissionStatusType{
|
||||
Id: strconv.Itoa(int(in.ID)),
|
||||
Title: in.Title,
|
||||
IsLive: in.IsLive,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Evchargers) ReferenceData(ctx context.Context, request *evchargers.ReferenceDataRequest, response *evchargers.ReferenceDataResponse) error {
|
||||
|
||||
res := e.mdb.Database("ocm").Collection("reference").FindOne(ctx, bson.D{})
|
||||
if res.Err() != nil {
|
||||
log.Errorf("Error retrieving ref data %s", res.Err())
|
||||
return errors.InternalServerError("evchargers.referencedata", "Error retrieving reference data")
|
||||
}
|
||||
var r ReferenceData
|
||||
if err := res.Decode(&r); err != nil {
|
||||
log.Errorf("Error decoding ref data %s", err)
|
||||
return errors.InternalServerError("evchargers.referencedata", "Error retrieving reference data")
|
||||
}
|
||||
dps := make([]*evchargers.DataProvider, len(r.DataProviders))
|
||||
for i, dp := range r.DataProviders {
|
||||
dps[i] = marshalDataProvider(dp)
|
||||
}
|
||||
response.DataProviders = dps
|
||||
cs := make([]*evchargers.Country, len(r.Countries))
|
||||
for i, c := range r.Countries {
|
||||
cs[i] = marshalCountry(c)
|
||||
}
|
||||
response.Countries = cs
|
||||
|
||||
cst := make([]*evchargers.CheckinStatusType, len(r.CheckinStatusTypes))
|
||||
for i, v := range r.CheckinStatusTypes {
|
||||
cst[i] = marshalCheckinStatusType(v)
|
||||
}
|
||||
response.CheckinStatusTypes = cst
|
||||
|
||||
uct := make([]*evchargers.UserCommentType, len(r.UserCommentTypes))
|
||||
for i, v := range r.UserCommentTypes {
|
||||
uct[i] = marshalUserCommentType(v)
|
||||
}
|
||||
response.UserCommentTypes = uct
|
||||
|
||||
st := make([]*evchargers.StatusType, len(r.StatusTypes))
|
||||
for i, v := range r.StatusTypes {
|
||||
st[i] = marshalStatusType(v)
|
||||
}
|
||||
response.StatusTypes = st
|
||||
|
||||
ut := make([]*evchargers.UsageType, len(r.UsageTypes))
|
||||
for i, v := range r.UsageTypes {
|
||||
ut[i] = marshalUsageType(v)
|
||||
}
|
||||
response.UsageTypes = ut
|
||||
|
||||
ct := make([]*evchargers.CurrentType, len(r.CurrentTypes))
|
||||
for i, v := range r.CurrentTypes {
|
||||
ct[i] = marshalCurrentType(v)
|
||||
}
|
||||
response.CurrentTypes = ct
|
||||
|
||||
connt := make([]*evchargers.ConnectionType, len(r.ConnectionTypes))
|
||||
for i, v := range r.ConnectionTypes {
|
||||
connt[i] = marshalConnectionType(v)
|
||||
}
|
||||
response.ConnectionTypes = connt
|
||||
chrgt := make([]*evchargers.ChargerType, len(r.ChargerTypes))
|
||||
for i, v := range r.ChargerTypes {
|
||||
chrgt[i] = marshalChargerType(v)
|
||||
}
|
||||
response.ChargerTypes = chrgt
|
||||
|
||||
ops := make([]*evchargers.Operator, len(r.Operators))
|
||||
for i, v := range r.Operators {
|
||||
ops[i] = marshalOperator(v)
|
||||
}
|
||||
response.Operators = ops
|
||||
|
||||
sst := make([]*evchargers.SubmissionStatusType, len(r.SubmissionStatusTypes))
|
||||
for i, v := range r.SubmissionStatusTypes {
|
||||
sst[i] = marshalSubmissionStatusType(v)
|
||||
}
|
||||
|
||||
response.SubmissionStatusTypes = sst
|
||||
return nil
|
||||
}
|
||||
9251
evchargers/handler/test-reference.json
Normal file
9251
evchargers/handler/test-reference.json
Normal file
File diff suppressed because it is too large
Load Diff
389
evchargers/handler/test.json
Normal file
389
evchargers/handler/test.json
Normal file
@@ -0,0 +1,389 @@
|
||||
[
|
||||
{
|
||||
"DataProvider": {
|
||||
"WebsiteURL": "http://openchargemap.org",
|
||||
"Comments": null,
|
||||
"DataProviderStatusType": {
|
||||
"IsProviderEnabled": true,
|
||||
"ID": 1,
|
||||
"Title": "Manual Data Entry"
|
||||
},
|
||||
"IsRestrictedEdit": false,
|
||||
"IsOpenDataLicensed": true,
|
||||
"IsApprovedImport": true,
|
||||
"License": "Licensed under Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)",
|
||||
"DateLastImported": null,
|
||||
"ID": 1,
|
||||
"Title": "Open Charge Map Contributors"
|
||||
},
|
||||
"OperatorInfo": {
|
||||
"WebsiteURL": "https://evcharge.online/",
|
||||
"Comments": "Rolec. Pre-payment, minimum topup £5.00",
|
||||
"PhonePrimaryContact": null,
|
||||
"PhoneSecondaryContact": null,
|
||||
"IsPrivateIndividual": false,
|
||||
"AddressInfo": null,
|
||||
"BookingURL": null,
|
||||
"ContactEmail": "Contact@power-portal.co.uk",
|
||||
"FaultReportEmail": "Contact@power-portal.co.uk",
|
||||
"IsRestrictedEdit": false,
|
||||
"ID": 3295,
|
||||
"Title": "evcharge.online"
|
||||
},
|
||||
"UsageType": {
|
||||
"IsPayAtLocation": false,
|
||||
"IsMembershipRequired": false,
|
||||
"IsAccessKeyRequired": false,
|
||||
"ID": 6,
|
||||
"Title": "Private - For Staff, Visitors or Customers"
|
||||
},
|
||||
"StatusType": {
|
||||
"IsOperational": true,
|
||||
"IsUserSelectable": true,
|
||||
"ID": 50,
|
||||
"Title": "Operational"
|
||||
},
|
||||
"SubmissionStatus": {
|
||||
"IsLive": true,
|
||||
"ID": 200,
|
||||
"Title": "Submission Published"
|
||||
},
|
||||
"UserComments": null,
|
||||
"PercentageSimilarity": null,
|
||||
"MediaItems": null,
|
||||
"IsRecentlyVerified": true,
|
||||
"DateLastVerified": "2021-09-27T16:41:00Z",
|
||||
"ID": 189420,
|
||||
"UUID": "E8B7D92B-8E36-43AB-909C-48A87A4BA279",
|
||||
"ParentChargePointID": null,
|
||||
"DataProviderID": 1,
|
||||
"DataProvidersReference": null,
|
||||
"OperatorID": 3295,
|
||||
"OperatorsReference": null,
|
||||
"UsageTypeID": 6,
|
||||
"UsageCost": "Free",
|
||||
"AddressInfo": {
|
||||
"ID": 189779,
|
||||
"Title": "Agrovista Allscott ",
|
||||
"AddressLine1": "Agrovista Amenity",
|
||||
"AddressLine2": "Walcot",
|
||||
"Town": "Wrockwardine",
|
||||
"StateOrProvince": "Telford and Wrekin",
|
||||
"Postcode": "TF6 5DY",
|
||||
"CountryID": 1,
|
||||
"Country": {
|
||||
"ISOCode": "GB",
|
||||
"ContinentCode": "EU",
|
||||
"ID": 1,
|
||||
"Title": "United Kingdom"
|
||||
},
|
||||
"Latitude": 52.70780288629825,
|
||||
"Longitude": -2.5934020726602967,
|
||||
"ContactTelephone1": null,
|
||||
"ContactTelephone2": null,
|
||||
"ContactEmail": null,
|
||||
"AccessComments": null,
|
||||
"RelatedURL": null,
|
||||
"Distance": null,
|
||||
"DistanceUnit": 0
|
||||
},
|
||||
"Connections": [
|
||||
{
|
||||
"ID": 306355,
|
||||
"ConnectionTypeID": 25,
|
||||
"ConnectionType": {
|
||||
"FormalName": "IEC 62196-2 Type 2",
|
||||
"IsDiscontinued": false,
|
||||
"IsObsolete": false,
|
||||
"ID": 25,
|
||||
"Title": "Type 2 (Socket Only)"
|
||||
},
|
||||
"Reference": "UKEV6095",
|
||||
"StatusTypeID": 50,
|
||||
"StatusType": {
|
||||
"IsOperational": true,
|
||||
"IsUserSelectable": true,
|
||||
"ID": 50,
|
||||
"Title": "Operational"
|
||||
},
|
||||
"LevelID": 2,
|
||||
"Level": {
|
||||
"Comments": "Over 2 kW, usually non-domestic socket type",
|
||||
"IsFastChargeCapable": false,
|
||||
"ID": 2,
|
||||
"Title": "Level 2 : Medium (Over 2kW)"
|
||||
},
|
||||
"Amps": 32,
|
||||
"Voltage": 400,
|
||||
"PowerKW": 22.0,
|
||||
"CurrentTypeID": 20,
|
||||
"CurrentType": {
|
||||
"Description": "Alternating Current - Three Phase",
|
||||
"ID": 20,
|
||||
"Title": "AC (Three-Phase)"
|
||||
},
|
||||
"Quantity": 1,
|
||||
"Comments": null
|
||||
},
|
||||
{
|
||||
"ID": 306356,
|
||||
"ConnectionTypeID": 25,
|
||||
"ConnectionType": {
|
||||
"FormalName": "IEC 62196-2 Type 2",
|
||||
"IsDiscontinued": false,
|
||||
"IsObsolete": false,
|
||||
"ID": 25,
|
||||
"Title": "Type 2 (Socket Only)"
|
||||
},
|
||||
"Reference": "UKEV6096",
|
||||
"StatusTypeID": 50,
|
||||
"StatusType": {
|
||||
"IsOperational": true,
|
||||
"IsUserSelectable": true,
|
||||
"ID": 50,
|
||||
"Title": "Operational"
|
||||
},
|
||||
"LevelID": 2,
|
||||
"Level": {
|
||||
"Comments": "Over 2 kW, usually non-domestic socket type",
|
||||
"IsFastChargeCapable": false,
|
||||
"ID": 2,
|
||||
"Title": "Level 2 : Medium (Over 2kW)"
|
||||
},
|
||||
"Amps": 32,
|
||||
"Voltage": 400,
|
||||
"PowerKW": 22.0,
|
||||
"CurrentTypeID": 20,
|
||||
"CurrentType": {
|
||||
"Description": "Alternating Current - Three Phase",
|
||||
"ID": 20,
|
||||
"Title": "AC (Three-Phase)"
|
||||
},
|
||||
"Quantity": 1,
|
||||
"Comments": null
|
||||
},
|
||||
{
|
||||
"ID": 306357,
|
||||
"ConnectionTypeID": 25,
|
||||
"ConnectionType": {
|
||||
"FormalName": "IEC 62196-2 Type 2",
|
||||
"IsDiscontinued": false,
|
||||
"IsObsolete": false,
|
||||
"ID": 25,
|
||||
"Title": "Type 2 (Socket Only)"
|
||||
},
|
||||
"Reference": "UKEV6097",
|
||||
"StatusTypeID": 50,
|
||||
"StatusType": {
|
||||
"IsOperational": true,
|
||||
"IsUserSelectable": true,
|
||||
"ID": 50,
|
||||
"Title": "Operational"
|
||||
},
|
||||
"LevelID": 2,
|
||||
"Level": {
|
||||
"Comments": "Over 2 kW, usually non-domestic socket type",
|
||||
"IsFastChargeCapable": false,
|
||||
"ID": 2,
|
||||
"Title": "Level 2 : Medium (Over 2kW)"
|
||||
},
|
||||
"Amps": 32,
|
||||
"Voltage": 400,
|
||||
"PowerKW": 22.0,
|
||||
"CurrentTypeID": 20,
|
||||
"CurrentType": {
|
||||
"Description": "Alternating Current - Three Phase",
|
||||
"ID": 20,
|
||||
"Title": "AC (Three-Phase)"
|
||||
},
|
||||
"Quantity": 1,
|
||||
"Comments": null
|
||||
},
|
||||
{
|
||||
"ID": 306358,
|
||||
"ConnectionTypeID": 25,
|
||||
"ConnectionType": {
|
||||
"FormalName": "IEC 62196-2 Type 2",
|
||||
"IsDiscontinued": false,
|
||||
"IsObsolete": false,
|
||||
"ID": 25,
|
||||
"Title": "Type 2 (Socket Only)"
|
||||
},
|
||||
"Reference": "UKEV6098",
|
||||
"StatusTypeID": 50,
|
||||
"StatusType": {
|
||||
"IsOperational": true,
|
||||
"IsUserSelectable": true,
|
||||
"ID": 50,
|
||||
"Title": "Operational"
|
||||
},
|
||||
"LevelID": 2,
|
||||
"Level": {
|
||||
"Comments": "Over 2 kW, usually non-domestic socket type",
|
||||
"IsFastChargeCapable": false,
|
||||
"ID": 2,
|
||||
"Title": "Level 2 : Medium (Over 2kW)"
|
||||
},
|
||||
"Amps": 32,
|
||||
"Voltage": 400,
|
||||
"PowerKW": 22.0,
|
||||
"CurrentTypeID": 20,
|
||||
"CurrentType": {
|
||||
"Description": "Alternating Current - Three Phase",
|
||||
"ID": 20,
|
||||
"Title": "AC (Three-Phase)"
|
||||
},
|
||||
"Quantity": 1,
|
||||
"Comments": null
|
||||
}
|
||||
],
|
||||
"NumberOfPoints": 4,
|
||||
"GeneralComments": null,
|
||||
"DatePlanned": null,
|
||||
"DateLastConfirmed": null,
|
||||
"StatusTypeID": 50,
|
||||
"DateLastStatusUpdate": "2021-09-27T16:41:00Z",
|
||||
"MetadataValues": null,
|
||||
"DataQualityLevel": 1,
|
||||
"DateCreated": "2021-09-27T16:41:00Z",
|
||||
"SubmissionStatusTypeID": 200
|
||||
},
|
||||
{
|
||||
"DataProvider": {
|
||||
"WebsiteURL": "http://openchargemap.org",
|
||||
"Comments": null,
|
||||
"DataProviderStatusType": {
|
||||
"IsProviderEnabled": true,
|
||||
"ID": 1,
|
||||
"Title": "Manual Data Entry"
|
||||
},
|
||||
"IsRestrictedEdit": false,
|
||||
"IsOpenDataLicensed": true,
|
||||
"IsApprovedImport": true,
|
||||
"License": "Licensed under Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)",
|
||||
"DateLastImported": null,
|
||||
"ID": 1,
|
||||
"Title": "Open Charge Map Contributors"
|
||||
},
|
||||
"OperatorInfo": {
|
||||
"WebsiteURL": null,
|
||||
"Comments": null,
|
||||
"PhonePrimaryContact": null,
|
||||
"PhoneSecondaryContact": null,
|
||||
"IsPrivateIndividual": null,
|
||||
"AddressInfo": null,
|
||||
"BookingURL": null,
|
||||
"ContactEmail": null,
|
||||
"FaultReportEmail": null,
|
||||
"IsRestrictedEdit": null,
|
||||
"ID": 1,
|
||||
"Title": "(Unknown Operator)"
|
||||
},
|
||||
"UsageType": {
|
||||
"IsPayAtLocation": null,
|
||||
"IsMembershipRequired": null,
|
||||
"IsAccessKeyRequired": null,
|
||||
"ID": 1,
|
||||
"Title": "Public"
|
||||
},
|
||||
"StatusType": {
|
||||
"IsOperational": true,
|
||||
"IsUserSelectable": true,
|
||||
"ID": 50,
|
||||
"Title": "Operational"
|
||||
},
|
||||
"SubmissionStatus": {
|
||||
"IsLive": true,
|
||||
"ID": 200,
|
||||
"Title": "Submission Published"
|
||||
},
|
||||
"UserComments": null,
|
||||
"PercentageSimilarity": null,
|
||||
"MediaItems": null,
|
||||
"IsRecentlyVerified": true,
|
||||
"DateLastVerified": "2021-09-27T15:13:00Z",
|
||||
"ID": 189419,
|
||||
"UUID": "2A489FF2-CF7B-41D8-979D-08A9A251568C",
|
||||
"ParentChargePointID": null,
|
||||
"DataProviderID": 1,
|
||||
"DataProvidersReference": null,
|
||||
"OperatorID": 1,
|
||||
"OperatorsReference": null,
|
||||
"UsageTypeID": 1,
|
||||
"UsageCost": "FREE",
|
||||
"AddressInfo": {
|
||||
"ID": 189778,
|
||||
"Title": "CRMU EV Station",
|
||||
"AddressLine1": "Main Street",
|
||||
"AddressLine2": null,
|
||||
"Town": "Coon Rapids",
|
||||
"StateOrProvince": "Iowa",
|
||||
"Postcode": "50059",
|
||||
"CountryID": 2,
|
||||
"Country": {
|
||||
"ISOCode": "US",
|
||||
"ContinentCode": "NA",
|
||||
"ID": 2,
|
||||
"Title": "United States"
|
||||
},
|
||||
"Latitude": 41.870824375016525,
|
||||
"Longitude": -94.67421942978399,
|
||||
"ContactTelephone1": null,
|
||||
"ContactTelephone2": null,
|
||||
"ContactEmail": null,
|
||||
"AccessComments": null,
|
||||
"RelatedURL": null,
|
||||
"Distance": null,
|
||||
"DistanceUnit": 0
|
||||
},
|
||||
"Connections": [
|
||||
{
|
||||
"ID": 306345,
|
||||
"ConnectionTypeID": 1,
|
||||
"ConnectionType": {
|
||||
"FormalName": "SAE J1772-2009",
|
||||
"IsDiscontinued": null,
|
||||
"IsObsolete": null,
|
||||
"ID": 1,
|
||||
"Title": "Type 1 (J1772)"
|
||||
},
|
||||
"Reference": null,
|
||||
"StatusTypeID": 50,
|
||||
"StatusType": {
|
||||
"IsOperational": true,
|
||||
"IsUserSelectable": true,
|
||||
"ID": 50,
|
||||
"Title": "Operational"
|
||||
},
|
||||
"LevelID": 2,
|
||||
"Level": {
|
||||
"Comments": "Over 2 kW, usually non-domestic socket type",
|
||||
"IsFastChargeCapable": false,
|
||||
"ID": 2,
|
||||
"Title": "Level 2 : Medium (Over 2kW)"
|
||||
},
|
||||
"Amps": 30,
|
||||
"Voltage": 239,
|
||||
"PowerKW": 6.6,
|
||||
"CurrentTypeID": 10,
|
||||
"CurrentType": {
|
||||
"Description": "Alternating Current - Single Phase",
|
||||
"ID": 10,
|
||||
"Title": "AC (Single-Phase)"
|
||||
},
|
||||
"Quantity": 2,
|
||||
"Comments": null
|
||||
}
|
||||
],
|
||||
"NumberOfPoints": 2,
|
||||
"GeneralComments": null,
|
||||
"DatePlanned": null,
|
||||
"DateLastConfirmed": null,
|
||||
"StatusTypeID": 50,
|
||||
"DateLastStatusUpdate": "2021-09-27T16:06:00Z",
|
||||
"MetadataValues": null,
|
||||
"DataQualityLevel": 1,
|
||||
"DateCreated": "2021-09-27T15:13:00Z",
|
||||
"SubmissionStatusTypeID": 200
|
||||
}
|
||||
]
|
||||
157
evchargers/handler/types.go
Normal file
157
evchargers/handler/types.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package handler
|
||||
|
||||
type Poi struct {
|
||||
ID int32 `bson:"ID" json:"ID"`
|
||||
DataProviderID int32 `bson:"DataProviderID" json:"DataProviderID"`
|
||||
DataProvider DataProvider `bson:"DataProvider" json:"DataProvider"`
|
||||
OperatorID int32 `bson:"OperatorID" json:"OperatorID"`
|
||||
OperatorInfo Operator `bson:"OperatorInfo" json:"OperatorInfo"`
|
||||
UsageTypeID int32 `bson:"UsageTypeID" json:"UsageTypeID"`
|
||||
UsageType UsageType `bson:"UsageType" json:"UsageType"`
|
||||
Cost string `bson:"UsageCost" json:"UsageCost"`
|
||||
Address Address `bson:"AddressInfo" json:"AddressInfo"`
|
||||
Connections []Connection `bson:"Connections" json:"Connections"`
|
||||
NumberOfPoints int32 `bson:"NumberOfPoints" json:"NumberOfPoints"`
|
||||
GeneralComments string `bson:"GeneralComments" json:"GeneralComments"`
|
||||
StatusTypeID int32 `bson:"StatusTypeID" json:"StatusTypeID"`
|
||||
StatusType StatusType `bson:"StatusType" json:"StatusType"`
|
||||
SpatialPosition Position `bson:"SpatialPosition" json:"SpatialPosition"`
|
||||
}
|
||||
|
||||
type Position struct {
|
||||
Type string `bson:"type" json:"type"`
|
||||
Coordinates []float64 `bson:"coordinates" json:"coordinates"`
|
||||
}
|
||||
|
||||
type Address struct {
|
||||
Title string `bson:"Title" json:"Title"`
|
||||
Latitude float64 `bson:"Latitude" json:"Latitude"`
|
||||
Longitude float64 `bson:"Longitude" json:"Longitude"`
|
||||
AddressLine1 string `bson:"AddressLine1" json:"AddressLine1"`
|
||||
AddressLine2 string `bson:"AddressLine2" json:"AddressLine2"`
|
||||
Town string `bson:"Town" json:"Town"`
|
||||
StateOrProvince string `bson:"StateOrProvince" json:"StateOrProvince"`
|
||||
AccessComments string `bson:"AccessComments" json:"AccessComments"`
|
||||
Postcode string `bson:"Postcode" json:"Postcode"`
|
||||
CountryID int32 `bson:"CountryID" json:"CountryID"`
|
||||
Country Country `bson:"Country" json:"Country"`
|
||||
}
|
||||
|
||||
type Country struct {
|
||||
ID int32 `bson:"ID" json:"ID"`
|
||||
Title string `bson:"Title" json:"Title"`
|
||||
ISOCode string `bson:"ISOCode" json:"ISOCode"`
|
||||
ContinentCode string `bson:"ContinentCode" json:"ContinentCode"`
|
||||
}
|
||||
|
||||
type Connection struct {
|
||||
TypeID int32 `bson:"ConnectionTypeID" json:"ConnectionTypeID"`
|
||||
Type ConnectionType `bson:"ConnectionType" json:"ConnectionType"`
|
||||
StatusTypeID int32 `bson:"StatusTypeID" json:"StatusTypeID"`
|
||||
StatusType StatusType `bson:"StatusType" json:"StatusType"`
|
||||
LevelID int32 `bson:"LevelID" json:"LevelID"`
|
||||
Level ChargerType `bson:"Level" json:"Level"`
|
||||
Amps float64 `bson:"Amps" json:"Amps"`
|
||||
Voltage float64 `bson:"Voltage" json:"Voltage"`
|
||||
Power float64 `bson:"PowerKW" json:"PowerKW"`
|
||||
CurrentTypeID int32 `bson:"CurrentTypeID" json:"CurrentTypeID"`
|
||||
CurrentType CurrentType `bson:"CurrentType" json:"CurrentType"`
|
||||
Quantity int32 `bson:"Quantity" json:"Quantity"`
|
||||
Reference string `bson:"Reference" json:"Reference"`
|
||||
}
|
||||
|
||||
type ChargerType struct {
|
||||
ID int32 `bson:"ID" json:"ID"`
|
||||
Title string `bson:"Title" json:"Title"`
|
||||
Comments string `bson:"Comments" json:"Comments"`
|
||||
IsFastChargeCapable bool `bson:"IsFastChargeCapable" json:"IsFastChargeCapable"`
|
||||
}
|
||||
|
||||
type CurrentType struct {
|
||||
ID int32 `bson:"ID" json:"ID"`
|
||||
Title string `bson:"Title" json:"Title"`
|
||||
Description string `bson:"Description" json:"Description"`
|
||||
}
|
||||
|
||||
type ConnectionType struct {
|
||||
ID int32 `bson:"ID" json:"ID"`
|
||||
Title string `bson:"Title" json:"Title"`
|
||||
FormalName string `bson:"FormalName" json:"FormalName"`
|
||||
IsDiscontinued bool `bson:"IsDiscontinued" json:"IsDiscontinued"`
|
||||
IsObsolete bool `bson:"IsObsolete" json:"IsObsolete"`
|
||||
}
|
||||
|
||||
type DataProvider struct {
|
||||
ID int32 `bson:"ID" json:"ID"`
|
||||
Title string `bson:"Title" json:"Title"`
|
||||
WebsiteURL string `bson:"WebsiteURL" json:"WebsiteURL"`
|
||||
Comments string `bson:"Comments" json:"Comments"`
|
||||
DataProviderStatus DataProviderStatus `bson:"DataProviderStatusType" json:"DataProviderStatusType"`
|
||||
IsOpenDataLicensed bool `bson:"IsOpenDataLicensed" json:"IsOpenDataLicensed"`
|
||||
License string `bson:"License" json:"License"`
|
||||
}
|
||||
|
||||
type DataProviderStatus struct {
|
||||
ID int32 `bson:"ID" json:"ID"`
|
||||
Title string `bson:"Title" json:"Title"`
|
||||
IsProviderEnabled bool `bson:"IsProviderEnabled" json:"IsProviderEnabled"`
|
||||
}
|
||||
|
||||
type Operator struct {
|
||||
ID int32 `bson:"ID" json:"ID"`
|
||||
Title string `bson:"Title" json:"Title"`
|
||||
WebsiteURL string `bson:"WebsiteURL" json:"WebsiteURL"`
|
||||
Comments string `bson:"Comments" json:"Comments"`
|
||||
PhonePrimary string `bson:"PhonePrimaryContact" json:"PhonePrimaryContact"`
|
||||
PhoneSecondary string `bson:"PhoneSecondaryContact" json:"PhoneSecondaryContact"`
|
||||
IsPrivateIndividual bool `bson:"IsPrivateIndividual" json:"IsPrivateIndividual"`
|
||||
ContactEmail string `bson:"ContactEmail" json:"ContactEmail"`
|
||||
FaultReportEmail string `bson:"FaultReportEmail" json:"FaultReportEmail"`
|
||||
}
|
||||
|
||||
type UsageType struct {
|
||||
ID int32 `bson:"ID" json:"ID"`
|
||||
Title string `bson:"Title" json:"Title"`
|
||||
IsPayAtLocation bool `bson:"IsPayAtLocation" json:"IsPayAtLocation"`
|
||||
IsMembershipRequired bool `bson:"IsMembershipRequired" json:"IsMembershipRequired"`
|
||||
IsAccessKeyRequired bool `bson:"IsAccessKeyRequired" json:"IsAccessKeyRequired"`
|
||||
}
|
||||
|
||||
type StatusType struct {
|
||||
ID int32 `bson:"ID" json:"ID"`
|
||||
Title string `bson:"Title" json:"Title"`
|
||||
IsUsageSelectable bool `bson:"IsUsageSelectable" json:"IsUsageSelectable"`
|
||||
IsOperational bool `bson:"IsOperational" json:"IsOperational"`
|
||||
}
|
||||
|
||||
type UserCommentType struct {
|
||||
ID int32 `bson:"ID" json:"ID"`
|
||||
Title string `bson:"Title" json:"Title"`
|
||||
}
|
||||
|
||||
type CheckinStatusType struct {
|
||||
ID int32 `bson:"ID" json:"ID"`
|
||||
Title string `bson:"Title" json:"Title"`
|
||||
IsPositive bool `bson:"IsPositive" json:"IsPositive"`
|
||||
IsAutomatedCheckin bool `bson:"IsAutomatedCheckin" json:"IsAutomatedCheckin"`
|
||||
}
|
||||
|
||||
type ReferenceData struct {
|
||||
ChargerTypes []ChargerType `bson:"ChargerTypes" json:"ChargerTypes"`
|
||||
ConnectionTypes []ConnectionType `bson:"ConnectionTypes" json:"ConnectionTypes"`
|
||||
CurrentTypes []CurrentType `bson:"CurrentTypes" json:"CurrentTypes"`
|
||||
Countries []Country `bson:"Countries" json:"Countries"`
|
||||
DataProviders []DataProvider `bson:"DataProviders" json:"DataProviders"`
|
||||
Operators []Operator `bson:"Operators" json:"Operators"`
|
||||
StatusTypes []StatusType `bson:"StatusTypes" json:"StatusTypes"`
|
||||
UsageTypes []UsageType `bson:"UsageTypes" json:"UsageTypes"`
|
||||
UserCommentTypes []UserCommentType `bson:"UserCommentTypes" json:"UserCommentTypes"`
|
||||
CheckinStatusTypes []CheckinStatusType `bson:"CheckinStatusTypes" json:"CheckinStatusTypes"`
|
||||
SubmissionStatusTypes []SubmissionStatusType `bson:"SubmissionStatusTypes" json:"SubmissionStatusTypes"`
|
||||
}
|
||||
|
||||
type SubmissionStatusType struct {
|
||||
ID int32 `bson:"ID" json:"ID"`
|
||||
Title string `bson:"Title" json:"Title"`
|
||||
IsLive bool `bson:"IsLive" json:"IsLive"`
|
||||
}
|
||||
25
evchargers/main.go
Normal file
25
evchargers/main.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/micro/services/evchargers/handler"
|
||||
pb "github.com/micro/services/evchargers/proto"
|
||||
|
||||
"github.com/micro/micro/v3/service"
|
||||
"github.com/micro/micro/v3/service/logger"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create service
|
||||
srv := service.New(
|
||||
service.Name("evchargers"),
|
||||
service.Version("latest"),
|
||||
)
|
||||
|
||||
// Register handler
|
||||
pb.RegisterEvchargersHandler(srv.Server(), handler.New())
|
||||
|
||||
// Run service
|
||||
if err := srv.Run(); err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
}
|
||||
1
evchargers/micro.mu
Normal file
1
evchargers/micro.mu
Normal file
@@ -0,0 +1 @@
|
||||
service evchargers
|
||||
2355
evchargers/proto/evchargers.pb.go
Normal file
2355
evchargers/proto/evchargers.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
110
evchargers/proto/evchargers.pb.micro.go
Normal file
110
evchargers/proto/evchargers.pb.micro.go
Normal file
@@ -0,0 +1,110 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: proto/evchargers.proto
|
||||
|
||||
package evchargers
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
import (
|
||||
context "context"
|
||||
api "github.com/micro/micro/v3/service/api"
|
||||
client "github.com/micro/micro/v3/service/client"
|
||||
server "github.com/micro/micro/v3/service/server"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ api.Endpoint
|
||||
var _ context.Context
|
||||
var _ client.Option
|
||||
var _ server.Option
|
||||
|
||||
// Api Endpoints for Evchargers service
|
||||
|
||||
func NewEvchargersEndpoints() []*api.Endpoint {
|
||||
return []*api.Endpoint{}
|
||||
}
|
||||
|
||||
// Client API for Evchargers service
|
||||
|
||||
type EvchargersService interface {
|
||||
Search(ctx context.Context, in *SearchRequest, opts ...client.CallOption) (*SearchResponse, error)
|
||||
ReferenceData(ctx context.Context, in *ReferenceDataRequest, opts ...client.CallOption) (*ReferenceDataResponse, error)
|
||||
}
|
||||
|
||||
type evchargersService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewEvchargersService(name string, c client.Client) EvchargersService {
|
||||
return &evchargersService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *evchargersService) Search(ctx context.Context, in *SearchRequest, opts ...client.CallOption) (*SearchResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Evchargers.Search", in)
|
||||
out := new(SearchResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *evchargersService) ReferenceData(ctx context.Context, in *ReferenceDataRequest, opts ...client.CallOption) (*ReferenceDataResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Evchargers.ReferenceData", in)
|
||||
out := new(ReferenceDataResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Evchargers service
|
||||
|
||||
type EvchargersHandler interface {
|
||||
Search(context.Context, *SearchRequest, *SearchResponse) error
|
||||
ReferenceData(context.Context, *ReferenceDataRequest, *ReferenceDataResponse) error
|
||||
}
|
||||
|
||||
func RegisterEvchargersHandler(s server.Server, hdlr EvchargersHandler, opts ...server.HandlerOption) error {
|
||||
type evchargers interface {
|
||||
Search(ctx context.Context, in *SearchRequest, out *SearchResponse) error
|
||||
ReferenceData(ctx context.Context, in *ReferenceDataRequest, out *ReferenceDataResponse) error
|
||||
}
|
||||
type Evchargers struct {
|
||||
evchargers
|
||||
}
|
||||
h := &evchargersHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Evchargers{h}, opts...))
|
||||
}
|
||||
|
||||
type evchargersHandler struct {
|
||||
EvchargersHandler
|
||||
}
|
||||
|
||||
func (h *evchargersHandler) Search(ctx context.Context, in *SearchRequest, out *SearchResponse) error {
|
||||
return h.EvchargersHandler.Search(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *evchargersHandler) ReferenceData(ctx context.Context, in *ReferenceDataRequest, out *ReferenceDataResponse) error {
|
||||
return h.EvchargersHandler.ReferenceData(ctx, in, out)
|
||||
}
|
||||
228
evchargers/proto/evchargers.proto
Normal file
228
evchargers/proto/evchargers.proto
Normal file
@@ -0,0 +1,228 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package evchargers;
|
||||
|
||||
option go_package = "./proto;evchargers";
|
||||
|
||||
service Evchargers {
|
||||
rpc Search(SearchRequest) returns (SearchResponse) {}
|
||||
rpc ReferenceData(ReferenceDataRequest) returns (ReferenceDataResponse) {}
|
||||
}
|
||||
|
||||
|
||||
// Search by giving a coordinate and a max distance, or bounding box and optional filters
|
||||
message SearchRequest {
|
||||
// Coordinates from which to begin search
|
||||
Coordinates location = 1;
|
||||
// Search distance from point in metres, defaults to 5000m
|
||||
int64 distance = 2;
|
||||
// Bounding box to search within (top left and bottom right coordinates)
|
||||
BoundingBox box = 3;
|
||||
// Maximum number of results to return, defaults to 100
|
||||
int64 max_results = 4;
|
||||
// Country ID
|
||||
string country_id = 5;
|
||||
// IDs of the the EV charger operator
|
||||
repeated string operators = 6;
|
||||
// IDs of the connection type
|
||||
repeated string connection_types = 7;
|
||||
// Supported charging levels
|
||||
repeated string levels = 8;
|
||||
// Minimum power in KW. Note: data not available for many chargers
|
||||
int64 min_power = 9;
|
||||
// Usage of the charge point (is it public, membership required, etc)
|
||||
repeated string usage_types = 11;
|
||||
|
||||
// TODO https://openchargemap.org/site/develop/api#POI
|
||||
// verbose / compact to only return IDs for ref data
|
||||
// polygon
|
||||
// polyline
|
||||
}
|
||||
|
||||
message Coordinates {
|
||||
float latitude = 1;
|
||||
float longitude = 2;
|
||||
}
|
||||
|
||||
// Box to search (top left and bottom right coordinates)
|
||||
message BoundingBox {
|
||||
Coordinates bottom_left = 1;
|
||||
Coordinates top_right = 2;
|
||||
}
|
||||
|
||||
message SearchResponse {
|
||||
repeated Poi pois = 1;
|
||||
}
|
||||
|
||||
message Poi {
|
||||
// The ID of the charger
|
||||
string id = 1;
|
||||
// The ID of the data provider
|
||||
string data_provider_id = 2;
|
||||
// The ID of the operator of the charger
|
||||
string operator_id = 3;
|
||||
// The type of usage for this charger point (is it public, membership required, etc)
|
||||
string usage_type_id = 4;
|
||||
// The address
|
||||
Address address = 5;
|
||||
// The connections available at this charge point
|
||||
repeated Connection connections = 6;
|
||||
// The number of charging points
|
||||
int64 num_points = 7;
|
||||
// The cost of charging
|
||||
string cost = 8;
|
||||
// The operator
|
||||
Operator operator = 10;
|
||||
// The type of usage
|
||||
UsageType usage_type = 11;
|
||||
|
||||
}
|
||||
|
||||
message Address {
|
||||
Coordinates location = 1;
|
||||
string title = 2;
|
||||
string address_line_1 = 3;
|
||||
string address_line_2 = 4;
|
||||
string town = 5;
|
||||
string state_or_province = 6;
|
||||
// Any comments about how to access the charger
|
||||
string access_comments = 7;
|
||||
string postcode = 8;
|
||||
string country_id = 9;
|
||||
Country country = 10;
|
||||
}
|
||||
|
||||
message Connection {
|
||||
// The ID of the connection type
|
||||
string connection_type_id = 1;
|
||||
string reference = 2;
|
||||
// The level of charging power available
|
||||
string level = 4;
|
||||
// The amps offered
|
||||
float amps = 5;
|
||||
// The voltage offered
|
||||
float voltage = 6;
|
||||
// The power in KW
|
||||
float power = 7;
|
||||
// The current
|
||||
string current = 8;
|
||||
ConnectionType connection_type = 9;
|
||||
}
|
||||
|
||||
// Retrieve reference data as used by this API
|
||||
message ReferenceDataRequest {}
|
||||
|
||||
message ReferenceDataResponse {
|
||||
// The types of charger
|
||||
repeated ChargerType charger_types = 1;
|
||||
// The types of connection
|
||||
repeated ConnectionType connection_types = 2;
|
||||
// The types of current
|
||||
repeated CurrentType current_types = 3;
|
||||
// The countries
|
||||
repeated Country countries = 4;
|
||||
// The providers of the charger data
|
||||
repeated DataProvider data_providers = 5;
|
||||
// The companies operating the chargers
|
||||
repeated Operator operators = 6;
|
||||
// The status of the charger
|
||||
repeated StatusType status_types = 7;
|
||||
// The status of a submission
|
||||
repeated SubmissionStatusType submission_status_types = 8;
|
||||
// The different types of usage
|
||||
repeated UsageType usage_types = 9;
|
||||
// The types of user comment
|
||||
repeated UserCommentType user_comment_types = 10;
|
||||
// The types of checkin status
|
||||
repeated CheckinStatusType checkin_status_types = 11;
|
||||
}
|
||||
|
||||
message ChargerType {
|
||||
string id = 1;
|
||||
string title = 2;
|
||||
string comments = 3;
|
||||
// Is this 40KW+
|
||||
bool is_fast_charge_capable = 4;
|
||||
}
|
||||
|
||||
message ConnectionType {
|
||||
string id = 1;
|
||||
string title = 2;
|
||||
string formal_name = 3;
|
||||
bool is_discontinued = 4;
|
||||
bool is_obsolete = 5;
|
||||
}
|
||||
|
||||
message CurrentType {
|
||||
string id = 1;
|
||||
string title = 2;
|
||||
string description = 3;
|
||||
}
|
||||
|
||||
message Country {
|
||||
string id = 1;
|
||||
string title = 2;
|
||||
string iso_code = 3;
|
||||
string continent_code = 4;
|
||||
}
|
||||
|
||||
message DataProvider {
|
||||
string id = 1;
|
||||
string title = 2;
|
||||
string website = 3;
|
||||
string comments = 4;
|
||||
DataProviderStatusType data_provider_status_type = 5;
|
||||
// How is this data licensed
|
||||
string license = 6;
|
||||
}
|
||||
|
||||
message DataProviderStatusType {
|
||||
string id = 1;
|
||||
string title = 2;
|
||||
bool is_provider_enabled = 3;
|
||||
}
|
||||
|
||||
message Operator {
|
||||
string id = 1;
|
||||
string title = 2;
|
||||
string website = 3;
|
||||
string comments = 4;
|
||||
// Is this operator a private individual vs a company
|
||||
bool is_private_individual = 5;
|
||||
string contact_email = 6;
|
||||
string phone_primary = 7;
|
||||
string phone_secondary = 8;
|
||||
string fault_report_email = 9;
|
||||
}
|
||||
|
||||
message StatusType {
|
||||
string id = 1;
|
||||
string title = 2;
|
||||
bool is_operational = 3;
|
||||
}
|
||||
|
||||
message SubmissionStatusType {
|
||||
string id = 1;
|
||||
string title = 2;
|
||||
bool is_live = 3;
|
||||
}
|
||||
|
||||
message UsageType {
|
||||
string id = 1;
|
||||
string title = 2;
|
||||
bool is_pay_at_location = 3;
|
||||
bool is_membership_required = 4;
|
||||
bool is_access_key_required = 5;
|
||||
}
|
||||
|
||||
message UserCommentType {
|
||||
string id = 1;
|
||||
string title = 2;
|
||||
}
|
||||
|
||||
message CheckinStatusType {
|
||||
string id = 1;
|
||||
string title = 2;
|
||||
bool is_positive = 3;
|
||||
bool is_automated = 4;
|
||||
}
|
||||
5
evchargers/publicapi.json
Normal file
5
evchargers/publicapi.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "evchargers",
|
||||
"icon": "🔋",
|
||||
"category": "travel"
|
||||
}
|
||||
Reference in New Issue
Block a user