1use crate::conversion::*;
2use crate::en1545date::*;
3use crate::models::*;
4use chrono::prelude::*;
5use num_traits::Zero;
6
7#[derive(Debug)]
8pub struct ETicket {
9 pub product_code: ProductCode,
11 pub customer_profile: u8,
13 pub language: Language,
14 pub validity_length: ValidityLength,
16 pub validity_area: ValidityArea,
17 pub sale_datetime: DateTime<Utc>,
18 pub sale_device: SaleDevice,
19 pub ticket_fare_cents: u16,
21 pub group_size: u8,
22
23 pub extra_zone: bool,
26 pub period_pass_validity_area: ValidityArea,
28 pub extension_product_code: ProductCode,
29 pub extension_1_validity_area: ValidityArea,
30 pub extension_1_fare_cents: u16,
31 pub extension_2_validity_area: ValidityArea,
32 pub extension_2_fare_cents: u16,
33 pub sale_status: bool,
34
35 pub validity_start_datetime: DateTime<Utc>,
37 pub validity_end_datetime: DateTime<Utc>,
39 pub validity_status: bool,
41
42 pub boarding_datetime: DateTime<Utc>,
44 pub boarding_vehicle: u16,
45 pub boarding_location: BoardingLocation,
46 pub boarding_direction: BoardingDirection,
47 pub boarding_area: BoardingArea,
48}
49
50pub fn create_e_ticket(e_ticket: &[u8]) -> ETicket {
51 let product_code_type = get_bits_as_u8(e_ticket, 0, 1);
52 let product_code_value = get_bits_as_u16(e_ticket, 1, 14);
53 let product_code_group_value = get_bits_as_u16(e_ticket, 15, 14);
54 let product_code = single_or_group(product_code_value, product_code_group_value);
55
56 let customer_profile_value = get_bits_as_u8(e_ticket, 29, 5);
57 let customer_profile_group_value = get_bits_as_u8(e_ticket, 34, 5);
58 let customer_profile = single_or_group(customer_profile_value, customer_profile_group_value);
59 let language_code = get_bits_as_u8(e_ticket, 39, 2);
60
61 let validity_length_type_value = get_bits_as_u8(e_ticket, 41, 2);
62 let validity_length_value = get_bits_as_u8(e_ticket, 43, 8);
63 let validity_length_type_group_value = get_bits_as_u8(e_ticket, 51, 2);
64 let validity_length_group_value = get_bits_as_u8(e_ticket, 53, 8);
65 let validity_length_type =
66 single_or_group(validity_length_type_value, validity_length_type_group_value);
67 let validity_length = single_or_group(validity_length_value, validity_length_group_value);
68 let validity_area_type = get_bits_as_u8(e_ticket, 61, 2);
69 let validity_area_value = get_bits_as_u8(e_ticket, 63, 6);
70
71 let sale_date = get_bits_as_u16(e_ticket, 69, 14);
72 let sale_hour_as_minutes = get_bits_as_u16(e_ticket, 83, 5) * 60; let sale_datetime = from_en1545_date_and_time(sale_date, sale_hour_as_minutes);
74 let sale_device_type = get_bits_as_u8(e_ticket, 88, 3);
75 let sale_device_number = get_bits_as_u16(e_ticket, 91, 14);
76
77 let ticket_fare_value = get_bits_as_u16(e_ticket, 105, 14);
78 let ticket_fare_group_value = get_bits_as_u16(e_ticket, 119, 14);
79 let ticket_fare = single_or_group(ticket_fare_value, ticket_fare_group_value);
80 let group_size = get_bits_as_u8(e_ticket, 133, 6);
81 let extra_zone = get_bits_as_u8(e_ticket, 139, 1) != 0;
82
83 let period_pass_validity_area = get_bits_as_u8(e_ticket, 140, 6);
84 let extension_product_code = get_bits_as_u16(e_ticket, 146, 14);
85 let extension_1_validity_area = get_bits_as_u8(e_ticket, 160, 6);
86 let extension_1_fare_cents = get_bits_as_u16(e_ticket, 166, 14);
87 let extension_2_validity_area = get_bits_as_u8(e_ticket, 180, 6);
88 let extension_2_fare_cents = get_bits_as_u16(e_ticket, 186, 14);
89 let sale_status = get_bits_as_u8(e_ticket, 200, 1) != 0;
90
91 let validity_start_date = get_bits_as_u16(e_ticket, 205, 14);
92 let validity_start_time = get_bits_as_u16(e_ticket, 219, 11);
93 let validity_end_date_value = get_bits_as_u16(e_ticket, 230, 14);
94 let validity_end_time_value = get_bits_as_u16(e_ticket, 244, 11);
95 let validity_end_date_group_value = get_bits_as_u16(e_ticket, 255, 14);
96 let valdiity_end_time_group_value = get_bits_as_u16(e_ticket, 269, 11);
97 let validity_end_datetime = from_en1545_date_and_time(
98 single_or_group(validity_end_date_value, validity_end_date_group_value),
99 single_or_group(validity_end_time_value, valdiity_end_time_group_value),
100 );
101 let validity_status = get_bits_as_u8(e_ticket, 285, 1) != 0;
102
103 let boarding_date = get_bits_as_u16(e_ticket, 286, 14);
104 let boarding_time = get_bits_as_u16(e_ticket, 300, 11);
105 let boarding_vehicle = get_bits_as_u16(e_ticket, 311, 14);
106 let boarding_location_num_type = get_bits_as_u8(e_ticket, 325, 2);
107 let boarding_location_num = get_bits_as_u16(e_ticket, 327, 14);
108 let boarding_direction = get_bits_as_u8(e_ticket, 341, 1);
109 let boarding_area_type = get_bits_as_u8(e_ticket, 342, 2);
110 let boarding_area = get_bits_as_u8(e_ticket, 344, 6);
111
112 ETicket {
113 product_code: ProductCode::new(product_code_type, product_code),
114 customer_profile,
115 language: Language::from(language_code),
116 validity_length: ValidityLength::new(validity_length_type, validity_length),
117 validity_area: ValidityArea::new(validity_area_type, validity_area_value),
118 sale_datetime,
119 sale_device: SaleDevice::new(sale_device_type, sale_device_number),
120 ticket_fare_cents: ticket_fare,
121 group_size,
122
123 extra_zone,
124 period_pass_validity_area: ValidityArea::new(
125 ValidityArea::OLD_ZONE_TYPE,
126 period_pass_validity_area,
127 ),
128 extension_product_code: ProductCode::new(
129 ProductCode::FARES_2014_TYPE,
130 extension_product_code,
131 ),
132 extension_1_validity_area: ValidityArea::new(
133 ValidityArea::OLD_ZONE_TYPE,
134 extension_1_validity_area,
135 ),
136 extension_1_fare_cents,
137 extension_2_validity_area: ValidityArea::new(
138 ValidityArea::OLD_ZONE_TYPE,
139 extension_2_validity_area,
140 ),
141 extension_2_fare_cents,
142 sale_status,
143
144 validity_start_datetime: from_en1545_date_and_time(
145 validity_start_date,
146 validity_start_time,
147 ),
148 validity_end_datetime,
149 validity_status,
150 boarding_datetime: from_en1545_date_and_time(boarding_date, boarding_time),
151 boarding_vehicle,
152 boarding_location: BoardingLocation::new(boarding_location_num_type, boarding_location_num),
153 boarding_direction: BoardingDirection::from(boarding_direction),
154 boarding_area: BoardingArea::new(boarding_area_type, boarding_area),
155 }
156}
157
158fn single_or_group<T: Zero + PartialOrd>(single: T, group: T) -> T {
159 if group > T::zero() {
160 group
161 } else {
162 single
163 }
164}