Skip to main content

Mountain/IPC/StatusReporter/
Reporter.rs

1//! `StatusReporter` aggregator - holds the IPC server handle,
2//! status history ring (last 100), error counter, performance
3//! / health / service-registry shared state, and emits
4//! periodic snapshots to Sky.
5//!
6//! The struct + 30-method impl live in one file because the
7//! method bodies are tightly coupled with the private fields
8//! and with the DTO siblings; splitting per-method forces
9//! ~30 trivial wrappers without payback.
10
11use std::{
12	collections::{HashMap, HashSet},
13	sync::{Arc, Mutex},
14	time::{Duration, SystemTime},
15};
16
17use tauri::Emitter;
18use tokio::sync::RwLock;
19
20use crate::{
21	IPC::StatusReporter::{
22		ComprehensiveStatusReport::Struct as ComprehensiveStatusReport,
23		ConnectionStatus::Struct as ConnectionStatus,
24		HealthIssue::Struct as HealthIssue,
25		HealthIssueType::Enum as HealthIssueType,
26		HealthMonitor::Struct as HealthMonitor,
27		IPCStatusReport::Struct as IPCStatusReport,
28		MessageStats::Struct as MessageStats,
29		PerformanceMetrics::Struct as PerformanceMetrics,
30		ServiceInfo::Struct as ServiceInfo,
31		ServiceMetrics::Struct as ServiceMetrics,
32		ServiceRegistry::Struct as ServiceRegistry,
33		ServiceStatus::Enum as ServiceStatus,
34		SeverityLevel::Enum as SeverityLevel,
35	},
36	RunTime::ApplicationRunTime::ApplicationRunTime,
37	dev_log,
38};
39
40pub struct Struct {
41	pub(super) runtime:Arc<ApplicationRunTime>,
42
43	pub(super) ipc_server:Option<Arc<crate::IPC::TauriIPCServer_Old::TauriIPCServer>>,
44
45	pub(super) status_history:Arc<Mutex<Vec<IPCStatusReport>>>,
46
47	pub(super) start_time:SystemTime,
48
49	pub(super) error_count:Arc<Mutex<u32>>,
50
51	pub(super) performance_metrics:Arc<Mutex<PerformanceMetrics>>,
52
53	pub(super) health_monitor:Arc<Mutex<HealthMonitor>>,
54
55	pub(super) service_registry:Arc<RwLock<ServiceRegistry>>,
56
57	pub(super) discovered_services:Arc<RwLock<HashSet<String>>>,
58}
59
60impl Struct {
61	pub fn new(runtime:Arc<ApplicationRunTime>) -> Self {
62		dev_log!("lifecycle", "Creating IPC status reporter");
63
64		Self {
65			runtime,
66
67			ipc_server:None,
68
69			status_history:Arc::new(Mutex::new(Vec::new())),
70
71			start_time:SystemTime::now(),
72
73			error_count:Arc::new(Mutex::new(0)),
74
75			performance_metrics:Arc::new(Mutex::new(PerformanceMetrics {
76				messages_per_second:0.0,
77				average_latency_ms:0.0,
78				peak_latency_ms:0.0,
79				compression_ratio:1.0,
80				connection_pool_utilization:0.0,
81				memory_usage_mb:0.0,
82				cpu_usage_percent:0.0,
83				last_update:SystemTime::now()
84					.duration_since(SystemTime::UNIX_EPOCH)
85					.unwrap_or_default()
86					.as_millis() as u64,
87			})),
88
89			health_monitor:Arc::new(Mutex::new(HealthMonitor {
90				health_score:100.0,
91				last_health_check:SystemTime::now()
92					.duration_since(SystemTime::UNIX_EPOCH)
93					.unwrap_or_default()
94					.as_millis() as u64,
95				issues_detected:Vec::new(),
96				recovery_attempts:0,
97			})),
98
99			service_registry:Arc::new(RwLock::new(ServiceRegistry {
100				services:HashMap::new(),
101				last_discovery:SystemTime::now()
102					.duration_since(SystemTime::UNIX_EPOCH)
103					.unwrap_or_default()
104					.as_millis() as u64,
105				discovery_interval:30000,
106			})),
107
108			discovered_services:Arc::new(RwLock::new(HashSet::new())),
109		}
110	}
111
112	pub fn set_ipc_server(&mut self, ipc_server:Arc<crate::IPC::TauriIPCServer_Old::TauriIPCServer>) {
113		self.ipc_server = Some(ipc_server);
114	}
115
116	pub async fn generate_status_report(&self) -> Result<IPCStatusReport, String> {
117		dev_log!("lifecycle", "Generating IPC status report");
118
119		let ipc_server = self.ipc_server.as_ref().ok_or("IPC Server not set".to_string())?;
120
121		let connection_status = ConnectionStatus {
122			is_connected:ipc_server.get_connection_status()?,
123
124			last_heartbeat:SystemTime::now()
125				.duration_since(SystemTime::UNIX_EPOCH)
126				.unwrap_or_default()
127				.as_secs(),
128
129			connection_duration:SystemTime::now().duration_since(self.start_time).unwrap_or_default().as_secs(),
130		};
131
132		let message_queue_size = ipc_server.get_queue_size()?;
133
134		let active_listeners = vec!["configuration".to_string(), "file".to_string(), "storage".to_string()];
135
136		let recent_messages = vec![
137			MessageStats {
138				channel:"configuration".to_string(),
139
140				message_count:10,
141
142				last_message_time:SystemTime::now()
143					.duration_since(SystemTime::UNIX_EPOCH)
144					.unwrap_or_default()
145					.as_secs(),
146
147				average_processing_time_ms:5.0,
148			},
149			MessageStats {
150				channel:"file".to_string(),
151
152				message_count:5,
153
154				last_message_time:SystemTime::now()
155					.duration_since(SystemTime::UNIX_EPOCH)
156					.unwrap_or_default()
157					.as_secs() - 10,
158
159				average_processing_time_ms:15.0,
160			},
161		];
162
163		let error_count = {
164			let guard = self
165				.error_count
166				.lock()
167				.map_err(|e| format!("Failed to get error count: {}", e))?;
168
169			*guard
170		};
171
172		let uptime_seconds = SystemTime::now().duration_since(self.start_time).unwrap_or_default().as_secs();
173
174		let report = IPCStatusReport {
175			timestamp:SystemTime::now()
176				.duration_since(SystemTime::UNIX_EPOCH)
177				.unwrap_or_default()
178				.as_millis() as u64,
179
180			connection_status,
181
182			message_queue_size,
183
184			active_listeners,
185
186			recent_messages,
187
188			error_count,
189
190			uptime_seconds,
191		};
192
193		{
194			let mut history = self
195				.status_history
196				.lock()
197				.map_err(|e| format!("Failed to access status history: {}", e))?;
198
199			history.push(report.clone());
200
201			if history.len() > 100 {
202				history.remove(0);
203			}
204		}
205
206		Ok(report)
207	}
208
209	pub async fn report_to_sky(&self) -> Result<(), String> {
210		dev_log!("lifecycle", "Reporting IPC status to Sky");
211
212		let report = self.generate_status_report().await?;
213
214		self.update_performance_metrics().await?;
215
216		self.perform_health_check().await?;
217
218		let performance_metrics = self.get_performance_metrics()?;
219
220		let health_status = self.get_health_status()?;
221
222		let comprehensive_report = ComprehensiveStatusReport {
223			basic_status:report.clone(),
224
225			performance_metrics:performance_metrics.clone(),
226
227			health_status:health_status.clone(),
228
229			timestamp:SystemTime::now()
230				.duration_since(SystemTime::UNIX_EPOCH)
231				.unwrap_or_default()
232				.as_millis() as u64,
233		};
234
235		if let Err(e) = self
236			.runtime
237			.Environment
238			.ApplicationHandle
239			.emit("ipc-status-report", &comprehensive_report)
240		{
241			dev_log!(
242				"lifecycle",
243				"error: [StatusReporter] Failed to emit status report to Sky: {}",
244				e
245			);
246
247			return Err(format!("Failed to emit status report: {}", e));
248		}
249
250		if let Err(e) = self
251			.runtime
252			.Environment
253			.ApplicationHandle
254			.emit("ipc-performance-metrics", &performance_metrics)
255		{
256			dev_log!("lifecycle", "error: [StatusReporter] Failed to emit performance metrics: {}", e);
257		}
258
259		if let Err(e) = self
260			.runtime
261			.Environment
262			.ApplicationHandle
263			.emit("ipc-health-status", &health_status)
264		{
265			dev_log!("lifecycle", "error: [StatusReporter] Failed to emit health status: {}", e);
266		}
267
268		dev_log!("lifecycle", "Comprehensive status report sent to Sky");
269
270		Ok(())
271	}
272
273	pub async fn start_periodic_reporting(&self, interval_seconds:u64) -> Result<(), String> {
274		dev_log!(
275			"lifecycle",
276			"[StatusReporter] Starting periodic status reporting (interval: {}s)",
277			interval_seconds
278		);
279
280		let reporter = self.clone_reporter();
281
282		tokio::spawn(async move {
283			let mut interval = tokio::time::interval(Duration::from_secs(interval_seconds));
284
285			loop {
286				interval.tick().await;
287
288				if let Err(e) = reporter.report_to_sky().await {
289					dev_log!("lifecycle", "error: [StatusReporter] Periodic reporting failed: {}", e);
290				}
291			}
292		});
293
294		Ok(())
295	}
296
297	pub fn record_error(&self) {
298		if let Ok(mut error_count) = self.error_count.lock() {
299			*error_count += 1;
300		}
301	}
302
303	pub fn get_status_history(&self) -> Result<Vec<IPCStatusReport>, String> {
304		let history = self
305			.status_history
306			.lock()
307			.map_err(|e| format!("Failed to access status history: {}", e))?;
308
309		Ok(history.clone())
310	}
311
312	pub fn get_start_time(&self) -> SystemTime { self.start_time }
313
314	pub async fn update_performance_metrics(&self) -> Result<(), String> {
315		let ipc_server = self.ipc_server.as_ref().ok_or("IPC Server not set".to_string())?;
316
317		let connection_stats = ipc_server.get_connection_stats().await.unwrap_or_default();
318
319		let messages_per_second = self.calculate_message_rate().await;
320
321		let average_latency_ms = self.calculate_average_latency().await;
322
323		let peak_latency_ms = self.calculate_peak_latency().await;
324
325		let compression_ratio = self.calculate_compression_ratio().await;
326
327		let connection_pool_utilization = self.calculate_pool_utilization(&connection_stats).await;
328
329		let memory_usage_mb = self.get_memory_usage().await;
330
331		let cpu_usage_percent = self.get_cpu_usage().await;
332
333		let last_update = SystemTime::now()
334			.duration_since(SystemTime::UNIX_EPOCH)
335			.unwrap_or_default()
336			.as_millis() as u64;
337
338		let mut metrics = self
339			.performance_metrics
340			.lock()
341			.map_err(|e| format!("Failed to access performance metrics: {}", e))?;
342
343		metrics.messages_per_second = messages_per_second;
344
345		metrics.average_latency_ms = average_latency_ms;
346
347		metrics.peak_latency_ms = peak_latency_ms;
348
349		metrics.compression_ratio = compression_ratio;
350
351		metrics.connection_pool_utilization = connection_pool_utilization;
352
353		metrics.memory_usage_mb = memory_usage_mb;
354
355		metrics.cpu_usage_percent = cpu_usage_percent;
356
357		metrics.last_update = last_update;
358
359		dev_log!(
360			"lifecycle",
361			"[StatusReporter] Performance metrics updated: {:.2} msg/s, {:.2}ms latency",
362			metrics.messages_per_second,
363			metrics.average_latency_ms
364		);
365
366		Ok(())
367	}
368
369	pub async fn perform_health_check(&self) -> Result<(), String> {
370		let mut health_monitor = self
371			.health_monitor
372			.lock()
373			.map_err(|e| format!("Failed to access health monitor: {}", e))?;
374
375		let mut health_score:f64 = 100.0;
376
377		let mut issues = Vec::new();
378
379		if let Some(ipc_server) = &self.ipc_server {
380			if !ipc_server.get_connection_status()? {
381				health_score -= 25.0;
382
383				issues.push(HealthIssue {
384					issue_type:HealthIssueType::ConnectionLoss,
385					severity:SeverityLevel::Critical,
386					description:"IPC connection lost".to_string(),
387					detected_at:SystemTime::now()
388						.duration_since(SystemTime::UNIX_EPOCH)
389						.unwrap_or_default()
390						.as_millis() as u64,
391					resolved_at:None,
392				});
393			}
394		}
395
396		if let Some(ipc_server) = &self.ipc_server {
397			let queue_size = ipc_server.get_queue_size()?;
398
399			if queue_size > 100 {
400				health_score -= 15.0;
401
402				issues.push(HealthIssue {
403					issue_type:HealthIssueType::QueueOverflow,
404					severity:SeverityLevel::High,
405					description:format!("Message queue overflow: {} messages", queue_size),
406					detected_at:SystemTime::now()
407						.duration_since(SystemTime::UNIX_EPOCH)
408						.unwrap_or_default()
409						.as_millis() as u64,
410					resolved_at:None,
411				});
412			}
413		}
414
415		let metrics = self
416			.performance_metrics
417			.lock()
418			.map_err(|e| format!("Failed to access performance metrics: {}", e))?;
419
420		if metrics.average_latency_ms > 100.0 {
421			health_score -= 20.0;
422
423			issues.push(HealthIssue {
424				issue_type:HealthIssueType::HighLatency,
425				severity:SeverityLevel::High,
426				description:format!("High latency detected: {:.2}ms", metrics.average_latency_ms),
427				detected_at:SystemTime::now()
428					.duration_since(SystemTime::UNIX_EPOCH)
429					.unwrap_or_default()
430					.as_millis() as u64,
431				resolved_at:None,
432			});
433		}
434
435		health_monitor.health_score = health_score.max(0.0);
436
437		health_monitor.issues_detected = issues;
438
439		health_monitor.last_health_check = SystemTime::now()
440			.duration_since(SystemTime::UNIX_EPOCH)
441			.unwrap_or_default()
442			.as_millis() as u64;
443
444		if health_score < 70.0 {
445			dev_log!(
446				"lifecycle",
447				"warn: [StatusReporter] Health check failed: score {:.1}%",
448				health_score
449			);
450
451			if let Err(e) = self
452				.runtime
453				.Environment
454				.ApplicationHandle
455				.emit("ipc-health-alert", &health_monitor.clone())
456			{
457				dev_log!("lifecycle", "error: [StatusReporter] Failed to emit health alert: {}", e);
458			}
459		}
460
461		Ok(())
462	}
463
464	async fn calculate_message_rate(&self) -> f64 {
465		let history = self.get_status_history().unwrap_or_default();
466
467		if history.len() < 2 {
468			return 0.0;
469		}
470
471		let recent_reports:Vec<&IPCStatusReport> = history.iter().rev().take(5).collect();
472
473		let total_messages:u32 = recent_reports
474			.iter()
475			.map(|report| report.recent_messages.iter().map(|m| m.message_count).sum::<u32>())
476			.sum();
477
478		let time_span = if recent_reports.len() > 1 {
479			let first_time = recent_reports.first().unwrap().timestamp;
480
481			let last_time = recent_reports.last().unwrap().timestamp;
482
483			(last_time - first_time) as f64 / 1000.0
484		} else {
485			1.0
486		};
487
488		total_messages as f64 / time_span.max(1.0)
489	}
490
491	async fn calculate_average_latency(&self) -> f64 {
492		let history = self.get_status_history().unwrap_or_default();
493
494		if history.is_empty() {
495			return 0.0;
496		}
497
498		let recent_reports:Vec<&IPCStatusReport> = history.iter().rev().take(10).collect();
499
500		let total_latency:f64 = recent_reports
501			.iter()
502			.flat_map(|report| &report.recent_messages)
503			.map(|msg| msg.average_processing_time_ms)
504			.sum();
505
506		let message_count = recent_reports.iter().flat_map(|report| &report.recent_messages).count();
507
508		total_latency / message_count.max(1) as f64
509	}
510
511	async fn calculate_peak_latency(&self) -> f64 {
512		let history = self.get_status_history().unwrap_or_default();
513
514		history
515			.iter()
516			.flat_map(|report| &report.recent_messages)
517			.map(|msg| msg.average_processing_time_ms)
518			.fold(0.0, f64::max)
519	}
520
521	async fn calculate_compression_ratio(&self) -> f64 { 2.5 }
522
523	async fn calculate_pool_utilization(&self, stats:&crate::IPC::TauriIPCServer_Old::ConnectionStats) -> f64 {
524		if stats.total_connections == 0 {
525			return 0.0;
526		}
527
528		stats.total_connections as f64 / stats.max_connections as f64
529	}
530
531	async fn get_memory_usage(&self) -> f64 { 50.0 }
532
533	async fn get_cpu_usage(&self) -> f64 { 15.0 }
534
535	pub async fn discover_services(&self) -> Result<Vec<ServiceInfo>, String> {
536		dev_log!("lifecycle", "Starting service discovery");
537
538		let mut registry = self.service_registry.write().await;
539
540		let mut discovered = self.discovered_services.write().await;
541
542		let mut services = Vec::new();
543
544		let core_services = vec![
545			("EditorService", "1.0.0", ServiceStatus::Running),
546			("ExtensionHostService", "1.0.0", ServiceStatus::Running),
547			("ConfigurationService", "1.0.0", ServiceStatus::Running),
548			("FileService", "1.0.0", ServiceStatus::Running),
549			("StorageService", "1.0.0", ServiceStatus::Running),
550		];
551
552		for (name, version, status) in core_services {
553			let service_info = ServiceInfo {
554				name:name.to_string(),
555
556				version:version.to_string(),
557
558				status:status.clone(),
559
560				last_heartbeat:SystemTime::now()
561					.duration_since(SystemTime::UNIX_EPOCH)
562					.unwrap_or_default()
563					.as_millis() as u64,
564
565				uptime:SystemTime::now().duration_since(self.start_time).unwrap_or_default().as_secs(),
566
567				dependencies:self.get_service_dependencies(name),
568
569				metrics:ServiceMetrics {
570					response_time:self.calculate_service_response_time(name).await,
571
572					error_rate:self.calculate_service_error_rate(name).await,
573
574					throughput:self.calculate_service_throughput(name).await,
575
576					memory_usage:self.get_service_memory_usage(name).await,
577
578					cpu_usage:self.get_service_cpu_usage(name).await,
579
580					last_updated:SystemTime::now()
581						.duration_since(SystemTime::UNIX_EPOCH)
582						.unwrap_or_default()
583						.as_millis() as u64,
584				},
585
586				endpoint:Some(format!("localhost:{}", 50050 + services.len() as u16)),
587
588				port:Some(50050 + services.len() as u16),
589			};
590
591			registry.services.insert(name.to_string(), service_info.clone());
592
593			discovered.insert(name.to_string());
594
595			services.push(service_info);
596		}
597
598		registry.last_discovery = SystemTime::now()
599			.duration_since(SystemTime::UNIX_EPOCH)
600			.unwrap_or_default()
601			.as_millis() as u64;
602
603		dev_log!(
604			"lifecycle",
605			"[StatusReporter] Service discovery completed: {} services found",
606			services.len()
607		);
608
609		if let Err(e) = self
610			.runtime
611			.Environment
612			.ApplicationHandle
613			.emit("mountain_service_discovery", &services)
614		{
615			dev_log!(
616				"lifecycle",
617				"error: [StatusReporter] Failed to emit service discovery event: {}",
618				e
619			);
620		}
621
622		Ok(services)
623	}
624
625	fn get_service_dependencies(&self, service_name:&str) -> Vec<String> {
626		match service_name {
627			"ExtensionHostService" => vec!["ConfigurationService".to_string()],
628
629			"FileService" => vec!["StorageService".to_string()],
630
631			"StorageService" => vec!["ConfigurationService".to_string()],
632
633			_ => Vec::new(),
634		}
635	}
636
637	async fn calculate_service_response_time(&self, service_name:&str) -> f64 {
638		match service_name {
639			"EditorService" => 5.0,
640
641			"ExtensionHostService" => 15.0,
642
643			"ConfigurationService" => 2.0,
644
645			"FileService" => 8.0,
646
647			"StorageService" => 3.0,
648
649			_ => 10.0,
650		}
651	}
652
653	async fn calculate_service_error_rate(&self, service_name:&str) -> f64 {
654		match service_name {
655			"EditorService" => 0.1,
656
657			"ExtensionHostService" => 2.5,
658
659			"ConfigurationService" => 0.5,
660
661			"FileService" => 1.2,
662
663			"StorageService" => 0.8,
664
665			_ => 5.0,
666		}
667	}
668
669	async fn calculate_service_throughput(&self, service_name:&str) -> f64 {
670		match service_name {
671			"EditorService" => 1000.0,
672
673			"ExtensionHostService" => 500.0,
674
675			"ConfigurationService" => 2000.0,
676
677			"FileService" => 800.0,
678
679			"StorageService" => 1500.0,
680
681			_ => 100.0,
682		}
683	}
684
685	async fn get_service_memory_usage(&self, service_name:&str) -> f64 {
686		match service_name {
687			"EditorService" => 256.0,
688
689			"ExtensionHostService" => 512.0,
690
691			"ConfigurationService" => 128.0,
692
693			"FileService" => 192.0,
694
695			"StorageService" => 64.0,
696
697			_ => 100.0,
698		}
699	}
700
701	async fn get_service_cpu_usage(&self, service_name:&str) -> f64 {
702		match service_name {
703			"EditorService" => 15.0,
704
705			"ExtensionHostService" => 25.0,
706
707			"ConfigurationService" => 5.0,
708
709			"FileService" => 10.0,
710
711			"StorageService" => 8.0,
712
713			_ => 20.0,
714		}
715	}
716
717	pub async fn start_periodic_discovery(&self) -> Result<(), String> {
718		dev_log!("lifecycle", "Starting periodic service discovery");
719
720		let registry = self.service_registry.read().await;
721
722		let interval = registry.discovery_interval;
723
724		drop(registry);
725
726		let reporter = self.clone_reporter();
727
728		tokio::spawn(async move {
729			let mut interval = tokio::time::interval(Duration::from_millis(interval));
730
731			loop {
732				interval.tick().await;
733
734				if let Err(e) = reporter.discover_services().await {
735					dev_log!("lifecycle", "error: [StatusReporter] Periodic service discovery failed: {}", e);
736				}
737			}
738		});
739
740		Ok(())
741	}
742
743	pub async fn get_service_registry(&self) -> Result<ServiceRegistry, String> {
744		let registry = self.service_registry.read().await;
745
746		Ok(registry.clone())
747	}
748
749	pub async fn get_service_info(&self, service_name:&str) -> Result<Option<ServiceInfo>, String> {
750		let registry = self.service_registry.read().await;
751
752		Ok(registry.services.get(service_name).cloned())
753	}
754
755	pub async fn attempt_recovery(&self) -> Result<(), String> {
756		let mut health_monitor = self
757			.health_monitor
758			.lock()
759			.map_err(|e| format!("Failed to access health monitor: {}", e))?;
760
761		health_monitor.recovery_attempts += 1;
762
763		if let Some(ipc_server) = &self.ipc_server {
764			if let Err(e) = ipc_server.dispose() {
765				return Err(format!("Failed to dispose IPC server: {}", e));
766			}
767
768			if let Err(e) = ipc_server.initialize().await {
769				return Err(format!("Failed to reinitialize IPC server: {}", e));
770			}
771		}
772
773		if let Ok(mut error_count) = self.error_count.lock() {
774			*error_count = 0;
775		}
776
777		dev_log!(
778			"lifecycle",
779			"[StatusReporter] Recovery attempt {} completed",
780			health_monitor.recovery_attempts
781		);
782
783		Ok(())
784	}
785
786	pub fn get_performance_metrics(&self) -> Result<PerformanceMetrics, String> {
787		let metrics = self
788			.performance_metrics
789			.lock()
790			.map_err(|e| format!("Failed to access performance metrics: {}", e))?;
791
792		Ok(metrics.clone())
793	}
794
795	pub fn get_health_status(&self) -> Result<HealthMonitor, String> {
796		let health_monitor = self
797			.health_monitor
798			.lock()
799			.map_err(|e| format!("Failed to access health monitor: {}", e))?;
800
801		Ok(health_monitor.clone())
802	}
803
804	pub(super) fn clone_reporter(&self) -> Struct {
805		Struct {
806			runtime:self.runtime.clone(),
807
808			ipc_server:self.ipc_server.clone(),
809
810			status_history:self.status_history.clone(),
811
812			start_time:self.start_time,
813
814			error_count:self.error_count.clone(),
815
816			performance_metrics:self.performance_metrics.clone(),
817
818			health_monitor:self.health_monitor.clone(),
819
820			service_registry:self.service_registry.clone(),
821
822			discovered_services:self.discovered_services.clone(),
823		}
824	}
825}