Skip to main content

Mountain/IPC/StatusReporter/
Reporter.rs

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