it2s-itss-facilities/src/evm.c

473 lines
21 KiB
C

#include "evm.h"
#include "facilities.h"
#include <fcntl.h>
#include <it2s-asn/etsi-its-v1/evcsnm/EI1_EvcsnPdu.h>
#include <it2s-asn/etsi-its-sdu/itss-facilities/EIS_FacilitiesIndication.h>
#include <it2s-asn/etsi-its-sdu/itss-management/EIS_ManagementRequest.h>
#include <it2s-asn/etsi-its-sdu/itss-networking/EIS_NetworkingRequest.h>
#include <it2s-tender/constants.h>
#include <it2s-tender/database.h>
#include <it2s-tender/packet.h>
#include <it2s-tender/recorder.h>
#include <it2s-tender/space.h>
#include <it2s-tender/time.h>
#include <math.h>
#include <stdint.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <zmq.h>
static UTF8String_t *create_utf8_from_string(const char *string, size_t length) {
UTF8String_t *utf8_string = calloc(1, sizeof(UTF8String_t));
utf8_string->buf = calloc(length, sizeof(uint8_t));
utf8_string->size = length;
memcpy(utf8_string->buf, string, length);
return utf8_string;
}
static UTF8String_t *create_empty_utf8_string() {
UTF8String_t *utf8_string = calloc(1, sizeof(UTF8String_t));
utf8_string->size = 0;
return utf8_string;
}
static int mk_evcsnm(uint8_t *evcsnm_oer, uint32_t *evcsnm_len) {
int rv = 0;
int shm_fd, shm_valid = 0;
EI1_EvcsnPdu_t *evcsnm = calloc(1, sizeof(EI1_EvcsnPdu_t));
evcsnm->header.protocolVersion = 2;
evcsnm->header.messageID = 1;
pthread_mutex_lock(&facilities.id.lock);
evcsnm->header.stationID = facilities.id.station_id;
pthread_mutex_unlock(&facilities.id.lock);
uint64_t now = itss_time_get();
asn_ulong2INTEGER(&evcsnm->evcsn.poiHeader.timeStamp, now);
evcsnm->evcsn.poiHeader.poiType = 1; // set to "EV charging station POI ID = 1"
evcsnm->evcsn.poiHeader.relayCapable = 1;
evcsnm->evcsn.evcsnData.totalNumberOfStations = 1;
evcsnm->evcsn.evcsnData.chargingStationsData.list.array = calloc(1, sizeof(void *));
evcsnm->evcsn.evcsnData.chargingStationsData.list.count = 1;
evcsnm->evcsn.evcsnData.chargingStationsData.list.size = sizeof(void *) * 1;
evcsnm->evcsn.evcsnData.chargingStationsData.list.array[0] = calloc(1, sizeof(struct EI1_ItsChargingStationData));
struct EI1_ItsChargingStationData *cs0 = evcsnm->evcsn.evcsnData.chargingStationsData.list.array[0];
cs0->chargingStationID = 0;
itss_space_lock();
itss_space_get();
cs0->chargingStationLocation.latitude = 405970830;
cs0->chargingStationLocation.longitude = -86628610;
cs0->chargingStationLocation.altitude.altitudeValue = epv.space.data.altitude.value;
cs0->chargingStationLocation.altitude.altitudeConfidence = epv.space.data.altitude.confidence;
cs0->chargingStationLocation.positionConfidenceEllipse.semiMajorConfidence = EI1_SemiAxisLength_unavailable;
cs0->chargingStationLocation.positionConfidenceEllipse.semiMinorConfidence = EI1_SemiAxisLength_unavailable;
cs0->chargingStationLocation.positionConfidenceEllipse.semiMajorOrientation = EI1_HeadingValue_unavailable;
cs0->accessibility = *create_utf8_from_string("Free Access", strlen("Free Access"));
cs0->pricing = *create_utf8_from_string(".15 €/kWh", strlen(".15 €/kWh"));
cs0->openingDaysHours = *create_utf8_from_string("Always", strlen("Always"));
const char *booking_url = "ccam.av.it.pt";
cs0->bookingContactInfo = create_utf8_from_string(booking_url, strlen(booking_url));
cs0->chargingSpotsAvailable.list.array = calloc(1, sizeof(void *));
cs0->chargingSpotsAvailable.list.count = 1;
cs0->chargingSpotsAvailable.list.size = sizeof(void *) * 1;
cs0->chargingSpotsAvailable.list.array[0] = calloc(1, sizeof(struct EI1_ItsChargingSpotDataElements));
struct EI1_ItsChargingSpotDataElements *cs_elem0 = cs0->chargingSpotsAvailable.list.array[0];
cs_elem0->energyAvailability = *create_utf8_from_string("Max: 30kW", strlen("Max: 30kW"));
cs_elem0->type.buf = calloc(1, sizeof(uint8_t));
cs_elem0->type.size = 1;
cs_elem0->type.bits_unused = 0;
cs_elem0->type.buf[0] = 0x03;
cs_elem0->typeOfReceptacle.buf = calloc(1, sizeof(uint8_t));
cs_elem0->typeOfReceptacle.size = 1;
cs_elem0->typeOfReceptacle.bits_unused = 0;
cs_elem0->typeOfReceptacle.buf[0] = 0x0D;
cs_elem0->parkingPlacesData = calloc(1, sizeof(struct EI1_ParkingPlacesData));
cs_elem0->parkingPlacesData->list.array = calloc(2, sizeof(void *));
cs_elem0->parkingPlacesData->list.count = 2;
cs_elem0->parkingPlacesData->list.size = sizeof(void *) * 2;
// As requested onnly 2 parking places are available
for (int parckingPlaceIndex = 0; parckingPlaceIndex < 2; parckingPlaceIndex++) {
cs_elem0->parkingPlacesData->list.array[parckingPlaceIndex] = calloc(1, sizeof(struct EI1_SpotAvailability));
struct EI1_SpotAvailability *spot = cs_elem0->parkingPlacesData->list.array[parckingPlaceIndex];
spot->maxWaitingTimeMinutes = 0;
spot->blocking = 1;
}
itss_space_unlock(epv);
// if (facilities.station_type == StationType_roadSideUnit)
//{
// }
asn_enc_rval_t enc = uper_encode_to_buffer(&asn_DEF_EI1_EvcsnPdu, NULL, evcsnm, evcsnm_oer, 512);
if (enc.encoded == -1) {
log_error("[ev] failed encoding evcsnm (%s)", enc.failed_type->name);
rv = 1;
goto cleanup;
}
*evcsnm_len = (enc.encoded + 7) / 8;
cleanup:
ASN_STRUCT_FREE(asn_DEF_EI1_EvcsnPdu, evcsnm);
return rv;
}
static uint64_t pre_reservation_id = 1;
static uint64_t reservation_id = 1;
int evrsrm_recv(EI1_EV_RSR_t *evrsr_request) {
int rv = 0;
EIS_NetworkingRequest_t* nr = calloc(1, sizeof(EIS_NetworkingRequest_t));
nr->present = EIS_NetworkingRequest_PR_packet;
EIS_NetworkingPacketRequest_t* npr = &nr->choice.packet;
npr->network.present = EIS_NetworkingPacketRequestNW_PR_gn;
npr->network.choice.gn.trafficClass = 2;
npr->network.choice.gn.destinationAddress.buf = malloc(6);
for (int i = 0; i < 6; ++i) {
npr->network.choice.gn.destinationAddress.buf[i] = 0xff;
}
npr->network.choice.gn.destinationAddress.size = 6;
npr->network.choice.gn.packetTransportType = EIS_PacketTransportType_shb;
npr->network.choice.gn.securityProfile.sign = true;
npr->transport.present = EIS_NetworkingPacketRequestTP_PR_btp;
npr->transport.choice.btp.btpType = EIS_BTPType_btpB;
npr->transport.choice.btp.destinationPort = EIS_Port_evrsr;
npr->data.buf = malloc(512);
// Fill header for FacilitiesIndication and FacilitiesMessageIndication structs
EIS_FacilitiesIndication_t *fi = calloc(1, sizeof(EIS_FacilitiesIndication_t));
fi->present = EIS_FacilitiesIndication_PR_message;
EIS_FacilitiesMessageIndication_t *fmi = &fi->choice.message;
fmi->itsMessageType = EIS_ItsMessageType_evrsr;
fmi->data.buf = malloc(512);
uint8_t tr_oer[1024];
uint8_t fi_oer[1024];
tr_oer[0] = ITSS_FACILITIES; // Facilities
fi_oer[0] = ITSS_FACILITIES;
if (!(evrsr_request->messageBody.present == EI1_EV_RSR_MessageBody_PR_preReservationRequestMessage ||
evrsr_request->messageBody.present == EI1_EV_RSR_MessageBody_PR_reservationRequestMessage ||
evrsr_request->messageBody.present == EI1_EV_RSR_MessageBody_PR_cancellationRequestMessage ||
evrsr_request->messageBody.present == EI1_EV_RSR_MessageBody_PR_updateRequestMessage)) {
rv = 1;
goto cleanup;
}
int evrsr_response;
if (evrsr_request->messageBody.present == EI1_EV_RSR_MessageBody_PR_preReservationRequestMessage)
evrsr_response = evrsrm_pre_reservation_response(npr->data.buf, (uint32_t *)&npr->data.size);
else if (evrsr_request->messageBody.present == EI1_EV_RSR_MessageBody_PR_reservationRequestMessage)
evrsr_response = evrsrm_reservation_response(evrsr_request, npr->data.buf, (uint32_t *)&npr->data.size);
else if (evrsr_request->messageBody.present == EI1_EV_RSR_MessageBody_PR_cancellationRequestMessage)
evrsr_response = evrsrm_cancellation_response(evrsr_request, npr->data.buf, (uint32_t *)&npr->data.size);
else if (evrsr_request->messageBody.present == EI1_EV_RSR_MessageBody_PR_updateRequestMessage)
evrsr_response = evrsrm_update_response(evrsr_request, npr->data.buf, (uint32_t *)&npr->data.size);
if (evrsr_response != 0) {
rv = 1;
goto cleanup;
}
memcpy(fmi->data.buf, npr->data.buf, npr->data.size);
fmi->data.size = npr->data.size;
uint32_t id = itss_id(npr->data.buf, npr->data.size);
npr->id = id;
fmi->id = id;
asn_enc_rval_t enc = oer_encode_to_buffer(&asn_DEF_EIS_NetworkingRequest, NULL, nr, tr_oer + 1, 1023);
if (enc.encoded == -1) {
log_error("[ev] failed encoding transport request (%s)", enc.failed_type->name);
rv = 1;
goto cleanup;
}
asn_enc_rval_t enc_fdi = oer_encode_to_buffer(&asn_DEF_EIS_FacilitiesIndication, NULL, fi, fi_oer + 1, 1023);
if (enc_fdi.encoded == -1) {
log_error("[ev] encoding FI for evrsrm failed");
rv = 1;
goto cleanup;
}
itss_queue_send(facilities.tx_queue, itss_queue_packet_new(tr_oer, enc.encoded + 1, ITSS_NETWORKING, id, "NR.packet.btp"));
itss_queue_send(facilities.tx_queue, itss_queue_packet_new(fi_oer, enc_fdi.encoded + 1, ITSS_APPLICATIONS, id, "FI.message"));
cleanup:
log_debug("[ev] evrsrm_recv done with rv %d", rv);
return rv;
}
static int evrsrm_cancellation_response(EI1_EV_RSR_t *evrsrm_request, uint8_t *evrsrm_oer, uint32_t *evrsrm_len){
int rv = 0;
EI1_EV_RSR_t *evrsr_response = calloc(1, sizeof(EI1_EV_RSR_t));
evrsr_response->header.protocolVersion = 1;
evrsr_response->header.messageID = 7;
pthread_mutex_lock(&facilities.id.lock);
evrsr_response->header.stationID = facilities.id.station_id;
pthread_mutex_unlock(&facilities.id.lock);
evrsr_response->messageBody.present = EI1_EV_RSR_MessageBody_PR_cancellationResponseMessage;
EI1_CancellationResponseMessage_t *evrsr_cancellation_response = &evrsr_response->messageBody.choice.cancellationResponseMessage;
evrsr_cancellation_response->reservation_ID.buf = calloc(8, sizeof(uint8_t));
evrsr_cancellation_response->reservation_ID.size = 8;
memccpy(evrsr_cancellation_response->reservation_ID.buf, evrsrm_request->messageBody.choice.cancellationRequestMessage.reservation_ID.buf, 0, 8);
evrsr_cancellation_response->cancellationResponseCode = EI1_CancellationResponseCode_ok;
asn_enc_rval_t enc = uper_encode_to_buffer(&asn_DEF_EI1_EV_RSR, NULL, evrsr_response, evrsrm_oer, 512);
if (enc.encoded == -1) {
log_error("[ev] failed encoding evrsrm cancellation response (%s)", enc.failed_type->name);
rv = 1;
goto cleanup;
}
*evrsrm_len = (enc.encoded + 7) / 8;
cleanup:
ASN_STRUCT_FREE(asn_DEF_EI1_EV_RSR, evrsr_response);
return rv;
}
static int evrsrm_update_response(EI1_EV_RSR_t *evrsrm_request, uint8_t *evrsrm_oer, uint32_t *evrsrm_len){
int rv = 0;
EI1_EV_RSR_t *evrsr_response = calloc(1, sizeof(EI1_EV_RSR_t));
evrsr_response->header.protocolVersion = 1;
evrsr_response->header.messageID = 7;
pthread_mutex_lock(&facilities.id.lock);
evrsr_response->header.stationID = facilities.id.station_id;
pthread_mutex_unlock(&facilities.id.lock);
evrsr_response->messageBody.present = EI1_EV_RSR_MessageBody_PR_updateResponseMessage;
EI1_UpdateResponseMessage_t *evrsr_update_response = &evrsr_response->messageBody.choice.updateResponseMessage;
evrsr_update_response->reservation_ID.buf = calloc(8, sizeof(uint8_t));
evrsr_update_response->reservation_ID.size = 8;
memccpy(evrsr_update_response->reservation_ID.buf, evrsrm_request->messageBody.choice.updateRequestMessage.reservation_ID.buf, 0, 8);
evrsr_update_response->updateResponseCode = EI1_UpdateResponseCode_ok;
asn_enc_rval_t enc = uper_encode_to_buffer(&asn_DEF_EI1_EV_RSR, NULL, evrsr_response, evrsrm_oer, 512);
if (enc.encoded == -1) {
log_error("[ev] failed encoding evrsrm update response (%s)", enc.failed_type->name);
rv = 1;
goto cleanup;
}
*evrsrm_len = (enc.encoded + 7) / 8;
cleanup:
ASN_STRUCT_FREE(asn_DEF_EI1_EV_RSR, evrsr_response);
return rv;
}
static int evrsrm_reservation_response(EI1_EV_RSR_t *evrsrm_request, uint8_t *evrsrm_oer, uint32_t *evrsrm_len) {
int rv = 0;
EI1_EV_RSR_t *evrsr_response = calloc(1, sizeof(EI1_EV_RSR_t));
evrsr_response->header.protocolVersion = 1;
evrsr_response->header.messageID = 7;
pthread_mutex_lock(&facilities.id.lock);
evrsr_response->header.stationID = facilities.id.station_id;
pthread_mutex_unlock(&facilities.id.lock);
evrsr_response->messageBody.present = EI1_EV_RSR_MessageBody_PR_reservationResponseMessage;
EI1_ReservationResponseMessage_t *response = &evrsr_response->messageBody.choice.reservationResponseMessage;
// Assumes the pre reservation was successful
response->reservationResponseCode = EI1_ReservationResponseCode_ok;
response->reservation_ID = calloc(1, sizeof(EI1_Reservation_ID_t));
response->reservation_ID->buf = calloc(8, sizeof(uint8_t));
response->reservation_ID->size = 8;
response->reservation_ID->buf[0] = '0' + reservation_id / 10000000;
response->reservation_ID->buf[1] = '0' + (reservation_id / 1000000) % 10;
response->reservation_ID->buf[2] = '0' + (reservation_id / 100000) % 10;
response->reservation_ID->buf[3] = '0' + (reservation_id / 10000) % 10;
response->reservation_ID->buf[4] = '0' + (reservation_id / 1000) % 10;
response->reservation_ID->buf[5] = '0' + (reservation_id / 100) % 10;
response->reservation_ID->buf[6] = '0' + (reservation_id / 10) % 10;
response->reservation_ID->buf[7] = '0' + reservation_id % 10;
response->reservation_Password = calloc(1, sizeof(EI1_Reservation_Password_t));
response->reservation_Password->buf = calloc(8, sizeof(uint8_t));
response->reservation_Password->size = 8;
response->reservation_Password->buf[0] = 'i';
response->reservation_Password->buf[1] = 't';
response->reservation_Password->buf[2] = '2';
response->reservation_Password->buf[3] = 's';
response->reservation_Password->buf[4] = 'i';
response->reservation_Password->buf[5] = 't';
response->reservation_Password->buf[6] = '2';
response->reservation_Password->buf[7] = 's';
// estimated arrival time + tolerance time
response->expirationTime = evrsrm_request->messageBody.choice.reservationRequestMessage.arrivalTime;
response->expirationTime += 15 * 60 * 1000; // 15 minutes
asn_enc_rval_t enc = uper_encode_to_buffer(&asn_DEF_EI1_EV_RSR, NULL, evrsr_response, evrsrm_oer, 512);
if (enc.encoded == -1) {
log_error("[ev] failed encoding evrsrm reservation response (%s)", enc.failed_type->name);
rv = 1;
goto cleanup;
}
*evrsrm_len = (enc.encoded + 7) / 8;
reservation_id++;
cleanup:
ASN_STRUCT_FREE(asn_DEF_EI1_EV_RSR, evrsr_response);
return rv;
}
static int evrsrm_pre_reservation_response(uint8_t *evrsrm_oer, uint32_t *evrsrm_len) {
int rv = 0;
EI1_EV_RSR_t *evrsr_response = calloc(1, sizeof(EI1_EV_RSR_t));
evrsr_response->header.protocolVersion = 1;
evrsr_response->header.messageID = 7;
pthread_mutex_lock(&facilities.id.lock);
evrsr_response->header.stationID = facilities.id.station_id;
pthread_mutex_unlock(&facilities.id.lock);
evrsr_response->messageBody.present = EI1_EV_RSR_MessageBody_PR_preReservationResponseMessage;
EI1_PreReservationResponseMessage_t *response = &evrsr_response->messageBody.choice.preReservationResponseMessage;
response->preReservation_ID.buf = calloc(8, sizeof(uint8_t));
response->preReservation_ID.size = 8;
response->preReservation_ID.buf[0] = '0' + (pre_reservation_id / 10000000);
response->preReservation_ID.buf[1] = '0' + (pre_reservation_id / 1000000) % 10;
response->preReservation_ID.buf[2] = '0' + (pre_reservation_id / 100000) % 10;
response->preReservation_ID.buf[3] = '0' + (pre_reservation_id / 10000) % 10;
response->preReservation_ID.buf[4] = '0' + (pre_reservation_id / 1000) % 10;
response->preReservation_ID.buf[5] = '0' + (pre_reservation_id / 100) % 10;
response->preReservation_ID.buf[6] = '0' + (pre_reservation_id / 10) % 10;
response->preReservation_ID.buf[7] = '0' + pre_reservation_id % 10;
response->availabilityStatus = 0;
response->supportedPaymentTypes.buf = calloc(1, sizeof(uint8_t));
response->supportedPaymentTypes.bits_unused = 0;
response->supportedPaymentTypes.size = 1;
response->supportedPaymentTypes.buf[0] = 0x00;
response->preReservationExpirationTime = 0;
asn_enc_rval_t enc = uper_encode_to_buffer(&asn_DEF_EI1_EV_RSR, NULL, evrsr_response, evrsrm_oer, 512);
if (enc.encoded == -1) {
log_error("[ev] failed encoding evrsrm pre reservation response (%s)", enc.failed_type->name);
rv = 1;
goto cleanup;
}
*evrsrm_len = (enc.encoded + 7) / 8;
pre_reservation_id++;
cleanup:
ASN_STRUCT_FREE(asn_DEF_EI1_EV_RSR, evrsr_response);
return rv;
}
int evcsnm_check(EI1_EvcsnPdu_t *evcsnm) {
return 0;
}
int evrsrm_check(EI1_EV_RSR_t *evrsrm) {
return 0;
}
enum EVM_CHECK_R check_evcsnm(EIS_BTPPacketIndication_t *bpi, EI1_EvcsnPdu_t *evcsnm, uint8_t *ssp, uint32_t ssp_len) {
return 0;
}
enum EVM_CHECK_R check_evrsrm(EIS_BTPPacketIndication_t *bpi, EI1_EV_RSR_t *evrsrm, uint8_t *ssp, uint32_t ssp_len) {
return 0;
}
void *evcsn_service() {
int rv = 0;
EIS_NetworkingRequest_t* nr = calloc(1, sizeof(EIS_NetworkingRequest_t));
nr->present = EIS_NetworkingRequest_PR_packet;
EIS_NetworkingPacketRequest_t* npr = &nr->choice.packet;
npr->network.present = EIS_NetworkingPacketRequestNW_PR_gn;
npr->network.choice.gn.trafficClass = 2;
npr->network.choice.gn.destinationAddress.buf = malloc(6);
for (int i = 0; i < 6; ++i) {
npr->network.choice.gn.destinationAddress.buf[i] = 0xff;
}
npr->network.choice.gn.destinationAddress.size = 6;
npr->network.choice.gn.packetTransportType = EIS_PacketTransportType_shb;
npr->network.choice.gn.securityProfile.sign = true;
npr->transport.present = EIS_NetworkingPacketRequestTP_PR_btp;
npr->transport.choice.btp.btpType = EIS_BTPType_btpB;
npr->transport.choice.btp.destinationPort = EIS_Port_poi;
npr->data.buf = malloc(512);
// Fill header for FacilitiesIndication and FacilitiesMessageIndication structs
EIS_FacilitiesIndication_t *fi = calloc(1, sizeof(EIS_FacilitiesIndication_t));
fi->present = EIS_FacilitiesIndication_PR_message;
EIS_FacilitiesMessageIndication_t *fmi = &fi->choice.message;
fmi->itsMessageType = EIS_ItsMessageType_poi;
fmi->data.buf = malloc(512);
uint8_t tr_oer[1024];
uint8_t fi_oer[1024];
tr_oer[0] = ITSS_FACILITIES; // Facilities
fi_oer[0] = ITSS_FACILITIES;
while (!facilities.exit) {
itss_usleep(1000 * 1000);
if (facilities.evm_args.activate) {
rv = mk_evcsnm(npr->data.buf, (uint32_t *)&npr->data.size);
if (rv) {
continue;
}
memcpy(fmi->data.buf, npr->data.buf, npr->data.size);
fmi->data.size = npr->data.size;
uint32_t id = itss_id(npr->data.buf, npr->data.size);
npr->id = id;
fmi->id = id;
asn_enc_rval_t enc = oer_encode_to_buffer(&asn_DEF_EIS_NetworkingRequest, NULL, nr, tr_oer + 1, 1023);
if (enc.encoded == -1) {
log_error("encoding TR for evcsnm failed");
continue;
}
asn_enc_rval_t enc_fdi = oer_encode_to_buffer(&asn_DEF_EIS_FacilitiesIndication, NULL, fi, fi_oer + 1, 1023);
if (enc_fdi.encoded == -1) {
log_error("encoding FI for evcsnm failed");
continue;
}
itss_queue_send(facilities.tx_queue, itss_queue_packet_new(tr_oer, enc.encoded + 1, ITSS_NETWORKING, id, "NR.packet.btp"));
itss_queue_send(facilities.tx_queue, itss_queue_packet_new(fi_oer, enc_fdi.encoded + 1, ITSS_APPLICATIONS, id, "FI.message"));
// Logging
if (facilities.logging.dbms) {
pthread_mutex_lock(&facilities.id.lock);
uint32_t station_id = facilities.id.station_id;
pthread_mutex_unlock(&facilities.id.lock);
itss_db_add(facilities.logging.dbms, station_id, npr->id, true, EI1_messageID_evcsn, NULL, npr->data.buf, npr->data.size);
}
if (facilities.logging.recorder) {
uint16_t buffer_len = 2048;
uint8_t buffer[buffer_len];
int e = itss_management_record_packet_sdu(
buffer,
buffer_len,
npr->data.buf,
npr->data.size,
id,
itss_time_get(),
ITSS_FACILITIES,
true);
if (e != -1) {
itss_queue_send(facilities.tx_queue, itss_queue_packet_new(buffer, e, ITSS_MANAGEMENT, id, "MReq.packet.set"));
}
}
}
}
ASN_STRUCT_FREE(asn_DEF_EIS_NetworkingRequest, nr);
return NULL;
}