智慧水务管理系统 - 精河县供水工程综合管理平台

water_service.dart 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. /// 供水管理相关 API 调用服务
  2. /// 当前使用 mock 数据,后端 API 就绪后替换为真实请求
  3. class WaterService {
  4. WaterService._internal();
  5. static final WaterService instance = WaterService._internal();
  6. factory WaterService() => instance;
  7. // ==================== Mock 数据 ====================
  8. /// 实时监测数据列表
  9. Future<List<MonitorItem>> getMonitorList() async {
  10. await Future.delayed(const Duration(milliseconds: 800));
  11. return [
  12. MonitorItem(
  13. id: 'M001',
  14. name: '1号泵站',
  15. type: MonitorType.pumpStation,
  16. flow: 120.5,
  17. pressure: 0.35,
  18. level: null,
  19. ph: 7.2,
  20. turbidity: 0.8,
  21. chlorine: 0.5,
  22. status: DeviceStatus.online,
  23. updateTime: DateTime.now().subtract(const Duration(minutes: 2)),
  24. ),
  25. MonitorItem(
  26. id: 'M002',
  27. name: '2号泵站',
  28. type: MonitorType.pumpStation,
  29. flow: 98.3,
  30. pressure: 0.32,
  31. level: null,
  32. ph: 7.1,
  33. turbidity: 0.6,
  34. chlorine: 0.45,
  35. status: DeviceStatus.online,
  36. updateTime: DateTime.now().subtract(const Duration(minutes: 1)),
  37. ),
  38. MonitorItem(
  39. id: 'M003',
  40. name: '3号泵站',
  41. type: MonitorType.pumpStation,
  42. flow: 0,
  43. pressure: 0,
  44. level: null,
  45. ph: 0,
  46. turbidity: 0,
  47. chlorine: 0,
  48. status: DeviceStatus.offline,
  49. updateTime: DateTime.now().subtract(const Duration(hours: 2)),
  50. ),
  51. MonitorItem(
  52. id: 'M004',
  53. name: '清水池',
  54. type: MonitorType.reservoir,
  55. flow: null,
  56. pressure: null,
  57. level: 4.2,
  58. ph: 7.3,
  59. turbidity: 0.3,
  60. chlorine: 0.4,
  61. status: DeviceStatus.online,
  62. updateTime: DateTime.now().subtract(const Duration(minutes: 5)),
  63. ),
  64. MonitorItem(
  65. id: 'M005',
  66. name: 'DN300主干管',
  67. type: MonitorType.pipeline,
  68. flow: 285.6,
  69. pressure: 0.28,
  70. level: null,
  71. ph: null,
  72. turbidity: null,
  73. chlorine: null,
  74. status: DeviceStatus.online,
  75. updateTime: DateTime.now().subtract(const Duration(minutes: 3)),
  76. ),
  77. MonitorItem(
  78. id: 'M006',
  79. name: 'DN200分支管-A区',
  80. type: MonitorType.pipeline,
  81. flow: 86.2,
  82. pressure: 0.22,
  83. level: null,
  84. ph: null,
  85. turbidity: null,
  86. chlorine: null,
  87. status: DeviceStatus.warning,
  88. updateTime: DateTime.now().subtract(const Duration(minutes: 1)),
  89. ),
  90. MonitorItem(
  91. id: 'M007',
  92. name: '末梢水质监测点-1',
  93. type: MonitorType.quality,
  94. flow: null,
  95. pressure: 0.15,
  96. level: null,
  97. ph: 7.0,
  98. turbidity: 0.5,
  99. chlorine: 0.3,
  100. status: DeviceStatus.online,
  101. updateTime: DateTime.now().subtract(const Duration(minutes: 10)),
  102. ),
  103. MonitorItem(
  104. id: 'M008',
  105. name: '加药间',
  106. type: MonitorType.dosing,
  107. flow: null,
  108. pressure: null,
  109. level: 2.8,
  110. ph: null,
  111. turbidity: null,
  112. chlorine: null,
  113. status: DeviceStatus.online,
  114. updateTime: DateTime.now().subtract(const Duration(minutes: 8)),
  115. ),
  116. ];
  117. }
  118. /// 报警列表
  119. Future<List<AlertItem>> getAlertList({bool unreadOnly = false}) async {
  120. await Future.delayed(const Duration(milliseconds: 600));
  121. final alerts = [
  122. AlertItem(
  123. id: 'A001',
  124. level: AlertLevel.critical,
  125. title: '3号泵站设备离线',
  126. content: '3号泵站PLC控制器通信中断,已持续2小时,请尽快安排人员现场检查。',
  127. location: '3号泵站',
  128. deviceName: '3号泵站PLC',
  129. isRead: false,
  130. createTime: DateTime.now().subtract(const Duration(hours: 2)),
  131. handlerName: '待处理',
  132. ),
  133. AlertItem(
  134. id: 'A002',
  135. level: AlertLevel.warning,
  136. title: 'DN200分支管压力偏低',
  137. content: 'A区DN200分支管压力0.22MPa,低于下限0.25MPa,可能存在泄漏或用水量激增。',
  138. location: 'A区DN200分支管',
  139. deviceName: '压力传感器-P205',
  140. isRead: false,
  141. createTime: DateTime.now().subtract(const Duration(hours: 1)),
  142. handlerName: '待处理',
  143. ),
  144. AlertItem(
  145. id: 'A003',
  146. level: AlertLevel.warning,
  147. title: '清水池液位接近上限',
  148. content: '清水池液位4.2m,接近上限4.5m,建议减少进水量或增加供水。',
  149. location: '清水池',
  150. deviceName: '液位传感器-L001',
  151. isRead: false,
  152. createTime: DateTime.now().subtract(const Duration(minutes: 30)),
  153. handlerName: '待处理',
  154. ),
  155. AlertItem(
  156. id: 'A004',
  157. level: AlertLevel.info,
  158. title: '1号泵站例行维护提醒',
  159. content: '1号泵站运行时间已达5000小时,建议安排例行保养。',
  160. location: '1号泵站',
  161. deviceName: '1号泵站主控',
  162. isRead: true,
  163. createTime: DateTime.now().subtract(const Duration(hours: 5)),
  164. handlerName: '王工',
  165. ),
  166. AlertItem(
  167. id: 'A005',
  168. level: AlertLevel.critical,
  169. title: '出厂水余氯超标',
  170. content: '出厂水余氯0.8mg/L,超过国标上限0.8mg/L,请立即调整加药量。',
  171. location: '水厂出水口',
  172. deviceName: '水质分析仪-WQ001',
  173. isRead: true,
  174. createTime: DateTime.now().subtract(const Duration(hours: 8)),
  175. handlerName: '李工',
  176. ),
  177. AlertItem(
  178. id: 'A006',
  179. level: AlertLevel.info,
  180. title: '2号泵站变频器固件更新',
  181. content: '2号泵站变频器有新固件版本可用(v2.3.1),建议在下次停机时更新。',
  182. location: '2号泵站',
  183. deviceName: '2号变频器',
  184. isRead: true,
  185. createTime: DateTime.now().subtract(const Duration(days: 1)),
  186. handlerName: '系统',
  187. ),
  188. ];
  189. if (unreadOnly) {
  190. return alerts.where((a) => !a.isRead).toList();
  191. }
  192. return alerts;
  193. }
  194. /// 标记报警为已读
  195. Future<void> markAlertRead(String alertId) async {
  196. await Future.delayed(const Duration(milliseconds: 300));
  197. }
  198. /// 标记所有报警为已读
  199. Future<void> markAllAlertsRead() async {
  200. await Future.delayed(const Duration(milliseconds: 500));
  201. }
  202. /// 今日值班信息
  203. Future<DutyInfo> getTodayDuty() async {
  204. await Future.delayed(const Duration(milliseconds: 700));
  205. return DutyInfo(
  206. date: DateTime.now(),
  207. shiftName: '白班',
  208. shiftTime: '08:00 - 20:00',
  209. leader: DutyPerson(name: '张建国', phone: '13800138001', role: '值班长'),
  210. members: [
  211. DutyPerson(name: '李明', phone: '13800138002', role: '泵站巡检'),
  212. DutyPerson(name: '王芳', phone: '13800138003', role: '水质监测'),
  213. DutyPerson(name: '赵强', phone: '13800138004', role: '管网维护'),
  214. DutyPerson(name: '刘伟', phone: '13800138005', role: '应急抢修'),
  215. ],
  216. instructions: [
  217. DutyInstruction(
  218. id: 'DI001',
  219. title: '清水池液位监控',
  220. content: '密切关注清水池液位,若超过4.5m立即报告并减少进水量。',
  221. time: '08:30',
  222. issuer: '调度中心-陈主任',
  223. status: InstructionStatus.active,
  224. ),
  225. DutyInstruction(
  226. id: 'DI002',
  227. title: '3号泵站故障排查',
  228. content: '3号泵站PLC离线,安排刘伟前往现场检查,预计14:00前恢复。',
  229. time: '09:15',
  230. issuer: '调度中心-陈主任',
  231. status: InstructionStatus.inProgress,
  232. ),
  233. DutyInstruction(
  234. id: 'DI003',
  235. title: 'A区压力监测',
  236. content: 'A区DN200分支管压力偏低,赵强每30分钟上报一次压力数据。',
  237. time: '10:00',
  238. issuer: '调度中心-陈主任',
  239. status: InstructionStatus.active,
  240. ),
  241. DutyInstruction(
  242. id: 'DI004',
  243. title: '出厂水余氯检测',
  244. content: '每小时对出厂水进行一次余氯检测,确保在0.3-0.8mg/L范围内。',
  245. time: '08:00',
  246. issuer: '水厂-周厂长',
  247. status: InstructionStatus.completed,
  248. ),
  249. ],
  250. );
  251. }
  252. /// 水质数据
  253. Future<List<QualitySample>> getQualityData() async {
  254. await Future.delayed(const Duration(milliseconds: 900));
  255. return [
  256. QualitySample(
  257. id: 'Q001',
  258. category: QualityCategory.rawWater,
  259. source: '水源地取水口',
  260. sampleTime: DateTime.now().subtract(const Duration(hours: 1)),
  261. ph: 7.5,
  262. turbidity: 12.3,
  263. color: 15,
  264. odor: '无异味',
  265. ammonia: 0.3,
  266. permanganate: 3.2,
  267. totalColiform: 280,
  268. isCompliant: true,
  269. ),
  270. QualitySample(
  271. id: 'Q002',
  272. category: QualityCategory.factoryWater,
  273. source: '水厂出水口',
  274. sampleTime: DateTime.now().subtract(const Duration(hours: 1)),
  275. ph: 7.2,
  276. turbidity: 0.3,
  277. color: 2,
  278. odor: '无异味',
  279. ammonia: 0.05,
  280. permanganate: 1.8,
  281. totalColiform: 0,
  282. isCompliant: true,
  283. ),
  284. QualitySample(
  285. id: 'Q003',
  286. category: QualityCategory.factoryWater,
  287. source: '清水池出水',
  288. sampleTime: DateTime.now().subtract(const Duration(hours: 2)),
  289. ph: 7.1,
  290. turbidity: 0.4,
  291. color: 3,
  292. odor: '无异味',
  293. ammonia: 0.08,
  294. permanganate: 2.0,
  295. totalColiform: 0,
  296. isCompliant: true,
  297. ),
  298. QualitySample(
  299. id: 'Q004',
  300. category: QualityCategory.endWater,
  301. source: 'A区末梢-学校',
  302. sampleTime: DateTime.now().subtract(const Duration(hours: 3)),
  303. ph: 7.0,
  304. turbidity: 0.6,
  305. color: 4,
  306. odor: '无异味',
  307. ammonia: 0.1,
  308. permanganate: 2.5,
  309. totalColiform: 0,
  310. isCompliant: true,
  311. ),
  312. QualitySample(
  313. id: 'Q005',
  314. category: QualityCategory.endWater,
  315. source: 'B区末梢-医院',
  316. sampleTime: DateTime.now().subtract(const Duration(hours: 3)),
  317. ph: 6.8,
  318. turbidity: 1.2,
  319. color: 6,
  320. odor: '轻微氯味',
  321. ammonia: 0.15,
  322. permanganate: 3.0,
  323. totalColiform: 2,
  324. isCompliant: false,
  325. ),
  326. QualitySample(
  327. id: 'Q006',
  328. category: QualityCategory.endWater,
  329. source: 'C区末梢-居民区',
  330. sampleTime: DateTime.now().subtract(const Duration(hours: 4)),
  331. ph: 7.1,
  332. turbidity: 0.5,
  333. color: 3,
  334. odor: '无异味',
  335. ammonia: 0.08,
  336. permanganate: 2.2,
  337. totalColiform: 0,
  338. isCompliant: true,
  339. ),
  340. ];
  341. }
  342. }
  343. // ==================== 数据模型 ====================
  344. /// 监测类型
  345. enum MonitorType {
  346. pumpStation('泵站'),
  347. reservoir('水池/水箱'),
  348. pipeline('管网'),
  349. quality('水质监测点'),
  350. dosing('加药间');
  351. final String label;
  352. const MonitorType(this.label);
  353. }
  354. /// 设备状态
  355. enum DeviceStatus {
  356. online('在线', Color(0xFF4CAF50)),
  357. offline('离线', Color(0xFF9E9E9E)),
  358. warning('告警', Color(0xFFFF9800)),
  359. error('故障', Color(0xFFF44336));
  360. final String label;
  361. final int color;
  362. const DeviceStatus(this.label, this.color);
  363. }
  364. /// 监测数据项
  365. class MonitorItem {
  366. final String id;
  367. final String name;
  368. final MonitorType type;
  369. final double? flow; // 流量 m³/h
  370. final double? pressure; // 压力 MPa
  371. final double? level; // 液位 m
  372. final double? ph;
  373. final double? turbidity; // 浊度 NTU
  374. final double? chlorine; // 余氯 mg/L
  375. final DeviceStatus status;
  376. final DateTime updateTime;
  377. const MonitorItem({
  378. required this.id,
  379. required this.name,
  380. required this.type,
  381. this.flow,
  382. this.pressure,
  383. this.level,
  384. this.ph,
  385. this.turbidity,
  386. this.chlorine,
  387. required this.status,
  388. required this.updateTime,
  389. });
  390. String get statusLabel => status.label;
  391. }
  392. /// 报警级别
  393. enum AlertLevel {
  394. critical('严重', Color(0xFFF44336)),
  395. warning('警告', Color(0xFFFF9800)),
  396. info('提示', Color(0xFF2196F3));
  397. final String label;
  398. final int color;
  399. const AlertLevel(this.label, this.color);
  400. }
  401. /// 报警项
  402. class AlertItem {
  403. final String id;
  404. final AlertLevel level;
  405. final String title;
  406. final String content;
  407. final String location;
  408. final String deviceName;
  409. bool isRead;
  410. final DateTime createTime;
  411. final String handlerName;
  412. AlertItem({
  413. required this.id,
  414. required this.level,
  415. required this.title,
  416. required this.content,
  417. required this.location,
  418. required this.deviceName,
  419. required this.isRead,
  420. required this.createTime,
  421. required this.handlerName,
  422. });
  423. }
  424. /// 值班人员
  425. class DutyPerson {
  426. final String name;
  427. final String phone;
  428. final String role;
  429. const DutyPerson({
  430. required this.name,
  431. required this.phone,
  432. required this.role,
  433. });
  434. }
  435. /// 指令状态
  436. enum InstructionStatus {
  437. active('待执行', Color(0xFFFF9800)),
  438. inProgress('执行中', Color(0xFF2196F3)),
  439. completed('已完成', Color(0xFF4CAF50));
  440. final String label;
  441. final int color;
  442. const InstructionStatus(this.label, this.color);
  443. }
  444. /// 调度指令
  445. class DutyInstruction {
  446. final String id;
  447. final String title;
  448. final String content;
  449. final String time;
  450. final String issuer;
  451. final InstructionStatus status;
  452. const DutyInstruction({
  453. required this.id,
  454. required this.title,
  455. required this.content,
  456. required this.time,
  457. required this.issuer,
  458. required this.status,
  459. });
  460. }
  461. /// 今日值班信息
  462. class DutyInfo {
  463. final DateTime date;
  464. final String shiftName;
  465. final String shiftTime;
  466. final DutyPerson leader;
  467. final List<DutyPerson> members;
  468. final List<DutyInstruction> instructions;
  469. const DutyInfo({
  470. required this.date,
  471. required this.shiftName,
  472. required this.shiftTime,
  473. required this.leader,
  474. required this.members,
  475. required this.instructions,
  476. });
  477. }
  478. /// 水质类别
  479. enum QualityCategory {
  480. rawWater('原水'),
  481. factoryWater('出厂水'),
  482. endWater('末梢水');
  483. final String label;
  484. const QualityCategory(this.label);
  485. }
  486. /// 水质采样数据
  487. class QualitySample {
  488. final String id;
  489. final QualityCategory category;
  490. final String source;
  491. final DateTime sampleTime;
  492. final double ph;
  493. final double turbidity; // NTU
  494. final int color; // 色度
  495. final String odor; // 嗅味
  496. final double ammonia; // 氨氮 mg/L
  497. final double permanganate; // 高锰酸盐指数 mg/L
  498. final int totalColiform; // 总大肠菌群 CFU/100mL
  499. final bool isCompliant;
  500. const QualitySample({
  501. required this.id,
  502. required this.category,
  503. required this.source,
  504. required this.sampleTime,
  505. required this.ph,
  506. required this.turbidity,
  507. required this.color,
  508. required this.odor,
  509. required this.ammonia,
  510. required this.permanganate,
  511. required this.totalColiform,
  512. required this.isCompliant,
  513. });
  514. /// 获取各指标达标情况
  515. List<QualityIndicator> getIndicators() {
  516. return [
  517. QualityIndicator('pH', ph.toStringAsFixed(1), '6.5-8.5', ph >= 6.5 && ph <= 8.5),
  518. QualityIndicator('浊度', '${turbidity.toStringAsFixed(1)} NTU', '≤1 NTU', turbidity <= 1.0),
  519. QualityIndicator('色度', '$color 度', '≤15 度', color <= 15),
  520. QualityIndicator('嗅味', odor, '无异味', odor == '无异味'),
  521. QualityIndicator('氨氮', '${ammonia.toStringAsFixed(2)} mg/L', '≤0.5 mg/L', ammonia <= 0.5),
  522. QualityIndicator('高锰酸盐', '${permanganate.toStringAsFixed(1)} mg/L', '≤3 mg/L',
  523. category == QualityCategory.rawWater ? true : permanganate <= 3.0),
  524. QualityIndicator('总大肠菌群', '$totalColiform CFU/100mL',
  525. category == QualityCategory.rawWater ? '≤1000' : '不得检出',
  526. category == QualityCategory.rawWater ? totalColiform <= 1000 : totalColiform == 0),
  527. ];
  528. }
  529. }
  530. /// 水质指标
  531. class QualityIndicator {
  532. final String name;
  533. final String value;
  534. final String standard;
  535. final bool isCompliant;
  536. const QualityIndicator(this.name, this.value, this.standard, this.isCompliant);
  537. }