1use std::collections::BTreeMap;
41use std::sync::Arc;
42use std::time::Duration;
43
44use bytes::Buf;
45use time::OffsetDateTime;
46use tokio::time::sleep;
47use tracing::{debug, info, warn};
48
49use crate::commands::{HISTORY_V1_REQUEST, HISTORY_V2_REQUEST};
50use crate::device::Device;
51use crate::error::{Error, Result};
52use crate::uuid::{COMMAND, HISTORY_V2, READ_INTERVAL, SECONDS_SINCE_UPDATE, TOTAL_READINGS};
53use aranet_types::HistoryRecord;
54
55#[derive(Debug, Clone)]
57pub struct HistoryProgress {
58 pub current_param: HistoryParam,
60 pub param_index: usize,
62 pub total_params: usize,
64 pub values_downloaded: usize,
66 pub total_values: usize,
68 pub overall_progress: f32,
70}
71
72impl HistoryProgress {
73 pub fn new(
75 param: HistoryParam,
76 param_idx: usize,
77 total_params: usize,
78 total_values: usize,
79 ) -> Self {
80 Self {
81 current_param: param,
82 param_index: param_idx,
83 total_params,
84 values_downloaded: 0,
85 total_values,
86 overall_progress: 0.0,
87 }
88 }
89
90 fn update(&mut self, values_downloaded: usize) {
91 self.values_downloaded = values_downloaded;
92 let param_progress = if self.total_values > 0 {
93 values_downloaded as f32 / self.total_values as f32
94 } else {
95 1.0
96 };
97 let base_progress = (self.param_index - 1) as f32 / self.total_params as f32;
98 let param_contribution = param_progress / self.total_params as f32;
99 self.overall_progress = base_progress + param_contribution;
100 }
101}
102
103pub type ProgressCallback = Arc<dyn Fn(HistoryProgress) + Send + Sync>;
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108#[repr(u8)]
109pub enum HistoryParam {
110 Temperature = 1,
111 Humidity = 2,
112 Pressure = 3,
113 Co2 = 4,
114 Humidity2 = 5,
116 Radon = 10,
118}
119
120#[derive(Clone)]
138pub struct HistoryOptions {
139 pub start_index: Option<u16>,
141 pub end_index: Option<u16>,
143 pub read_delay: Duration,
145 pub progress_callback: Option<ProgressCallback>,
147}
148
149impl std::fmt::Debug for HistoryOptions {
150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 f.debug_struct("HistoryOptions")
152 .field("start_index", &self.start_index)
153 .field("end_index", &self.end_index)
154 .field("read_delay", &self.read_delay)
155 .field("progress_callback", &self.progress_callback.is_some())
156 .finish()
157 }
158}
159
160impl Default for HistoryOptions {
161 fn default() -> Self {
162 Self {
163 start_index: None,
164 end_index: None,
165 read_delay: Duration::from_millis(50),
166 progress_callback: None,
167 }
168 }
169}
170
171impl HistoryOptions {
172 #[must_use]
174 pub fn new() -> Self {
175 Self::default()
176 }
177
178 #[must_use]
180 pub fn start_index(mut self, index: u16) -> Self {
181 self.start_index = Some(index);
182 self
183 }
184
185 #[must_use]
187 pub fn end_index(mut self, index: u16) -> Self {
188 self.end_index = Some(index);
189 self
190 }
191
192 #[must_use]
194 pub fn read_delay(mut self, delay: Duration) -> Self {
195 self.read_delay = delay;
196 self
197 }
198
199 #[must_use]
201 pub fn with_progress<F>(mut self, callback: F) -> Self
202 where
203 F: Fn(HistoryProgress) + Send + Sync + 'static,
204 {
205 self.progress_callback = Some(Arc::new(callback));
206 self
207 }
208
209 pub fn report_progress(&self, progress: &HistoryProgress) {
211 if let Some(cb) = &self.progress_callback {
212 cb(progress.clone());
213 }
214 }
215}
216
217#[derive(Debug, Clone)]
219pub struct HistoryInfo {
220 pub total_readings: u16,
222 pub interval_seconds: u16,
224 pub seconds_since_update: u16,
226}
227
228impl Device {
229 pub async fn get_history_info(&self) -> Result<HistoryInfo> {
231 let total_data = self.read_characteristic(TOTAL_READINGS).await?;
233 let total_readings = if total_data.len() >= 2 {
234 u16::from_le_bytes([total_data[0], total_data[1]])
235 } else {
236 return Err(Error::InvalidData(
237 "Invalid total readings data".to_string(),
238 ));
239 };
240
241 let interval_data = self.read_characteristic(READ_INTERVAL).await?;
243 let interval_seconds = if interval_data.len() >= 2 {
244 u16::from_le_bytes([interval_data[0], interval_data[1]])
245 } else {
246 return Err(Error::InvalidData("Invalid interval data".to_string()));
247 };
248
249 let age_data = self.read_characteristic(SECONDS_SINCE_UPDATE).await?;
251 let seconds_since_update = if age_data.len() >= 2 {
252 u16::from_le_bytes([age_data[0], age_data[1]])
253 } else {
254 0
255 };
256
257 Ok(HistoryInfo {
258 total_readings,
259 interval_seconds,
260 seconds_since_update,
261 })
262 }
263
264 pub async fn download_history(&self) -> Result<Vec<HistoryRecord>> {
266 self.download_history_with_options(HistoryOptions::default())
267 .await
268 }
269
270 pub async fn download_history_with_options(
281 &self,
282 options: HistoryOptions,
283 ) -> Result<Vec<HistoryRecord>> {
284 use aranet_types::DeviceType;
285
286 let info = self.get_history_info().await?;
287 info!(
288 "Device has {} readings, interval {}s, last update {}s ago",
289 info.total_readings, info.interval_seconds, info.seconds_since_update
290 );
291
292 if info.total_readings == 0 {
293 return Ok(Vec::new());
294 }
295
296 let start_idx = options.start_index.unwrap_or(1);
297 let end_idx = options.end_index.unwrap_or(info.total_readings);
298
299 let is_radon = matches!(self.device_type(), Some(DeviceType::AranetRadon));
301
302 if is_radon {
303 self.download_radon_history_internal(&info, start_idx, end_idx, &options)
305 .await
306 } else {
307 self.download_aranet4_history_internal(&info, start_idx, end_idx, &options)
309 .await
310 }
311 }
312
313 async fn download_aranet4_history_internal(
315 &self,
316 info: &HistoryInfo,
317 start_idx: u16,
318 end_idx: u16,
319 options: &HistoryOptions,
320 ) -> Result<Vec<HistoryRecord>> {
321 let total_values = (end_idx - start_idx + 1) as usize;
322
323 let mut progress = HistoryProgress::new(HistoryParam::Co2, 1, 4, total_values);
325 options.report_progress(&progress);
326
327 let co2_values = self
328 .download_param_history_with_progress(
329 HistoryParam::Co2,
330 start_idx,
331 end_idx,
332 options.read_delay,
333 |downloaded| {
334 progress.update(downloaded);
335 options.report_progress(&progress);
336 },
337 )
338 .await?;
339
340 progress = HistoryProgress::new(HistoryParam::Temperature, 2, 4, total_values);
341 options.report_progress(&progress);
342
343 let temp_values = self
344 .download_param_history_with_progress(
345 HistoryParam::Temperature,
346 start_idx,
347 end_idx,
348 options.read_delay,
349 |downloaded| {
350 progress.update(downloaded);
351 options.report_progress(&progress);
352 },
353 )
354 .await?;
355
356 progress = HistoryProgress::new(HistoryParam::Pressure, 3, 4, total_values);
357 options.report_progress(&progress);
358
359 let pressure_values = self
360 .download_param_history_with_progress(
361 HistoryParam::Pressure,
362 start_idx,
363 end_idx,
364 options.read_delay,
365 |downloaded| {
366 progress.update(downloaded);
367 options.report_progress(&progress);
368 },
369 )
370 .await?;
371
372 progress = HistoryProgress::new(HistoryParam::Humidity, 4, 4, total_values);
373 options.report_progress(&progress);
374
375 let humidity_values = self
376 .download_param_history_with_progress(
377 HistoryParam::Humidity,
378 start_idx,
379 end_idx,
380 options.read_delay,
381 |downloaded| {
382 progress.update(downloaded);
383 options.report_progress(&progress);
384 },
385 )
386 .await?;
387
388 let now = OffsetDateTime::now_utc();
390 let latest_reading_time = now - time::Duration::seconds(info.seconds_since_update as i64);
391
392 let mut records = Vec::new();
394 let count = co2_values.len();
395
396 for i in 0..count {
397 let readings_ago = (count - 1 - i) as i64;
399 let timestamp = latest_reading_time
400 - time::Duration::seconds(readings_ago * info.interval_seconds as i64);
401
402 let record = HistoryRecord {
403 timestamp,
404 co2: co2_values.get(i).copied().unwrap_or(0),
405 temperature: raw_to_temperature(temp_values.get(i).copied().unwrap_or(0)),
406 pressure: raw_to_pressure(pressure_values.get(i).copied().unwrap_or(0)),
407 humidity: humidity_values.get(i).copied().unwrap_or(0) as u8,
408 radon: None,
409 radiation_rate: None,
410 radiation_total: None,
411 };
412 records.push(record);
413 }
414
415 info!("Downloaded {} history records", records.len());
416 Ok(records)
417 }
418
419 async fn download_radon_history_internal(
421 &self,
422 info: &HistoryInfo,
423 start_idx: u16,
424 end_idx: u16,
425 options: &HistoryOptions,
426 ) -> Result<Vec<HistoryRecord>> {
427 let total_values = (end_idx - start_idx + 1) as usize;
428
429 let mut progress = HistoryProgress::new(HistoryParam::Radon, 1, 4, total_values);
431 options.report_progress(&progress);
432
433 let radon_values = self
434 .download_param_history_u32_with_progress(
435 HistoryParam::Radon,
436 start_idx,
437 end_idx,
438 options.read_delay,
439 |downloaded| {
440 progress.update(downloaded);
441 options.report_progress(&progress);
442 },
443 )
444 .await?;
445
446 progress = HistoryProgress::new(HistoryParam::Temperature, 2, 4, total_values);
447 options.report_progress(&progress);
448
449 let temp_values = self
450 .download_param_history_with_progress(
451 HistoryParam::Temperature,
452 start_idx,
453 end_idx,
454 options.read_delay,
455 |downloaded| {
456 progress.update(downloaded);
457 options.report_progress(&progress);
458 },
459 )
460 .await?;
461
462 progress = HistoryProgress::new(HistoryParam::Pressure, 3, 4, total_values);
463 options.report_progress(&progress);
464
465 let pressure_values = self
466 .download_param_history_with_progress(
467 HistoryParam::Pressure,
468 start_idx,
469 end_idx,
470 options.read_delay,
471 |downloaded| {
472 progress.update(downloaded);
473 options.report_progress(&progress);
474 },
475 )
476 .await?;
477
478 progress = HistoryProgress::new(HistoryParam::Humidity2, 4, 4, total_values);
480 options.report_progress(&progress);
481
482 let humidity_values = self
483 .download_param_history_with_progress(
484 HistoryParam::Humidity2,
485 start_idx,
486 end_idx,
487 options.read_delay,
488 |downloaded| {
489 progress.update(downloaded);
490 options.report_progress(&progress);
491 },
492 )
493 .await?;
494
495 let now = OffsetDateTime::now_utc();
497 let latest_reading_time = now - time::Duration::seconds(info.seconds_since_update as i64);
498
499 let mut records = Vec::new();
501 let count = radon_values.len();
502
503 for i in 0..count {
504 let readings_ago = (count - 1 - i) as i64;
506 let timestamp = latest_reading_time
507 - time::Duration::seconds(readings_ago * info.interval_seconds as i64);
508
509 let humidity_raw = humidity_values.get(i).copied().unwrap_or(0);
511 let humidity = (humidity_raw / 10).min(100) as u8;
512
513 let record = HistoryRecord {
514 timestamp,
515 co2: 0, temperature: raw_to_temperature(temp_values.get(i).copied().unwrap_or(0)),
517 pressure: raw_to_pressure(pressure_values.get(i).copied().unwrap_or(0)),
518 humidity,
519 radon: Some(radon_values.get(i).copied().unwrap_or(0)),
520 radiation_rate: None,
521 radiation_total: None,
522 };
523 records.push(record);
524 }
525
526 info!("Downloaded {} radon history records", records.len());
527 Ok(records)
528 }
529
530 #[allow(clippy::too_many_arguments)]
537 async fn download_param_history_generic_with_progress<T, F>(
538 &self,
539 param: HistoryParam,
540 start_idx: u16,
541 end_idx: u16,
542 read_delay: Duration,
543 value_parser: impl Fn(&[u8], usize) -> Option<T>,
544 value_size: usize,
545 mut on_progress: F,
546 ) -> Result<Vec<T>>
547 where
548 T: Default + Clone,
549 F: FnMut(usize),
550 {
551 debug!(
552 "Downloading {:?} history from {} to {} (value_size={})",
553 param, start_idx, end_idx, value_size
554 );
555
556 let mut values: BTreeMap<u16, T> = BTreeMap::new();
557 let mut current_idx = start_idx;
558
559 while current_idx <= end_idx {
560 let cmd = [
562 HISTORY_V2_REQUEST,
563 param as u8,
564 (current_idx & 0xFF) as u8,
565 ((current_idx >> 8) & 0xFF) as u8,
566 ];
567
568 self.write_characteristic(COMMAND, &cmd).await?;
569 sleep(read_delay).await;
570
571 let response = self.read_characteristic(HISTORY_V2).await?;
573
574 if response.len() < 10 {
583 warn!(
584 "Invalid history response: too short ({} bytes)",
585 response.len()
586 );
587 break;
588 }
589
590 let resp_param = response[0];
591 if resp_param != param as u8 {
592 warn!("Unexpected parameter in response: {}", resp_param);
593 sleep(read_delay).await;
595 continue;
596 }
597
598 let resp_start = u16::from_le_bytes([response[7], response[8]]);
600 let resp_count = response[9] as usize;
601
602 debug!(
603 "History response: param={}, start={}, count={}",
604 resp_param, resp_start, resp_count
605 );
606
607 if resp_count == 0 {
609 debug!("Reached end of history (count=0)");
610 break;
611 }
612
613 let data = &response[10..];
615 let num_values = (data.len() / value_size).min(resp_count);
616
617 for i in 0..num_values {
618 let idx = resp_start + i as u16;
619 if idx > end_idx {
620 break;
621 }
622 if let Some(value) = value_parser(data, i) {
623 values.insert(idx, value);
624 }
625 }
626
627 current_idx = resp_start + num_values as u16;
628 debug!(
629 "Downloaded {} values, next index: {}",
630 num_values, current_idx
631 );
632
633 on_progress(values.len());
635
636 if (resp_start as usize + resp_count) >= end_idx as usize {
638 debug!("Reached end of requested range");
639 break;
640 }
641 }
642
643 Ok(values.into_values().collect())
645 }
646
647 async fn download_param_history_with_progress<F>(
649 &self,
650 param: HistoryParam,
651 start_idx: u16,
652 end_idx: u16,
653 read_delay: Duration,
654 on_progress: F,
655 ) -> Result<Vec<u16>>
656 where
657 F: FnMut(usize),
658 {
659 let value_size = if param == HistoryParam::Humidity {
660 1
661 } else {
662 2
663 };
664
665 self.download_param_history_generic_with_progress(
666 param,
667 start_idx,
668 end_idx,
669 read_delay,
670 |data, i| {
671 if param == HistoryParam::Humidity {
672 data.get(i).map(|&b| b as u16)
673 } else {
674 let offset = i * 2;
675 if offset + 1 < data.len() {
676 Some(u16::from_le_bytes([data[offset], data[offset + 1]]))
677 } else {
678 None
679 }
680 }
681 },
682 value_size,
683 on_progress,
684 )
685 .await
686 }
687
688 async fn download_param_history_u32_with_progress<F>(
690 &self,
691 param: HistoryParam,
692 start_idx: u16,
693 end_idx: u16,
694 read_delay: Duration,
695 on_progress: F,
696 ) -> Result<Vec<u32>>
697 where
698 F: FnMut(usize),
699 {
700 self.download_param_history_generic_with_progress(
701 param,
702 start_idx,
703 end_idx,
704 read_delay,
705 |data, i| {
706 let offset = i * 4;
707 if offset + 3 < data.len() {
708 Some(u32::from_le_bytes([
709 data[offset],
710 data[offset + 1],
711 data[offset + 2],
712 data[offset + 3],
713 ]))
714 } else {
715 None
716 }
717 },
718 4,
719 on_progress,
720 )
721 .await
722 }
723
724 pub async fn download_history_v1(&self) -> Result<Vec<HistoryRecord>> {
729 use crate::uuid::HISTORY_V1;
730 use tokio::sync::mpsc;
731
732 let info = self.get_history_info().await?;
733 info!(
734 "V1 download: {} readings, interval {}s",
735 info.total_readings, info.interval_seconds
736 );
737
738 if info.total_readings == 0 {
739 return Ok(Vec::new());
740 }
741
742 let (tx, mut rx) = mpsc::channel::<Vec<u8>>(256);
744
745 self.subscribe_to_notifications(HISTORY_V1, move |data| {
747 let _ = tx.try_send(data.to_vec());
748 })
749 .await?;
750
751 let mut co2_values = Vec::new();
753 let mut temp_values = Vec::new();
754 let mut pressure_values = Vec::new();
755 let mut humidity_values = Vec::new();
756
757 for param in [
758 HistoryParam::Co2,
759 HistoryParam::Temperature,
760 HistoryParam::Pressure,
761 HistoryParam::Humidity,
762 ] {
763 let cmd = [
765 HISTORY_V1_REQUEST,
766 param as u8,
767 0x01,
768 0x00,
769 (info.total_readings & 0xFF) as u8,
770 ((info.total_readings >> 8) & 0xFF) as u8,
771 ];
772
773 self.write_characteristic(COMMAND, &cmd).await?;
774
775 let mut values = Vec::new();
777 let expected = info.total_readings as usize;
778
779 while values.len() < expected {
780 match tokio::time::timeout(Duration::from_secs(5), rx.recv()).await {
781 Ok(Some(data)) => {
782 if data.len() >= 3 {
784 let resp_param = data[0];
785 if resp_param == param as u8 {
786 let mut buf = &data[3..];
787 while buf.len() >= 2 && values.len() < expected {
788 values.push(buf.get_u16_le());
789 }
790 }
791 }
792 }
793 Ok(None) => break,
794 Err(_) => {
795 warn!("Timeout waiting for V1 history notification");
796 break;
797 }
798 }
799 }
800
801 match param {
802 HistoryParam::Co2 => co2_values = values,
803 HistoryParam::Temperature => temp_values = values,
804 HistoryParam::Pressure => pressure_values = values,
805 HistoryParam::Humidity => humidity_values = values,
806 HistoryParam::Humidity2 | HistoryParam::Radon => {}
808 }
809 }
810
811 self.unsubscribe_from_notifications(HISTORY_V1).await?;
813
814 let now = OffsetDateTime::now_utc();
816 let latest_reading_time = now - time::Duration::seconds(info.seconds_since_update as i64);
817
818 let mut records = Vec::new();
819 let count = co2_values.len();
820
821 for i in 0..count {
822 let readings_ago = (count - 1 - i) as i64;
823 let timestamp = latest_reading_time
824 - time::Duration::seconds(readings_ago * info.interval_seconds as i64);
825
826 let record = HistoryRecord {
827 timestamp,
828 co2: co2_values.get(i).copied().unwrap_or(0),
829 temperature: raw_to_temperature(temp_values.get(i).copied().unwrap_or(0)),
830 pressure: raw_to_pressure(pressure_values.get(i).copied().unwrap_or(0)),
831 humidity: humidity_values.get(i).copied().unwrap_or(0) as u8,
832 radon: None,
833 radiation_rate: None,
834 radiation_total: None,
835 };
836 records.push(record);
837 }
838
839 info!("V1 download complete: {} records", records.len());
840 Ok(records)
841 }
842}
843
844pub fn raw_to_temperature(raw: u16) -> f32 {
846 raw as f32 / 20.0
847}
848
849pub fn raw_to_pressure(raw: u16) -> f32 {
851 raw as f32 / 10.0
852}
853
854#[cfg(test)]
858mod tests {
859 use super::*;
860
861 #[test]
864 fn test_raw_to_temperature_typical_values() {
865 assert!((raw_to_temperature(450) - 22.5).abs() < 0.001);
867
868 assert!((raw_to_temperature(400) - 20.0).abs() < 0.001);
870
871 assert!((raw_to_temperature(500) - 25.0).abs() < 0.001);
873 }
874
875 #[test]
876 fn test_raw_to_temperature_edge_cases() {
877 assert!((raw_to_temperature(0) - 0.0).abs() < 0.001);
879
880 assert!((raw_to_temperature(1000) - 50.0).abs() < 0.001);
885
886 assert!((raw_to_temperature(u16::MAX) - 3276.75).abs() < 0.01);
888 }
889
890 #[test]
891 fn test_raw_to_temperature_precision() {
892 assert!((raw_to_temperature(451) - 22.55).abs() < 0.001);
895
896 assert!((raw_to_temperature(441) - 22.05).abs() < 0.001);
898 }
899
900 #[test]
903 fn test_raw_to_pressure_typical_values() {
904 assert!((raw_to_pressure(10132) - 1013.2).abs() < 0.01);
906
907 assert!((raw_to_pressure(10000) - 1000.0).abs() < 0.01);
909
910 assert!((raw_to_pressure(10500) - 1050.0).abs() < 0.01);
912 }
913
914 #[test]
915 fn test_raw_to_pressure_edge_cases() {
916 assert!((raw_to_pressure(0) - 0.0).abs() < 0.01);
918
919 assert!((raw_to_pressure(9500) - 950.0).abs() < 0.01);
921
922 assert!((raw_to_pressure(11000) - 1100.0).abs() < 0.01);
924
925 assert!((raw_to_pressure(u16::MAX) - 6553.5).abs() < 0.1);
927 }
928
929 #[test]
932 fn test_history_param_values() {
933 assert_eq!(HistoryParam::Temperature as u8, 1);
934 assert_eq!(HistoryParam::Humidity as u8, 2);
935 assert_eq!(HistoryParam::Pressure as u8, 3);
936 assert_eq!(HistoryParam::Co2 as u8, 4);
937 }
938
939 #[test]
940 fn test_history_param_debug() {
941 assert_eq!(format!("{:?}", HistoryParam::Temperature), "Temperature");
942 assert_eq!(format!("{:?}", HistoryParam::Co2), "Co2");
943 }
944
945 #[test]
948 fn test_history_options_default() {
949 let options = HistoryOptions::default();
950
951 assert!(options.start_index.is_none());
952 assert!(options.end_index.is_none());
953 assert_eq!(options.read_delay, Duration::from_millis(50));
954 }
955
956 #[test]
957 fn test_history_options_custom() {
958 let options = HistoryOptions::new()
959 .start_index(10)
960 .end_index(100)
961 .read_delay(Duration::from_millis(100));
962
963 assert_eq!(options.start_index, Some(10));
964 assert_eq!(options.end_index, Some(100));
965 assert_eq!(options.read_delay, Duration::from_millis(100));
966 }
967
968 #[test]
969 fn test_history_options_with_progress() {
970 use std::sync::Arc;
971 use std::sync::atomic::{AtomicUsize, Ordering};
972
973 let call_count = Arc::new(AtomicUsize::new(0));
974 let call_count_clone = Arc::clone(&call_count);
975
976 let options = HistoryOptions::new().with_progress(move |_progress| {
977 call_count_clone.fetch_add(1, Ordering::SeqCst);
978 });
979
980 assert!(options.progress_callback.is_some());
981
982 let progress = HistoryProgress::new(HistoryParam::Co2, 1, 4, 100);
984 options.report_progress(&progress);
985 assert_eq!(call_count.load(Ordering::SeqCst), 1);
986 }
987
988 #[test]
991 fn test_history_info_creation() {
992 let info = HistoryInfo {
993 total_readings: 1000,
994 interval_seconds: 300,
995 seconds_since_update: 120,
996 };
997
998 assert_eq!(info.total_readings, 1000);
999 assert_eq!(info.interval_seconds, 300);
1000 assert_eq!(info.seconds_since_update, 120);
1001 }
1002
1003 #[test]
1004 fn test_history_info_debug() {
1005 let info = HistoryInfo {
1006 total_readings: 500,
1007 interval_seconds: 60,
1008 seconds_since_update: 30,
1009 };
1010
1011 let debug_str = format!("{:?}", info);
1012 assert!(debug_str.contains("total_readings"));
1013 assert!(debug_str.contains("500"));
1014 }
1015}