import 'dart:async'; import 'dart:math'; /// ๐Ÿ“Œ [๋…๋ฆฝ ํ•จ์ˆ˜] ์ง€๋‚œ 24์‹œ๊ฐ„ ์น˜์˜ ๊ฐ€์ƒ(๋”๋ฏธ) ์˜จ๋„ ๋ฐ์ดํ„ฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. /// /// ์‹ค์ œ DB ์—ฐ๋™ ์ „, ๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ ์ฐจํŠธ/๋ฆฌ์ŠคํŠธ UI๋ฅผ ์ฑ„์šฐ๊ธฐ ์œ„ํ•œ ์šฉ๋„์ž…๋‹ˆ๋‹ค. /// ๊ฐ’์ด ๋“ค์ญ‰๋‚ ์ญ‰ ํŠ€์ง€ ์•Š๊ณ  ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณด์ด๋„๋ก, ์ง์ „ ๊ฐ’์—์„œ ์กฐ๊ธˆ์”ฉ๋งŒ /// ๋ณ€ํ•˜๋Š” "๋žœ๋ค ์›Œํฌ(random walk)" ๋ฐฉ์‹์œผ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค. /// /// - [count] : ๋งŒ๋“ค ๋ฐ์ดํ„ฐ ๊ฐœ์ˆ˜ (๊ธฐ๋ณธ 24๊ฐœ = 1์‹œ๊ฐ„ ๊ฐ„๊ฒฉ 24์‹œ๊ฐ„) /// - [minTemp] : ๊ฐ€์ƒ ์˜จ๋„ ํ•˜ํ•œ /// - [maxTemp] : ๊ฐ€์ƒ ์˜จ๋„ ์ƒํ•œ List generateDummyTemperatures({ int count = 24, double minTemp = 18.0, double maxTemp = 28.0, }) { final random = Random(); final list = []; // ์‹œ์ž‘ ์˜จ๋„๋Š” ๋ฒ”์œ„ ๋‚ด ์ž„์˜ ๊ฐ’ double last = minTemp + random.nextDouble() * (maxTemp - minTemp); for (int i = 0; i < count; i++) { // ์ง์ „ ๊ฐ’์—์„œ -0.5 ~ +0.5ยฐC ๋‚ด์™ธ๋กœ ํ”๋“ค๊ธฐ (clamp๋Š” num ๋ฐ˜ํ™˜ โ†’ toDouble) last = (last + (random.nextDouble() - 0.5)).clamp(minTemp, maxTemp).toDouble(); list.add(double.parse(last.toStringAsFixed(1))); // ์†Œ์ˆ˜์  1์ž๋ฆฌ๋กœ ์ •๋ฆฌ } return list; } /// ๐Ÿ“ก [์ŠคํŠธ๋ฆผ ์„œ๋น„์Šค] ์•ฑ์ด ์ผœ์ ธ ์žˆ๋Š” ๋™์•ˆ ์ผ์ • ์ฃผ๊ธฐ๋งˆ๋‹ค /// ์ƒˆ๋กœ์šด ๊ฐ€์ƒ ์˜จ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ„์† ์ถ”๊ฐ€ํ•˜๋ฉฐ ๊ฐฑ์‹ ๋œ ๋ฆฌ์ŠคํŠธ๋ฅผ ํ˜๋ ค๋ณด๋‚ธ๋‹ค. /// /// ์‚ฌ์šฉ ํ๋ฆ„: /// final service = TempDataService()..start(); /// service.stream.listen((temps) { ... }); // StreamBuilder๋กœ ๊ตฌ๋… /// service.dispose(); // ํ™”๋ฉด์ด ์‚ฌ๋ผ์งˆ ๋•Œ ๋ฐ˜๋“œ์‹œ ํ˜ธ์ถœ /// /// ๐Ÿ‘‰ ๋‚˜์ค‘์— ์‹ค์ œ DB ์—ฐ๋™ ์‹œ์—๋Š” [_generateNextTemperature] ๋‚ด๋ถ€๋งŒ /// "DB์—์„œ ์ตœ์‹  ์˜จ๋„ 1๊ฑด ์กฐํšŒ" ๋กœ์ง์œผ๋กœ ๊ต์ฒดํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. class TempDataService { TempDataService({ this.minTemp = 18.0, this.maxTemp = 28.0, this.historyCount = 24, this.interval = const Duration(seconds: 3), // โฑ๏ธ ํ…Œ์ŠคํŠธ์šฉ 3์ดˆ this.maxLength = 200, // ๋ฉ”๋ชจ๋ฆฌ ๋ฌดํ•œ ์ฆ๊ฐ€ ๋ฐฉ์ง€์šฉ ๋ณด๊ด€ ํ•œ๋„ }); final double minTemp; // ์ตœ์ € ๊ฐ€์ƒ ์˜จ๋„ final double maxTemp; // ์ตœ๊ณ  ๊ฐ€์ƒ ์˜จ๋„ final int historyCount; // ์‹œ์ž‘ ์‹œ ์ฑ„์›Œ๋‘˜ 24์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ๊ฐœ์ˆ˜ final Duration interval; // ์ƒˆ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ ์ฃผ๊ธฐ final int maxLength; // ๋ฆฌ์ŠคํŠธ์— ๋ณด๊ด€ํ•  ์ตœ๋Œ€ ๊ฐœ์ˆ˜ final _random = Random(); final List _temperatures = []; Timer? _timer; // broadcast: ์—ฌ๋Ÿฌ ์œ„์ ฏ์ด ๋™์‹œ์— ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•จ final _controller = StreamController>.broadcast(); /// ๊ฐฑ์‹ ๋  ๋•Œ๋งˆ๋‹ค "์ „์ฒด ์˜จ๋„ ๋ฆฌ์ŠคํŠธ"๊ฐ€ ํ˜๋Ÿฌ๋‚˜์˜ค๋Š” ์ŠคํŠธ๋ฆผ Stream> get stream => _controller.stream; /// ํ˜„์žฌ๊นŒ์ง€ ์Œ“์ธ ๋ฐ์ดํ„ฐ (์ฝ๊ธฐ ์ „์šฉ ๋ณต์‚ฌ๋ณธ) List get current => List.unmodifiable(_temperatures); /// ์ŠคํŠธ๋ฆผ ์‹œ์ž‘: 24์‹œ๊ฐ„ ๋”๋ฏธ ๋ฐ์ดํ„ฐ๋กœ ์‹œ๋“œํ•œ ๋’ค, [interval]๋งˆ๋‹ค ์ƒˆ ๊ฐ’ ์ถ”๊ฐ€ void start() { _temperatures ..clear() ..addAll(generateDummyTemperatures( count: historyCount, minTemp: minTemp, maxTemp: maxTemp, )); _controller.add(current); // ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ํ•œ ๋ฒˆ ์ „๋‹ฌ _timer?.cancel(); _timer = Timer.periodic(interval, (_) { _temperatures.add(_generateNextTemperature()); // ์˜ค๋ž˜๋œ ๋ฐ์ดํ„ฐ๋ถ€ํ„ฐ ๋ฒ„๋ ค์„œ ๊ธธ์ด ์œ ์ง€ while (_temperatures.length > maxLength) { _temperatures.removeAt(0); } _controller.add(current); }); } /// ๋‹ค์Œ(์ƒˆ) ๊ฐ€์ƒ ์˜จ๋„ ํ•œ ๊ฑด์„ ์ƒ์„ฑํ•œ๋‹ค. ์ง์ „ ๊ฐ’ ๊ธฐ์ค€ ๋žœ๋ค ์›Œํฌ. double _generateNextTemperature() { final last = _temperatures.isEmpty ? minTemp + _random.nextDouble() * (maxTemp - minTemp) : _temperatures.last; final next = (last + (_random.nextDouble() - 0.5)).clamp(minTemp, maxTemp); return double.parse(next.toStringAsFixed(1)); } /// ํƒ€์ด๋จธ ์ •์ง€ + ์ŠคํŠธ๋ฆผ ์ •๋ฆฌ. ์œ„์ ฏ dispose() ์—์„œ ๊ผญ ํ˜ธ์ถœํ•  ๊ฒƒ. void dispose() { _timer?.cancel(); _controller.close(); } }