1741 Commits

Author SHA1 Message Date
xinzish 32bbe3ddcd fix bug and refine ,end of 2025 2025-12-31 16:11:28 +08:00
xinzish 38fb35a333 Merge branch 'dingsu/szh' of https://e.coding.net/tjwater/tjwaterbackend/TJWaterServer into dingsu/szh 2025-08-16 09:21:05 +08:00
unknown c15924b8e4 modify project name, change influxdb token, remove log simulation result 2025-08-15 19:00:01 +08:00
DingZQ 92e2122743 Refine 2025-08-15 14:59:36 +08:00
DingZQ 3b1a4b4b95 Refine 2025-08-15 14:50:58 +08:00
DingZQ 40f3ae02fb Remove logger: 2025-08-15 14:49:22 +08:00
DingZQ 065bcded8e Update document for time format 2025-08-14 20:19:50 +08:00
DingZQ 2ed288830f Update influxdb token 2025-08-14 19:59:20 +08:00
DingZQ e4b1648041 Change bb to project_info.name 2025-08-13 18:12:05 +08:00
DingZQ d745e6f011 Update Online_Analysis 2025-08-13 18:01:37 +08:00
DingZQ c27d1cf1d5 Add project_info.py 2025-08-13 17:58:40 +08:00
DingZQ 7086fe42a0 Add fx inp 2025-08-13 17:55:42 +08:00
DingZQ 675b309a0d Update setup_server document 2025-08-13 16:44:19 +08:00
DingZQ 10cffbbdf1 *.py 2025-08-13 16:38:44 +08:00
DingZQ 977c4d6fc6 Refine 2025-06-22 23:52:27 +08:00
DingZQ c10fcca46b Only getnetworkgeometries needs auth 2025-06-22 23:51:39 +08:00
DingZQ 11a31467fa Add a token for login 2025-06-22 23:26:09 +08:00
DingZQ c8b3d6b178 Refine 2025-06-22 23:21:48 +08:00
DingZQ 4e29ac11c5 Add Bear to header check 2025-06-22 23:20:34 +08:00
DingZQ d53b5f0409 Add global auth 2025-06-22 23:14:34 +08:00
DingZQ 5ab7246317 Add parameter token for api fastapi_get_network_geometries 2025-06-22 23:08:47 +08:00
DingZQ 641a879b59 Refine 2025-06-22 18:51:25 +08:00
DingZQ 17431dc22d Add fastapi method login 2025-06-22 18:43:08 +08:00
DingZQ eb49e2c849 Generate token with username/password 2025-06-22 18:40:33 +08:00
DingZQ 09ca1c43ec Add method to generate access token 2025-06-22 18:38:36 +08:00
DingZQ 4a54659360 Add login method 2025-06-22 16:02:50 +08:00
DingZQ 058a517280 Support auth for szh branch 2025-06-17 22:01:24 +08:00
DingZQ 4673ceb2b0 Add open szh 2025-06-16 21:42:02 +08:00
DingZQ 05f41b635d Open szh by default 2025-06-16 21:30:13 +08:00
DingZQ 7e50c2d2ad update script 2025-06-15 21:15:35 +08:00
DingZQ baa53130e6 Add szh inp 2025-06-15 21:07:58 +08:00
DingZQ 6e9dea1527 Set log level to INFO and only keep the file logger 2025-06-02 19:17:49 +08:00
DingZQ 9df472b11c We should return immediately 2025-06-02 19:11:31 +08:00
DingZQ 0f471a1903 Clear redis after we force mamually run simulation 2025-06-02 19:09:53 +08:00
DingZQ 70affaa182 Refine 2025-06-02 19:05:10 +08:00
DingZQ 5ede29efd4 Add simulation informaiton to log 2025-06-02 19:02:13 +08:00
DingZQ 8ad9459459 Refine 2025-06-02 18:57:41 +08:00
DingZQ 320735bc85 Refine 2025-06-02 18:54:55 +08:00
DingZQ 1e006673e4 Print run_simulation log to log file 2025-06-02 18:52:04 +08:00
DingZQ b587cc7c1e Refine 2025-06-02 18:36:10 +08:00
DingZQ 54bf12883c Change log level to info 2025-06-02 18:33:41 +08:00
DingZQ cbcbdda3e0 Disable file log 2025-05-28 20:47:54 +08:00
DingZQ 5fc5bc5ecc Update py 2025-05-22 23:10:20 +08:00
DingZQ 28eb0ee94c Run manually simulation in thread 2025-05-22 23:05:57 +08:00
DingZQ 5f3f75a0bc Setup logger for main.py 2025-05-18 19:29:39 +08:00
DingZQ 6321146abd Refine 2025-05-18 19:24:36 +08:00
DingZQ 16991f37c3 Refine 2025-05-18 19:21:42 +08:00
DingZQ 4a080bb409 Add log to external file 2025-05-18 19:19:46 +08:00
DingZQ 016722cec1 Update the modules to build pyd 2025-05-18 17:28:02 +08:00
DingZQ 7edb71c625 Update the script 2025-05-18 17:16:52 +08:00
DingZQ b8b2490c2a Add more py to pyd 2025-05-18 16:46:39 +08:00
DingZQ 7d08c84d8f Add script to build pyd 2025-05-18 16:42:43 +08:00
DingZQ 1ee0bb8067 Change simulation_date to string 2025-05-18 11:36:48 +08:00
DingZQ 17a8d84827 Print item 2025-05-18 11:31:15 +08:00
DingZQ d7d7e62d48 Change API to runsimulationmanullybydate 2025-05-17 22:35:28 +08:00
DingZQ d4133770c0 Refine 2025-05-17 22:19:18 +08:00
DingZQ f816de6426 Change sklearn to scikit_learn 2025-05-17 22:18:06 +08:00
DingZQ b9cd42ed76 Refine 2025-05-17 22:13:22 +08:00
DingZQ e97049eb30 Add lib spopt 2025-05-17 22:12:04 +08:00
DingZQ 09a55deaa1 Add lib libpysal 2025-05-17 22:10:00 +08:00
DingZQ 6fffba9bc2 Update main.py 2025-05-17 22:09:24 +08:00
DingZQ 4f785aad82 Refine 2025-05-17 22:03:55 +08:00
DingZQ 893351ce61 Refine 2025-05-17 22:02:08 +08:00
DingZQ d7d714d879 Refine 2025-05-17 21:56:39 +08:00
DingZQ 3faea5602a Refine 2025-05-17 21:53:25 +08:00
DingZQ 495b6be057 add method pressure_sensor_placement_sensitivity 2025-05-17 21:51:24 +08:00
DingZQ 36af0b56e4 Update for sensivitiy.py 2025-05-17 21:44:24 +08:00
DingZQ ab05458747 Add sensitivity.py 2025-05-17 21:38:36 +08:00
DingZQ 84ba85ac84 update 2025-05-17 21:35:11 +08:00
DingZQ 851e016ad2 update 2025-05-17 21:33:59 +08:00
DingZQ 583c771254 update 2025-05-17 21:32:12 +08:00
DingZQ 5e1eeb7ace update 2025-05-17 21:29:57 +08:00
DingZQ 1e6c88dabe Update run_simulation method 2025-05-17 21:26:33 +08:00
DingZQ d679070068 Update run_simulation method 2025-05-17 21:24:35 +08:00
DingZQ 678b98b1b4 Refine 2025-05-17 17:15:03 +08:00
DingZQ b055555819 Refine 2025-05-17 17:14:39 +08:00
DingZQ 3346a519ce Change to str 2025-05-17 17:13:19 +08:00
DingZQ 3a6991ea6b Add comments 2025-05-17 16:47:23 +08:00
DingZQ c4293acb2f Add API runsimulationbydate 2025-05-17 16:46:23 +08:00
DingZQ 3a9737173d Refine 2025-05-10 16:48:11 +08:00
DingZQ 2d81182c24 Refine simulation code by WMH 2025-05-10 16:40:13 +08:00
DingZQ 071a23112a Refine write option 2025-05-10 16:21:32 +08:00
DingZQ bcb88775ba Update write option 2025-05-09 22:32:46 +08:00
DingZQ bf6116aa46 Add API getallburstlocateresults 2025-05-07 21:06:08 +08:00
DingZQ 99c19f94f4 Upload cleaned data 2025-05-05 20:54:59 +08:00
DingZQ ce557a7c6c Refine 2025-05-05 20:52:07 +08:00
DingZQ 24226123d6 Update cleaned data 2025-05-05 17:31:43 +08:00
DingZQ cca3363bda Update cleaned data 2025-05-05 17:31:02 +08:00
DingZQ 511e95427d Update run script 2025-05-05 12:43:21 +08:00
DingZQ c9d9950512 Uncomment query methods 2025-05-05 11:34:30 +08:00
DingZQ 08b04bfeb8 Update influxdb api 2025-05-05 11:30:51 +08:00
DingZQ 1b0b0f308c Add api getallsensorplacements 2025-05-04 21:55:01 +08:00
DingZQ 23faeb96e1 Add API for sensor_placement 2025-05-04 21:52:13 +08:00
DingZQ 74c828cc3f Support bianma for risk page 2025-05-04 21:19:05 +08:00
DingZQ 3ba4837c9e Refine 2025-05-04 19:48:39 +08:00
DingZQ c55de1d3ae Fixed scada method name 2025-05-04 19:34:49 +08:00
DingZQ 3012ddab06 Add more SCADA API 2025-05-04 17:31:10 +08:00
DingZQ 8dfd56456e Add query_cleaning_SCADA_data_by_device_ID_and_timerange 2025-05-04 16:26:55 +08:00
DingZQ c7724edd4a Comment out the code to upload cleaned csv 2025-05-04 16:21:02 +08:00
DingZQ 3af3efc43f Update date in cleaned_demand_data.csv 2025-05-04 16:16:29 +08:00
DingZQ d775c1a9f9 Upload cleaned data 2025-05-03 22:19:27 +08:00
DingZQ b8dcec9349 Return points from gis_pipe 2025-04-26 23:49:30 +08:00
DingZQ 01be4ce1c4 Add drawpipe.py 2025-04-26 23:14:41 +08:00
DingZQ 86e06e1717 Refine influxdb api write api option 2025-04-24 21:02:31 +08:00
DingZQ 183c5a2bad Refine 2025-04-23 21:58:41 +08:00
DingZQ 17c0d6d634 Update influxdb_api.py 2025-04-23 21:58:08 +08:00
DingZQ 00b5e0a3bb Updte influxdb and online_Analysis 2025-04-23 21:52:56 +08:00
DingZQ 4448ea34b7 Update create_template 2025-04-23 21:47:47 +08:00
DingZQ 0a66b55a3e Update csv and sql file 2025-04-23 21:47:33 +08:00
DingZQ 291ac846c3 Add sensor_placement and burse_locate_result 2025-04-23 21:43:16 +08:00
DingZQ de3ac89ab8 If it's today or future, we don't cache queryallscadarecordsbydate 2025-04-20 21:51:06 +08:00
DingZQ f0a4b68347 Print data_str 2025-04-20 21:47:35 +08:00
DingZQ 8548f0b261 Print store time 2025-04-20 21:29:03 +08:00
DingZQ 116817aa5d Refine 2025-04-20 20:52:22 +08:00
DingZQ f465147b4a Update setup instruction 2025-04-20 18:20:29 +08:00
DingZQ ffe9a29d78 Refine 2025-04-20 17:27:50 +08:00
DingZQ 97b7d8fd0c Add PyMetis for Python 3.12 2025-04-20 17:22:36 +08:00
DingZQ d7185fbe27 Add AutoPullGitChanges script 2025-04-20 16:41:11 +08:00
DingZQ 3dd68faad8 Refine 2025-04-20 10:04:41 +08:00
DingZQ dae88d5626 Refine 2025-04-20 10:02:47 +08:00
DingZQ a6ddc9bb0e Refine 2025-04-20 10:02:20 +08:00
DingZQ 355aff61b9 Refine 2025-04-20 10:01:07 +08:00
DingZQ 51863b495e Refine 2025-04-20 09:58:25 +08:00
DingZQ 565e4d10df Refine 2025-04-20 09:57:56 +08:00
DingZQ 4bb40e7428 Refine 2025-04-20 09:56:56 +08:00
DingZQ 2c2cf24976 Refine 2025-04-20 09:54:58 +08:00
DingZQ 2a9c9da743 Refine 2025-04-20 09:53:32 +08:00
DingZQ c704f8a5a8 Return all data from gis pipe 2025-04-20 09:52:14 +08:00
DingZQ b35d15d244 Refine 2025-04-19 19:08:00 +08:00
DingZQ 2d1ff9212a Refine 2025-04-19 18:03:40 +08:00
DingZQ ad3a4d9461 Refine 2025-04-19 18:03:19 +08:00
DingZQ 5501049938 Refine 2025-04-19 18:01:33 +08:00
DingZQ 34d915404f Refine 2025-04-19 17:48:57 +08:00
DingZQ 7dca6685d1 Refine 2025-04-19 17:47:15 +08:00
DingZQ 9180b9c5cf Add fastapi querycleanedscadadatabydeviceidandtimerange 2025-04-19 17:36:26 +08:00
DingZQ b15a7fa3ed Refine 2025-04-19 17:29:38 +08:00
DingZQ c38ebf45d6 Add comments 2025-04-19 12:40:31 +08:00
DingZQ cd3a221459 Refine 2025-04-19 12:35:50 +08:00
DingZQ 3afcb678e5 Refine 2025-04-19 12:35:05 +08:00
DingZQ 9f7e26905e Refine 2025-04-19 12:33:45 +08:00
DingZQ 7223f4b876 Refine 2025-04-19 12:31:27 +08:00
DingZQ b085e4b4de Refine 2025-04-19 12:24:08 +08:00
DingZQ 2f8e1f1fc7 Refine 2025-04-19 12:22:08 +08:00
DingZQ f7b33904c7 Refine 2025-04-19 12:19:37 +08:00
DingZQ 734c1b61c4 Refine 2025-04-19 12:17:38 +08:00
DingZQ 25e97c7798 Refine 2025-04-19 11:48:04 +08:00
DingZQ 013929e1e5 Refine 2025-04-19 11:42:03 +08:00
DingZQ 387e1f5989 Add API getpiperiskprobabilitygeometries 2025-04-19 11:41:35 +08:00
DingZQ 2fc204d8ed Refine 2025-04-19 11:29:43 +08:00
DingZQ 4a8130a748 Refine 2025-04-19 11:28:57 +08:00
DingZQ edc6d6d9c2 Refine 2025-04-19 11:27:12 +08:00
DingZQ fbd4069ed5 Add cleaned_demand_data.csv 2025-04-19 11:20:10 +08:00
DingZQ 612c1b922f Refine 2025-04-19 11:18:24 +08:00
DingZQ ed11fefbab Refine 2025-04-19 11:17:38 +08:00
DingZQ 6eedd0d55a Refine 2025-04-19 11:15:02 +08:00
DingZQ e23761195e Refine 2025-04-17 23:12:07 +08:00
DingZQ 97f73a3521 Refine 2025-04-17 23:11:00 +08:00
DingZQ ab99d9a81c 'Add 2025-04-17 23:09:15 +08:00
DingZQ f675ec00de Refine 2025-04-17 21:23:23 +08:00
DingZQ 4a2f1fc103 Refine 2025-04-17 21:21:25 +08:00
DingZQ 82e87cc6bb Refine 2025-04-17 21:18:30 +08:00
DingZQ a2ec56018f Refine 2025-04-17 21:10:12 +08:00
DingZQ ec4f72f508 Refine 2025-04-17 21:02:26 +08:00
DingZQ 52097bb58c Refine 2025-04-17 21:00:31 +08:00
DingZQ 510fe520e8 Refine 2025-04-17 20:59:36 +08:00
DingZQ 32bc4e0553 Refine 2025-04-17 20:58:29 +08:00
DingZQ a9a5b1573f Refine 2025-04-17 20:57:59 +08:00
DingZQ 6d23a515dd Refine 2025-04-17 20:57:02 +08:00
DingZQ 6bb3c1c22b Add fastapi 2025-04-17 20:54:16 +08:00
DingZQ 72a4e8427d Add s41_pipe_risk_probability 2025-04-17 20:52:18 +08:00
DingZQ 3da6650784 Refine 2025-04-16 22:14:34 +08:00
DingZQ 70c6d56f95 Refine 2025-04-16 22:08:18 +08:00
DingZQ 3f0ed41da6 Add pipe risk probability 2025-04-16 22:01:30 +08:00
DingZQ 0169c962d0 Update online_Analysis.py from WMH 2025-04-15 22:07:34 +08:00
DingZQ 2d5b500bf7 Refine 2025-04-15 21:44:22 +08:00
DingZQ 22c2351690 Refine 2025-04-15 21:38:37 +08:00
DingZQ e9b01a5b50 Refine 2025-04-15 21:38:16 +08:00
DingZQ 0384b40baa Add logging infor to query_all_records_by_date 2025-04-15 21:33:19 +08:00
DingZQ 361d317595 Refine 2025-04-15 08:07:49 +08:00
DingZQ a810456448 Refine 2025-04-15 08:05:39 +08:00
DingZQ 65ba34ff3a Refine 2025-04-15 07:52:47 +08:00
DingZQ 6235383aca Refine 2025-04-15 07:51:21 +08:00
DingZQ e4dacba1c0 Refine 2025-04-15 07:50:12 +08:00
DingZQ ff112805ec Store date to redis 2025-04-15 07:48:24 +08:00
DingZQ db91bcb6f9 Update scada location information 2025-04-14 21:50:10 +08:00
DingZQ 9a6de4e571 Refine 2025-04-14 21:44:01 +08:00
DingZQ 7aa7b26234 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2025-04-14 21:42:58 +08:00
DingZQ 3d68da5af2 Update auto_cache 2025-04-14 21:42:54 +08:00
DingZQ 14477b6224 pull source code when start fastapi 2025-04-13 17:42:08 +08:00
DingZQ e4aa0f20b8 Enable gzip for influxdb client 2025-04-13 17:40:58 +08:00
DingZQ a8db5d63a2 Merge 2025-04-12 22:54:32 +08:00
DingZQ aba807075e Add API Query by daterange 2025-04-12 22:51:37 +08:00
DingZQ 7b77013815 Add API queryallrecordsbytimerange 2025-04-12 20:49:42 +08:00
DingZQ ab787d22c6 Add API query_all_records_by_time_range 2025-04-12 20:45:17 +08:00
DingZQ 1bddb1656d Refine 2025-04-12 17:55:58 +08:00
DingZQ 1b301b5a75 Refine 2025-04-12 17:43:13 +08:00
DingZQ f49fdc168d Refine 2025-04-12 17:17:51 +08:00
DingZQ 1263146cdb Refine options 2025-04-11 22:54:30 +08:00
DingZQ 603cb1f2aa Refine 2025-04-07 22:14:00 +08:00
DingZQ e07bbb70f3 If the query date is today, we don't cache it 2025-04-07 22:13:04 +08:00
DingZQ 63a8d0e3f4 Refine 2025-04-05 16:27:33 +08:00
DingZQ 36f3382c49 Open bb by default 2025-04-05 10:25:57 +08:00
DingZQ a97f39b247 Refine time 2025-04-04 23:32:50 +08:00
DingZQ 313ad32dcc Refine 2025-04-04 14:34:56 +08:00
DingZQ 5867451b63 Refine 2025-04-04 14:34:06 +08:00
DingZQ 017f870dc1 Refine 2025-04-04 14:32:58 +08:00
DingZQ 4755fac53d Refine 2025-04-04 14:23:03 +08:00
DingZQ 4f26ff32de Refine 2025-04-04 14:20:34 +08:00
DingZQ 9c5dbe6bda Refine 2025-04-04 14:19:32 +08:00
DingZQ 398bc1cb2a Refine 2025-04-04 14:16:57 +08:00
DingZQ 26c0ab7d90 Refine 2025-04-04 14:09:38 +08:00
DingZQ 0c32b60534 Refine 2025-04-04 14:05:07 +08:00
DingZQ 30e1587565 Fixed data range issue 2025-04-04 13:50:23 +08:00
DingZQ 98462635b3 Refine 2025-04-04 13:02:53 +08:00
DingZQ 1c72b08095 Refine 2025-04-04 12:49:38 +08:00
DingZQ d7f8d1e90f Refine 2025-04-04 12:42:01 +08:00
DingZQ e9f305fb82 Refine 2025-04-04 12:38:59 +08:00
DingZQ 65d6b6aa66 Refine 2025-04-04 12:36:59 +08:00
DingZQ 094d6b690a Refine 2025-04-04 12:34:38 +08:00
DingZQ 9afeb7f6e8 Refine 2025-04-04 12:33:16 +08:00
DingZQ 92cd2f0bff fastapi_burst_analysis cache immediately 2025-04-04 12:28:44 +08:00
DingZQ cefba29195 Refine 2025-04-04 11:15:41 +08:00
DingZQ 6f06eb43e7 Refine 2025-04-04 11:07:46 +08:00
DingZQ fd2d9dda64 Refine 2025-04-04 10:59:47 +08:00
DingZQ b8deadbd11 Refine msgpack usage for datetime 2025-04-04 10:56:40 +08:00
DingZQ 2f14395d1e Refine 2025-04-04 09:44:26 +08:00
DingZQ 180935a0a8 Add API clearrediskeys 2025-04-01 21:11:47 +08:00
DingZQ 39bb19363e Cache methods getall***properties 2025-03-29 21:32:54 +08:00
DingZQ 501b2933d0 Refine 2025-03-29 21:26:11 +08:00
DingZQ e452cbfa53 Refine 2025-03-29 21:25:38 +08:00
DingZQ 08586fc012 Cache getnetworkgeometries 2025-03-29 21:24:01 +08:00
DingZQ 90eb873ecf Fixed node properties 2025-03-29 17:24:49 +08:00
DingZQ f795ca29f1 Refine influxdb api 2025-03-29 17:18:01 +08:00
DingZQ 0431f4d82e Add API to get all node properties 2025-03-29 17:08:31 +08:00
DingZQ d1687b8464 Add API to get all valve and pumps 2025-03-29 17:02:30 +08:00
DingZQ 6e3d149503 Refine 2025-03-29 16:42:34 +08:00
DingZQ 1dafaa6dcc Add API to get all pipes 2025-03-29 16:30:15 +08:00
DingZQ 94773a59f8 Refine 2025-03-29 14:43:05 +08:00
DingZQ b8c08ac89c Refine 2025-03-28 22:48:50 +08:00
DingZQ 27fd74dfbf Refine 2025-03-28 22:41:26 +08:00
DingZQ ff9c0a0095 Refine 2025-03-28 22:38:55 +08:00
DingZQ c1b4a3685c Refine 2025-03-28 20:43:16 +08:00
DingZQ dfb247231f Refine 2025-03-28 20:41:18 +08:00
DingZQ 5376b150e9 Refine 2025-03-28 20:39:59 +08:00
DingZQ 54b4880977 Refine 2025-03-28 20:25:52 +08:00
DingZQ a6a0496a86 Refine 2025-03-28 20:20:03 +08:00
DingZQ dee3c9c6f8 Refine 2025-03-28 20:16:23 +08:00
DingZQ eb267803fe Refine 2025-03-28 20:06:13 +08:00
DingZQ 3e7258a55d Refine 2025-03-28 20:04:18 +08:00
DingZQ e90a7b205f Refine 2025-03-28 19:58:47 +08:00
DingZQ e0b6183ff0 Add user api 2025-03-27 21:25:44 +08:00
DingZQ 465831946e Refine 2025-03-25 21:19:35 +08:00
DingZQ 380a5df96f Refine 2025-03-25 21:15:46 +08:00
DingZQ 9b480b709e Refine 2025-03-25 20:50:50 +08:00
DingZQ 12dd85be21 Refine 2025-03-24 22:31:34 +08:00
DingZQ 99f47c94db Refine 2025-03-24 22:23:54 +08:00
DingZQ 4968be4a70 Refine 2025-03-24 22:22:31 +08:00
DingZQ 904761a7f1 Refine 2025-03-24 22:20:15 +08:00
DingZQ 056fa48a90 Refine 2025-03-24 22:17:27 +08:00
DingZQ 7e19ef83ec Refine 2025-03-24 22:10:53 +08:00
DingZQ 725cb7aa1e Refine 2025-03-24 22:10:25 +08:00
DingZQ 837c262f91 Remove influx_client 2025-03-24 22:07:14 +08:00
DingZQ f799630e43 Refine 2025-03-24 22:02:03 +08:00
DingZQ 49be1c7089 Update influxdb api 2025-03-24 21:59:17 +08:00
DingZQ 4a824707de Refine 2025-03-24 21:34:42 +08:00
DingZQ 0f18355a9c Refine 2025-03-24 21:33:00 +08:00
DingZQ eedd04549d Refine 2025-03-24 21:28:19 +08:00
DingZQ a6ebd79dae Refine 2025-03-24 21:26:40 +08:00
DingZQ a5c81b368e Update online_Analysis 2025-03-24 21:24:47 +08:00
DingZQ 21966d5669 Update create_template for user and scheme 2025-03-24 21:22:41 +08:00
DingZQ 189ad0bf8d Add users and scheme sql 2025-03-24 21:21:55 +08:00
DingZQ 55bab8cf60 Update simulation.py from WMH 2025-03-23 12:21:02 +08:00
DingZQ 51d45ef192 Refine 2025-03-22 15:23:19 +08:00
DingZQ 3f5b7e2319 Refine 2025-03-22 15:22:26 +08:00
DingZQ 97ff500422 Refine 2025-03-22 15:20:29 +08:00
DingZQ 9ab56c941a Refine 2025-03-22 15:19:05 +08:00
DingZQ 3c8b308419 Refine 2025-03-22 15:17:47 +08:00
DingZQ cbcf964c23 Refine 2025-03-22 15:16:22 +08:00
DingZQ 360a3eae94 Refine 2025-03-22 15:07:54 +08:00
DingZQ 434805029b Refine 2025-03-22 13:26:25 +08:00
DingZQ ea09d71604 Refine 2025-03-22 12:49:41 +08:00
DingZQ 8c930f1f7d Refine 2025-03-22 12:42:08 +08:00
DingZQ 98739b2510 Refine 2025-03-22 12:17:24 +08:00
DingZQ 60d6437777 Refine 2025-03-22 12:15:52 +08:00
DingZQ 0132ae1a79 Refine 2025-03-22 12:14:49 +08:00
DingZQ aab5de2840 Refine 2025-03-22 12:12:57 +08:00
DingZQ bf8f7d08e1 Refine 2025-03-22 08:38:11 +08:00
DingZQ 790db21a8e Update auto_cache 2025-03-22 08:29:07 +08:00
DingZQ 9c24dab4a3 Refine 2025-03-22 08:19:32 +08:00
DingZQ 7108e8f70d 'We 2025-03-21 21:53:42 +08:00
DingZQ 442dab54da Refine 2025-03-19 22:41:02 +08:00
DingZQ db4718e72b Refine 2025-03-19 22:35:29 +08:00
DingZQ eeedcca586 Refine 2025-03-19 22:32:07 +08:00
DingZQ fe1fa97000 Refine 2025-03-19 22:29:56 +08:00
DingZQ 62fc0e1271 Refine 2025-03-19 22:28:47 +08:00
DingZQ b3351ba6f8 Refine 2025-03-19 22:27:36 +08:00
DingZQ cfd87d82cc Refine 2025-03-19 22:25:55 +08:00
DingZQ 38f3e3a41a Refine 2025-03-19 22:25:03 +08:00
DingZQ 45fb4f268b Refine 2025-03-19 22:23:37 +08:00
DingZQ 2d6f8d8d68 Refine 2025-03-19 22:23:04 +08:00
DingZQ e56e3e0078 Refine 2025-03-19 22:21:53 +08:00
DingZQ fb2f6aee80 'Add 2025-03-19 21:57:58 +08:00
DingZQ c1a6242d7d Refine 2025-03-18 21:15:14 +08:00
DingZQ af55aca469 Refine 2025-03-16 22:03:52 +08:00
DingZQ 62ca3fd206 Refine 2025-03-16 13:15:29 +08:00
DingZQ bdefec8c9f Refine 2025-03-16 12:51:16 +08:00
DingZQ 317217b5f3 Refine 2025-03-16 12:50:22 +08:00
DingZQ d404809111 Add API queryallschemeallrecords 2025-03-16 12:45:28 +08:00
DingZQ 4058ddaed7 Refine 2025-03-15 21:45:06 +08:00
DingZQ 89f87aa63c Refine 2025-03-15 19:02:35 +08:00
DingZQ 22ffaaf410 Refine 2025-03-15 18:06:02 +08:00
DingZQ 86da9b2c60 Refine 2025-03-15 18:04:45 +08:00
DingZQ 5bc509ed7b Refine 2025-03-15 17:55:29 +08:00
DingZQ 0de513cda4 Refine 2025-03-15 17:54:06 +08:00
DingZQ 80f1885194 Refine 2025-03-15 17:48:47 +08:00
DingZQ c3510b3c2c Refine 2025-03-15 17:47:08 +08:00
DingZQ 32b7ae9f5c Refine 2025-03-15 17:42:36 +08:00
DingZQ f70ec0821b Refine 2025-03-15 17:35:30 +08:00
DingZQ fceedf9cba Refine 2025-03-15 17:35:09 +08:00
DingZQ f4f6bc559c Refine 2025-03-15 17:19:20 +08:00
DingZQ 40bc1491cc Refine 2025-03-15 17:16:29 +08:00
DingZQ 6f1a0ec694 Refine 2025-03-15 17:14:00 +08:00
DingZQ ef2ece752c Refine 2025-03-15 17:02:03 +08:00
DingZQ 48d99c6bb0 Refine 2025-03-15 17:00:36 +08:00
DingZQ 8701cf4b0c Refine 2025-03-15 16:59:51 +08:00
DingZQ a3e5d55ed8 Refine 2025-03-15 16:49:49 +08:00
DingZQ e8e8c115d5 Refine 2025-03-15 16:15:23 +08:00
DingZQ 78cdc0af7e Refine 2025-03-15 14:35:05 +08:00
DingZQ 176d6f00cd Refine 2025-03-15 14:26:38 +08:00
DingZQ e67f487737 Refine 2025-03-15 14:10:05 +08:00
DingZQ c146f68fc7 Refine 2025-03-15 14:05:59 +08:00
DingZQ 9a541b0d73 Refine 2025-03-15 14:01:10 +08:00
DingZQ 5b51ec147c Refine 2025-03-15 13:59:09 +08:00
DingZQ cc6e2819e8 Update with WMH's code 2025-03-15 13:54:45 +08:00
DingZQ 071f6ec100 Refine 2025-03-12 00:25:40 +08:00
DingZQ 8aa831c53e Refine 2025-03-12 00:24:25 +08:00
DingZQ 33252b721c Refine 2025-03-12 00:22:30 +08:00
DingZQ f249f0c7b0 Refine 2025-03-12 00:19:33 +08:00
DingZQ 6c3dc95954 Print before and after simulation 2025-03-12 00:12:31 +08:00
DingZQ 8045e6e79f 'Install 2025-03-11 22:19:39 +08:00
DingZQ 2000385de9 Update inp file 2025-03-09 23:22:16 +08:00
DingZQ 638406af48 Update inp file 2025-03-09 23:00:33 +08:00
DingZQ 8afdc62259 Refine 2025-03-09 22:46:25 +08:00
DingZQ 7b3fd12b4f Refine 2025-03-09 22:45:37 +08:00
DingZQ b42fa6c551 Refine 2025-03-09 22:43:24 +08:00
DingZQ 43db502aa0 Merge branch 'dingsu/shadell2' into TencentServer2 2025-03-09 15:16:02 +08:00
DingZQ 5cbcf83002 Update time_api 2025-03-09 15:15:57 +08:00
DingZQ 14d9fd0f4b install msgpack 2025-03-09 10:26:23 +08:00
DingZQ ced065a455 Merge branch 'dingsu/shadell2' into TencentServer2 2025-03-09 10:24:32 +08:00
DingZQ 637e05acdb Add redis methods 2025-03-09 10:24:24 +08:00
DingZQ 92363c0b40 Add method query redis 2025-03-09 10:23:02 +08:00
DingZQ 246c6592ed Use redis the store the cache 2025-03-09 10:18:54 +08:00
DingZQ 65d2031889 Merge branch 'dingsu/shadell2' into TencentServer2 2025-03-09 00:42:01 +08:00
DingZQ f2f1660b29 Fixed time format issue 2025-03-09 00:41:55 +08:00
DingZQ 346187329d Merge branch 'dingsu/shadell2' into TencentServer2 2025-03-09 00:38:00 +08:00
DingZQ 0903e8c6a5 Refine 2025-03-09 00:37:52 +08:00
DingZQ 2cf4ce89ea Merge branch 'dingsu/shadell2' into TencentServer2 2025-03-09 00:23:36 +08:00
DingZQ 05ea9e7045 Cache data in fastapi side 2025-03-09 00:23:27 +08:00
DingZQ d8d3157d43 Merge branch 'dingsu/shadell2' into TencentServer2 2025-03-08 23:38:48 +08:00
DingZQ b905c0316d Query all scada data 2025-03-08 23:38:34 +08:00
DingZQ 3ad3d36b2a Merge branch 'dingsu/shadell2' into TencentServer2 2025-03-08 23:27:19 +08:00
DingZQ fc2635ada7 Cache scada data 2025-03-08 23:27:10 +08:00
DingZQ 011b8fa326 Merge branch 'dingsu/shadell2' into TencentServer2 2025-03-08 23:15:08 +08:00
DingZQ ad029b1c92 Use cache to improve performance 2025-03-08 23:14:47 +08:00
DingZQ 0020a95f47 Merge branch 'dingsu/shadell2' into TencentServer2 2025-03-08 21:52:16 +08:00
DingZQ 6e952f62b6 Only return 2 hours to improve perfornace 2025-03-08 21:51:48 +08:00
DingZQ d2dc114c9c Merge branch 'dingsu/shadell2' into TencentServer2 2025-03-08 17:28:04 +08:00
DingZQ d9cb775540 Add comments 2025-03-08 17:27:45 +08:00
DingZQ ec1dde7d43 Merge branch 'dingsu/shadell2' into TencentServer2 2025-03-08 12:27:24 +08:00
DingZQ 5133b93c1f Refine api query_all_record_by_date_property 2025-03-08 12:27:10 +08:00
DingZQ acba5ef8c2 Merge branch 'dingsu/shadell2' into TencentServer2 2025-03-08 12:07:53 +08:00
DingZQ 0884b95a81 Refine key/value pair for get all scada elements 2025-03-08 12:07:23 +08:00
DingZQ 4f5cdb42db Add API getallscadarecordsbydate 2025-03-08 11:51:52 +08:00
DingZQ c1ba35e1b2 Add API getallscadarecordsbydate 2025-03-08 11:51:03 +08:00
DingZQ f098aa42ae Fixed time error 2025-03-08 10:35:20 +08:00
DingZQ f728ace4a7 Fixed time issue 2025-03-07 22:08:47 +08:00
DingZQ 0b66c22807 Update the api token 2025-03-07 21:19:10 +08:00
DingZQ 99cf111117 Refine 2025-03-04 21:47:49 +08:00
DingZQ 90b46de3dc Refine 2025-03-04 21:45:28 +08:00
DingZQ 47b3b8644d Refine 2025-03-04 21:43:36 +08:00
DingZQ e818522de8 Refine 2025-03-04 21:26:28 +08:00
DingZQ 6d5a70deb3 Refine 2025-03-04 21:24:07 +08:00
DingZQ 2d3799f10e Refine 2025-03-04 21:20:33 +08:00
DingZQ 30c4e40180 Refine 2025-03-04 21:15:08 +08:00
DingZQ afa26a561f Add API queryallrecordsbydateproperty 2025-03-04 21:06:52 +08:00
DingZQ 05569f8859 Update influxdb_api 2025-03-04 20:59:45 +08:00
DingZQ de0b4403d4 Refine 2025-03-02 19:36:15 +08:00
DingZQ bd21d71be7 Add more scripts 2025-03-02 19:35:20 +08:00
DingZQ 144f786f39 Refine 2025-03-02 19:22:24 +08:00
DingZQ 3ef178e5b0 Refine 2025-03-02 18:04:38 +08:00
DingZQ a2d6391d9a Refine 2025-03-02 18:03:33 +08:00
DingZQ 9d0c9df18e Refine 2025-03-02 18:02:25 +08:00
DingZQ 8485f8d3fc Refine 2025-03-02 17:58:14 +08:00
DingZQ 3c9db341c8 Refine 2025-03-02 17:57:25 +08:00
DingZQ 65e3d7bc40 Refine 2025-03-02 17:48:05 +08:00
DingZQ b138d15efb Refine 2025-03-02 17:45:20 +08:00
DingZQ 90329966f5 Refine 2025-03-02 17:35:18 +08:00
DingZQ de9e9f9fb3 Fixed time api 2025-03-02 17:30:47 +08:00
DingZQ 83826aa85a Add api fastapi_query_scada_data_by_device_id_and_date 2025-03-02 17:25:21 +08:00
DingZQ 7892f8ac20 Refine 2025-03-02 17:18:01 +08:00
DingZQ cd35519e62 Add more time api 2025-03-02 17:16:00 +08:00
DingZQ eb801660c8 Add API getallscadaproperties 2025-03-02 11:07:53 +08:00
DingZQ 36e1692ccf Fixed the getelementpropertieswithtype 2025-03-02 10:59:21 +08:00
DingZQ d14666fcff Refine 2025-03-01 22:04:27 +08:00
DingZQ cfce9acba9 Refine 2025-03-01 22:03:38 +08:00
DingZQ 7d28bd8632 Refine 2025-03-01 21:48:41 +08:00
DingZQ b6851ac321 Refine 2025-03-01 21:38:29 +08:00
DingZQ e591eba2f5 Refine 2025-03-01 21:35:21 +08:00
DingZQ ecf37ae50b Refine 2025-03-01 21:17:15 +08:00
DingZQ 30d1688c11 Refine 2025-03-01 21:14:48 +08:00
DingZQ 0a6d69d5de Refine 2025-03-01 20:58:53 +08:00
DingZQ 5bb4237010 Refine 2025-03-01 20:57:59 +08:00
DingZQ 0c74389874 Refine 2025-03-01 20:23:07 +08:00
DingZQ 305c047f20 Refine 2025-03-01 20:19:12 +08:00
DingZQ 373fc1facd Add server 2025-03-01 20:18:01 +08:00
DingZQ 0d4128c8c8 Refine code with cursor 2025-02-28 21:16:42 +08:00
DingZQ ae60649726 Update scada_info.csv 2025-02-27 21:11:59 +08:00
DingZQ 57d11aefa3 Refine 2025-02-24 21:03:33 +08:00
DingZQ 7e075cb02d Refine 2025-02-24 21:01:59 +08:00
DingZQ 7151476709 Refine 2025-02-24 21:00:41 +08:00
DingZQ 64389c8a81 Refine 2025-02-24 20:57:18 +08:00
DingZQ 7737b4a190 Refine 2025-02-24 20:55:00 +08:00
DingZQ 9dc8481b3a Refine 2025-02-24 20:52:51 +08:00
DingZQ 953140b6ff Refine 2025-02-24 20:52:23 +08:00
DingZQ f7f905fdf4 Refine 2025-02-24 20:49:16 +08:00
DingZQ 4bfbf3ac34 Refine 2025-02-24 20:46:42 +08:00
DingZQ 5cf7efc3a9 Refine 2025-02-24 20:36:00 +08:00
DingZQ d3e2961c60 Refine 2025-02-24 20:34:17 +08:00
DingZQ e8c6dfb19b Refine 2025-02-24 20:32:37 +08:00
DingZQ 486471f3e8 Refine 2025-02-24 20:31:52 +08:00
DingZQ 294fea9aa8 Refine 2025-02-24 20:29:57 +08:00
DingZQ f9546ebf4d Refine 2025-02-23 11:08:56 +08:00
DingZQ 7dfeee8c22 Refine 2025-02-23 10:51:55 +08:00
DingZQ e02e78d2cc Refine 2025-02-23 10:47:55 +08:00
DingZQ cfa5df6dc4 Refine 2025-02-23 10:42:30 +08:00
DingZQ ef1786a4c8 Refine 2025-02-23 10:36:15 +08:00
DingZQ 40a79ce590 Refine 2025-02-23 10:33:17 +08:00
DingZQ c1878e4375 Refine 2025-02-23 10:31:30 +08:00
DingZQ 575c7b9dc9 Refine 2025-02-23 10:16:54 +08:00
DingZQ b48e67b47e Refine 2025-02-23 10:12:34 +08:00
DingZQ a20a1f63c9 Refine 2025-02-23 09:54:02 +08:00
DingZQ 14358fc533 Refine 2025-02-23 09:52:49 +08:00
DingZQ 23044f1338 Refine 2025-02-23 09:51:32 +08:00
DingZQ a7b15568aa Refine 2025-02-23 09:44:18 +08:00
DingZQ 86e894e783 Add API to query all records by date 2025-02-23 09:39:52 +08:00
DingZQ 13acaf2572 Refine 2025-02-23 00:46:28 +08:00
DingZQ bd9ed05e13 get scada infos 2025-02-23 00:42:37 +08:00
DingZQ 0decc88d49 Refine 2025-02-23 00:38:23 +08:00
DingZQ ec532f75c8 Refine 2025-02-23 00:36:59 +08:00
DingZQ cb9f7c72a1 Refine 2025-02-23 00:35:39 +08:00
DingZQ c4df07e081 Refine 2025-02-23 00:33:58 +08:00
DingZQ 8cce40ffdb Refine 2025-02-23 00:31:05 +08:00
DingZQ 7d8562ae38 Refine 2025-02-23 00:24:49 +08:00
DingZQ aed90cfc7d Refine 2025-02-23 00:13:33 +08:00
DingZQ bc0365144c Add fastapi for scada info 2025-02-23 00:09:49 +08:00
DingZQ 895d4d2031 Add scada_info API 2025-02-23 00:05:12 +08:00
DingZQ 93aaf7d553 Add scada_info api 2025-02-23 00:00:20 +08:00
DingZQ 8745192589 Refine 2025-02-22 18:10:04 +08:00
DingZQ c3a51020d9 Add more libs to install.py 2025-02-22 17:42:28 +08:00
DingZQ 998d16bec7 Refine 2025-02-22 17:22:33 +08:00
DingZQ 0d7246b8d4 Update simulation.py 2025-02-22 17:21:22 +08:00
DingZQ 0f18a85641 Update online_anaylysis 2025-02-22 17:19:47 +08:00
DingZQ 9e58ca9254 Refine 2025-02-22 17:18:43 +08:00
DingZQ 7cd9493fa3 Update main.py 2025-02-22 17:18:34 +08:00
DingZQ fa435b105f Add influxdb_api 2025-02-22 17:15:47 +08:00
DingZQ a8ca7e9a19 Add globals 2025-02-22 17:11:31 +08:00
DingZQ 2d681c8349 Add comments for time 2025-02-15 16:57:30 +08:00
DingZQ 9a274b0c67 Refine 2025-02-15 15:55:05 +08:00
DingZQ 8384f0720d Refine 2025-02-15 15:51:25 +08:00
DingZQ bf4e589c4e Refine 2025-02-15 15:45:01 +08:00
DingZQ 842abaceb2 Add API queryscadadatabydeviceidandtime 2025-02-15 15:43:09 +08:00
DingZQ 4e5c9194f5 Refine 2025-02-15 15:30:51 +08:00
DingZQ e6a3fbd13c Refine 2025-02-15 15:29:10 +08:00
DingZQ 86cf0b1147 Refine 2025-02-15 15:27:34 +08:00
DingZQ 6847b35813 Refine 2025-02-15 15:24:36 +08:00
DingZQ 8c16a090ec Refine 2025-02-15 15:18:15 +08:00
DingZQ 1a23667b1b Add API to query influxdb info 2025-02-15 15:15:36 +08:00
DingZQ 13cdae6362 Refine 2025-02-15 15:02:00 +08:00
DingZQ 460aa79224 Update the query for scada 2025-02-15 14:59:40 +08:00
DingZQ 7db2b2c0ac Add time api 2025-02-15 14:53:46 +08:00
DingZQ e66a3dd7df Add scada data to API getnetworkgeometries 2025-02-15 14:20:07 +08:00
DingZQ de40ac7c4f Print time 2025-02-15 00:17:20 +08:00
DingZQ 528c660ddc Refine 2025-02-14 22:25:55 +08:00
DingZQ 21a6c54f58 Add api to query scada 2025-02-14 22:22:28 +08:00
DingZQ b18be90207 Refine 2025-02-14 22:13:37 +08:00
DingZQ 3df6dfe247 'Update 2025-02-14 22:11:33 +08:00
DingZQ 60ae213d15 Refine 2025-02-14 21:59:27 +08:00
DingZQ 65c61ac35c Add API a single id 2025-02-14 21:49:57 +08:00
DingZQ 5b18ec816e Add API queryscadadatabydeviceidandtime 2025-02-14 21:43:51 +08:00
DingZQ 64760b62c4 Fixed API name error 2025-02-14 21:29:56 +08:00
DingZQ f7d612d522 Return scada properties from API getelementproperties 2025-02-12 22:56:51 +08:00
DingZQ 4872772165 Refine 2025-02-10 22:05:06 +08:00
DingZQ 4bf2ef12aa Set influxdb client timeout to 100 seconds 2025-02-10 22:03:14 +08:00
DingZQ a17a0b3a89 Refine 2025-02-09 23:07:55 +08:00
DingZQ 5ceb893ad2 Update influxdb_api to try query data 2025-02-09 23:03:06 +08:00
DingZQ 137c74348b Refine 2025-02-09 13:27:07 +08:00
DingZQ f8422a6ec0 Refine 2025-02-09 13:23:17 +08:00
DingZQ 7eddd95947 Refine 2025-02-09 13:19:55 +08:00
DingZQ bdcd505733 Refine 2025-02-09 13:13:22 +08:00
DingZQ 59875effbf Refine 2025-02-09 13:10:44 +08:00
DingZQ e3e46b48e1 Refine 2025-02-09 13:00:16 +08:00
DingZQ 29841ddf6c Refine 2025-02-09 12:54:47 +08:00
DingZQ b60e0fc1dc Refine 2025-02-09 12:49:08 +08:00
DingZQ 0015152c04 Refine 2025-02-09 12:28:43 +08:00
DingZQ 24083d38d1 Add base64 encoding token 2025-02-09 11:09:12 +08:00
DingZQ bde762e64e 'Add 2025-02-09 11:05:18 +08:00
DingZQ 9671989ed3 Add redis 2025-02-09 10:46:24 +08:00
DingZQ d395a68935 Add influx db informaiton 2025-02-09 10:43:02 +08:00
DingZQ 1bb14ad06b Enable PATTERN_TIME_STEP 2025-02-09 10:33:27 +08:00
DingZQ 1131f7b1f9 Update auto_store_non_realtime 2025-02-09 10:17:07 +08:00
DingZQ fa643aca49 'Fixed 2025-02-09 00:04:16 +08:00
DingZQ 9beaf72502 Add scada_info.csv 2025-02-08 22:38:11 +08:00
DingZQ bf99e7f347 Refine 2025-02-08 22:12:18 +08:00
DingZQ daf392f362 Refine 2025-02-08 22:11:29 +08:00
DingZQ 27388eb96a Add query script 2025-02-08 22:10:13 +08:00
DingZQ 0eb190aa09 Remove auto_store_realtime_SCADA_data.py, it has been renamed to auto_realtime.py 2025-02-08 21:50:50 +08:00
DingZQ 20d3566f30 Remove chinese 2025-02-08 21:30:38 +08:00
DingZQ 3e873fad78 Refine 2025-02-08 21:19:14 +08:00
DingZQ a19b3c5f55 Refine 2025-02-08 21:18:18 +08:00
DingZQ a0793a9494 Refine 2025-02-08 21:17:28 +08:00
DingZQ 8a1795481a Refine 2025-02-08 21:16:57 +08:00
DingZQ 44b4ad2bd0 Refine 2025-02-08 21:16:20 +08:00
DingZQ 6b1b15f28c Add file influxdb_info and use information from the file 2025-02-08 21:06:59 +08:00
DingZQ d8c8eb1002 Refine 2025-02-08 20:48:43 +08:00
DingZQ 71e4af59eb Move run_simulation.py to Archive folder. We don't use it now 2025-02-08 20:47:52 +08:00
DingZQ c7434c3bcc Refine 2025-02-08 20:45:26 +08:00
DingZQ c5fc5ed9ce Add csv provided by WMH 2025-02-08 20:23:29 +08:00
DingZQ 60fd141b3c Comment out get_real_status 2025-02-08 20:17:21 +08:00
DingZQ e360540989 Add more code from WMH 2025-02-08 20:11:27 +08:00
DingZQ f6f37d012b Comment get_real_status.py' 2025-02-07 23:26:52 +08:00
DingZQ 8e3f62e06d Add run_simulation.py 2025-02-07 23:23:20 +08:00
DingZQ 1279083303 Refine 2025-02-07 23:14:21 +08:00
DingZQ 6a507dca7b Add more project methods 2025-02-07 23:12:28 +08:00
DingZQ 2ec2e10ff4 Comment run_simulation 2025-02-07 23:03:04 +08:00
DingZQ 15147b97fc Add WMH code 2025-02-07 22:58:15 +08:00
DingZQ c40903289e Add api getelementproperties without type 2025-02-06 10:43:23 +08:00
DingZQ e16d138c2c Add new api getnodetype, getlinktype, getelementtype and getelementtypevalue 2025-02-05 23:12:53 +08:00
DingZQ 5e8d738a27 Add fastapi getelementproperties 2025-02-05 22:24:28 +08:00
DingZQ c51b8524b0 Remove unnecessary logger info 2025-02-05 22:13:50 +08:00
DingZQ cf335052cf 增加统一接口get_node_properties get_link_properties获取所有类型的属性值 2025-02-05 20:37:50 +08:00
DingZQ 83d73a96bd Add output to 'output' node 2025-02-05 16:18:33 +08:00
DingZQ 49e85d51e8 Refine 2025-02-05 16:14:50 +08:00
DingZQ 44704539a4 We should dump readable output for API run_project_return_dict 2025-02-05 16:10:37 +08:00
DingZQ 8751347103 We should not dump REGION/BOUND/REGION_NODES to v2 inp file 2025-02-05 16:01:18 +08:00
DingZQ 08a4d4d2bb Fixed error 2025-02-05 15:16:11 +08:00
DingZQ 15b560dca4 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2025-02-05 15:13:44 +08:00
DingZQ 49a14a8343 Update 2025-02-05 15:13:32 +08:00
DingZQ 03c4e5772d Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2025-02-05 15:09:17 +08:00
DingZQ 7803164f47 Add fastapi fastapi_run_project_return_dict to return the text version of simulation results 2025-02-05 15:08:58 +08:00
DingZQ 5f9b5bd310 Add fastapi fastapi_run_project_return_dict to return the text version of simulation results 2025-02-05 15:06:00 +08:00
DingZQ 1996ffd17d Refine 2025-01-31 13:58:35 +08:00
DingZQ ad99d75548 Refine 2025-01-31 13:56:24 +08:00
DingZQ 45025e9988 Add influx db api 2025-01-31 13:54:53 +08:00
DingZQ 8e9859e2c1 Add influx db client to fastapi 2025-01-31 13:33:47 +08:00
DingZQ 35c3bf986c Refine 2025-01-31 13:31:41 +08:00
DingZQ c088a87d28 Update influx database info 2025-01-31 13:29:09 +08:00
DingZQ a55d53a60b Add comments for scada 2025-01-31 13:00:13 +08:00
DingZQ ca06f31e0a Refine 2025-01-31 09:30:35 +08:00
DingZQ 7884d0e562 Add startup method 2025-01-31 09:26:57 +08:00
DingZQ 32a4c15b6d delete redis key 2025-01-30 23:25:02 +08:00
DingZQ ab18c801e9 Refine 2025-01-30 23:23:27 +08:00
DingZQ c2417c7ed9 Merge 2025-01-30 23:20:27 +08:00
DingZQ 02de084fb6 Update startfastapiserver.bat 2025-01-30 23:18:52 +08:00
DingZQ f9a70eca1f Refine 2025-01-30 23:16:49 +08:00
DingZQ 8d82ffa234 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2025-01-30 23:15:42 +08:00
DingZQ e3fe2ddf51 Refine 2025-01-30 23:15:31 +08:00
DingZQ 71bbad3862 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2025-01-30 23:12:21 +08:00
DingZQ 5da8fb7c07 Refine 2025-01-30 23:12:10 +08:00
DingZQ 436a2f299e Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2025-01-30 23:11:09 +08:00
DingZQ 167fb434a2 Refine 2025-01-30 23:09:44 +08:00
DingZQ d133ebba84 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2025-01-30 23:08:18 +08:00
DingZQ 618a374a29 Refine 2025-01-30 23:08:09 +08:00
DingZQ 7bf31af7bb Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2025-01-30 23:05:12 +08:00
DingZQ ab0e674f8c Use redis 2025-01-30 23:05:02 +08:00
DingZQ 29883cc123 Refine 2025-01-30 22:59:02 +08:00
DingZQ 8e4574b167 Use redis to restrict corrurent access 2025-01-30 22:54:29 +08:00
DingZQ 97cd59cfae Add comemnts 2025-01-30 21:39:40 +08:00
DingZQ 1193a4fe30 Add API to get both nodes and links 2025-01-27 21:39:20 +08:00
DingZQ 97d1022460 Fixed the method of get all scada elements 2025-01-25 16:35:27 +08:00
DingZQ b453779fb2 Update create_template 2025-01-15 22:42:33 +08:00
DingZQ 1b6ce083cd Add scada info and history_patterns_flows 2025-01-15 22:41:25 +08:00
DingZQ 362439c52a Refine 2025-01-04 16:16:57 +08:00
DingZQ 01ffe3bde7 Refine 2025-01-04 16:14:55 +08:00
DingZQ 102f97b020 Add fastapi getnetworkinextent 2025-01-04 16:11:26 +08:00
DingZQ 98e58d1e53 Add method to get links in extent 2025-01-04 16:10:01 +08:00
DingZQ cf2c90d5f9 Add method to get nodes in extent 2025-01-04 10:35:43 +08:00
DingZQ ff83c9a974 Refine 2025-01-01 13:34:11 +08:00
DingZQ 9bd4e5b84b Refine 2025-01-01 13:31:48 +08:00
DingZQ 9f49d17027 Refine 2025-01-01 13:30:32 +08:00
DingZQ 670b4afe6b Refine 2025-01-01 13:28:04 +08:00
DingZQ a88e79cb86 Refine 2025-01-01 13:26:18 +08:00
DingZQ e024e8b904 Add script startfastapiserver.bat 2025-01-01 13:22:53 +08:00
DingZQ ced0f50420 Add time checker 2025-01-01 13:14:51 +08:00
DingZQ 6f0ad1baee 'Add 2025-01-01 10:32:06 +08:00
DingZQ fa00a5ff81 Refine 2025-01-01 10:27:15 +08:00
DingZQ 9e7a4616b4 Add diameter to getmajornodes/getmajorpipes 2025-01-01 10:19:12 +08:00
DingZQ 51e3ae18bb Change major diameter to 0 2025-01-01 00:43:56 +08:00
DingZQ 212c9e7182 Change major diameter to 100 2025-01-01 00:41:48 +08:00
DingZQ 1d09d41935 Add API to get major nodes and major pipes 2025-01-01 00:34:02 +08:00
DingZQ 69978c9868 Refine 2024-12-28 17:27:00 +08:00
DingZQ 4e9c5ed551 Refine 2024-12-28 17:22:43 +08:00
DingZQ 26f6c4436f Refine 2024-12-28 17:20:47 +08:00
DingZQ 27485ef8fc Refine 2024-12-28 17:18:45 +08:00
DingZQ 65b5c249a7 Refine 2024-12-28 17:13:42 +08:00
DingZQ cad6eeba47 Add API covnertv3tov2 2024-12-28 16:47:16 +08:00
DingZQ 17b47da6ea Add link type information for link 2024-12-27 20:13:39 +08:00
DingZQ 97b69eb5d0 Refine 2024-12-26 22:37:24 +08:00
DingZQ 0e44625f00 Refine method get_network_node_coords 2024-12-26 22:37:06 +08:00
DingZQ 147f88debd Refine 2024-12-22 14:03:54 +08:00
DingZQ d8c964308b Refine 2024-12-22 13:59:45 +08:00
DingZQ 66e5dae590 Refine 2024-12-22 13:51:37 +08:00
DingZQ 0b74a2ece9 Add document for PG Python env 2024-12-22 13:47:52 +08:00
DingZQ c6629f9a33 Update document for PG 2024-12-21 20:44:18 +08:00
DingZQ 70afb19184 Return type from getnetworkcoord 2024-12-18 22:41:35 +08:00
DingZQ f8ac46d80d Add methods to get node types 2024-12-18 22:28:57 +08:00
DingZQ 1e43cdb17a Refine 2024-12-09 23:38:44 +08:00
DingZQ 41e80212e1 Add influx api 2024-12-09 23:28:47 +08:00
DingZQ c7d51769e1 Refine 2024-12-08 21:13:58 +08:00
DingZQ de5ae4d1e5 Add API get network link nodes 2024-12-08 21:08:06 +08:00
DingZQ 8b818185ff Refine 2024-12-08 19:05:05 +08:00
DingZQ 000321c09c Refine 2024-12-08 18:56:11 +08:00
DingZQ c007685eaa Refine 2024-12-08 18:54:01 +08:00
DingZQ 103e863780 Refine 2024-12-08 18:49:36 +08:00
DingZQ fa26c46d7d Refine 2024-12-08 18:42:57 +08:00
DingZQ 41fa93cace Refine 2024-12-08 18:28:50 +08:00
DingZQ f8655fe8b6 Refine 2024-12-08 18:11:04 +08:00
DingZQ 73598b7b38 Add API get network coords 2024-12-08 18:08:52 +08:00
DingZQ 8d6d7b5c59 Refine 2024-12-07 17:05:08 +08:00
DingZQ 65c9913e83 Update server information 2024-12-07 16:59:18 +08:00
王琼钰 9e855b4584 Accept Merge Request #391: (wqy/fix-sa -> master)
Merge Request: Fix sa

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/391?initial=true
2024-04-13 09:36:36 +08:00
wqy dc233db163 Fix sa 2024-04-13 09:35:37 +08:00
xinzish e8fddfb71a redistribute the demands 2024-04-12 23:59:09 +08:00
xinzish 330248041f handle get history Q and H data 2024-04-12 18:12:58 +08:00
xinzish 44e487f85f start real time simulation api 2024-04-12 14:32:05 +08:00
xinzish 0976ba11af refine reading Service Area data from inp file 2024-04-12 14:29:12 +08:00
xinzish 0510d42bbe refine 2024-04-09 14:07:36 +08:00
xinzish 4389b4ea74 import geometry information from inp files to database 2024-04-09 14:06:26 +08:00
xinzish 46f898a000 refine 2024-04-08 23:41:36 +08:00
xinzish c1a5759220 read Region sections in inp 2024-04-08 23:23:02 +08:00
xinzish 5e80f35ae5 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer
inp file encoding improvement
2024-04-07 09:45:02 +08:00
xinzish e1b1f8d8ce to solve the encoding problem of inp file exchange 2024-04-07 09:36:12 +08:00
wqy 20da69c7a6 Improve SA algorithm 2024-01-19 00:08:04 +08:00
wqy d29a1a1a1a Fix import 2024-01-19 00:07:10 +08:00
wqy 6c97abdd16 Fix inp for DDA 2024-01-19 00:05:53 +08:00
wqy 0f7334bdaa Upload C dll 2024-01-19 00:04:52 +08:00
wqy 2c4a4365fe Fix option error 2024-01-19 00:04:30 +08:00
wqy 44edeffad9 Fix build error 2024-01-13 15:02:22 +08:00
wqy 37f5210ab9 Build pyd 2024-01-13 11:38:52 +08:00
DingZQ 8e7b3ebc1f Add document for FastAPI 2023-11-11 09:47:09 +08:00
DingZQ 8e51c437c4 Refine 2023-10-27 21:25:49 +08:00
DingZQ d105557533 Refine 2023-10-27 21:25:06 +08:00
DingZQ 1a0bc30785 Refine 2023-10-27 21:17:38 +08:00
DingZQ 6f22063a72 Refine 2023-10-27 21:12:32 +08:00
DingZQ f29047f535 Refine 2023-10-27 21:10:15 +08:00
DingZQ 0151fb3424 Refine 2023-10-27 21:08:24 +08:00
DingZQ 855cf6758a Refine 2023-10-27 21:02:32 +08:00
DingZQ 34a7ad1a12 Refine 2023-10-27 20:59:01 +08:00
DingZQ 772aea84bd Refine 2023-10-27 20:57:40 +08:00
DingZQ ffb5dc7854 Refine 2023-10-27 20:55:06 +08:00
DingZQ 152fc9d71b Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2023-10-27 20:21:12 +08:00
DingZQ 1c7cd33f22 Refine 2023-10-27 19:59:41 +08:00
wqy cde86d4281 Fix sa error 2023-10-21 11:21:01 +08:00
wqy 94597a2125 Encode binary 2023-10-21 10:36:16 +08:00
wqy c925e2f1fa Remove ''' 2023-10-21 10:21:44 +08:00
wqy d996a364e5 Dump output as binary 2023-10-21 10:13:33 +08:00
wqy 788c6cd1bc Rename 2023-10-21 10:00:25 +08:00
wqy 664bad41fa Update setup 2023-10-21 09:59:37 +08:00
wqy 925382b379 Setup environment 2023-10-21 09:52:04 +08:00
DingZQ 157f14868b Refine 2023-10-21 09:09:03 +08:00
DingZQ a3157b270f Refine 2023-10-21 09:07:49 +08:00
DingZQ 12b12a9d5a Refine 2023-10-21 09:06:27 +08:00
DingZQ 41e959e4da Refine 2023-10-21 09:02:53 +08:00
DingZQ ff7b1c921d Refine 2023-10-21 09:01:58 +08:00
DingZQ a8dd94bf20 Refine 2023-10-21 08:59:10 +08:00
DingZQ 782ec758ee Refine 2023-10-21 08:54:35 +08:00
DingZQ adc432b6c1 Refine 2023-10-21 08:52:26 +08:00
DingZQ 540d726c11 Refine 2023-10-21 08:50:45 +08:00
DingZQ 6bb046e1ca Refine 2023-10-21 08:49:10 +08:00
DingZQ 122b5462a5 Refine 2023-10-21 08:43:17 +08:00
DingZQ bf04c6493f Refine 2023-10-21 00:14:22 +08:00
DingZQ 890f8d9cf6 Refine 2023-10-21 00:12:19 +08:00
DingZQ 86242b13d2 Refine 2023-10-21 00:10:07 +08:00
DingZQ d32a7a46d6 Refine 2023-10-21 00:03:45 +08:00
DingZQ 9fa761e1cd Refine 2023-10-21 00:00:42 +08:00
DingZQ a98a44b2a4 Refine 2023-10-20 23:59:01 +08:00
DingZQ 769ab7d77c Refine 2023-10-20 23:53:43 +08:00
DingZQ 5f23ea9fad Refine 2023-10-20 23:47:42 +08:00
DingZQ a088165017 Refine 2023-10-20 23:44:23 +08:00
DingZQ 734f394d25 Refine 2023-10-20 23:42:28 +08:00
DingZQ bb9471321d Refine 2023-10-20 23:40:07 +08:00
DingZQ 3ed7e5a8bf Refine 2023-10-20 23:37:57 +08:00
DingZQ 8ac8822563 Refine 2023-10-20 23:33:12 +08:00
DingZQ 48cdf8f0dd Check isSimulation value when try to run simulation 2023-10-14 16:21:43 +08:00
wqy 6c345a29dc Support unknown device type 2023-10-14 15:35:16 +08:00
wqy 0457492b21 Disable undo redo for scada device 2023-10-14 14:04:17 +08:00
DingZQ f016182562 UPdate 2023-09-20 00:19:55 +08:00
DingZQ d190a838bf Update 2023-09-20 00:13:46 +08:00
DingZQ 0de74cc892 Upate 2023-09-20 00:12:58 +08:00
DingZQ d2d2948285 Print setstatus result 2023-09-20 00:04:35 +08:00
DingZQ c5d8b3e993 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2023-09-19 23:58:31 +08:00
DingZQ 6f60d68cbb Change status to string 2023-09-19 23:58:24 +08:00
王琼钰 b45c1261f2 Accept Merge Request #321: (wqy/region -> master)
Merge Request: Fix slope calculation

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/321?initial=true
2023-09-16 15:48:33 +08:00
wqy cdb37db2a2 Fix slope calculation 2023-09-16 15:47:50 +08:00
王琼钰 b2eb17f87e Accept Merge Request #302: (wqy/region -> master)
Merge Request: Update script

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/302?initial=true
2023-07-28 20:13:23 +08:00
wqy 6c3e500eb3 Update script 2023-07-28 20:12:58 +08:00
王琼钰 3c8f092259 Accept Merge Request #301: (wqy/region -> master)
Merge Request: Switch metis to pymetis

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/301?initial=true
2023-07-28 19:53:45 +08:00
wqy a3f88c61fa Switch metis to pymetis 2023-07-28 19:36:38 +08:00
wqy 72b3884c28 Add lib numpy and pymetis 2023-07-28 19:35:37 +08:00
王琼钰 7efaf272c7 Accept Merge Request #300: (wqy/region -> master)
Merge Request: Refine service area

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/300?initial=true
2023-07-28 18:40:10 +08:00
wqy 897dd72a07 Refine service area 2023-07-28 18:38:42 +08:00
王琼钰 12953069f1 Accept Merge Request #297: (wqy/region -> master)
Merge Request: Support to get link on boundary

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/297?initial=true
2023-07-21 18:42:13 +08:00
wqy fb347ba03a Support to get link on boundary 2023-07-21 18:40:27 +08:00
wqy 4c6e257d1d Fix service area case 2023-06-27 20:51:09 +08:00
wqy 9d90643b3b Fix option test case 2023-06-27 20:15:08 +08:00
wqy 1604f98b95 Fix option test case 2023-06-27 20:05:15 +08:00
WQY\qiong 7f31baee3c Update default options 2023-06-17 13:06:32 +08:00
WQY\qiong daa6ecc82f Do not apply accurate calculation by default 2023-06-17 11:23:02 +08:00
WQY\qiong 6a35a41f51 Support "TOTAL DURATION" 2023-06-17 10:37:13 +08:00
DingZQ 80fc13b74e Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2023-06-12 23:06:20 +08:00
DingZQ b0ad14e567 Print arguments 2023-06-12 23:05:42 +08:00
王琼钰 5a96c24249 Accept Merge Request #266: (region -> master)
Merge Request: Improve boundary algorithm

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/266?initial=true
2023-06-09 21:24:59 +08:00
WQY\qiong a4f477f8eb Refine code 2023-06-09 21:24:16 +08:00
WQY\qiong 2b424e4710 Improve boundary algorithm 2023-06-09 09:36:11 +08:00
WQY\qiong 2c5ca5ef53 Add more temp table for calculation 2023-06-09 09:34:53 +08:00
WQY\qiong 83f4bc063b Remove unused import 2023-06-09 09:34:00 +08:00
WQY\qiong a631b36694 Fix net3 2023-06-09 08:58:12 +08:00
王琼钰 c55d9b0204 Accept Merge Request #261: (region -> master)
Merge Request: Enhance boundary algorithm

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/261?initial=true
2023-06-08 22:02:24 +08:00
WQY\qiong 2bd314cc91 Enhance boundary algorithm 2023-06-08 22:00:15 +08:00
DingZQ 54adc4dc15 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2023-06-08 21:34:23 +08:00
DingZQ 07d144abec Add legend data 2023-06-08 21:33:42 +08:00
王琼钰 05c34e33c0 Accept Merge Request #260: (region -> master)
Merge Request: Prevent infinite loop

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/260?initial=true
2023-06-08 21:26:07 +08:00
WQY\qiong 6dbf701318 Prevent infinite loop 2023-06-08 21:25:13 +08:00
王琼钰 491e70f79f Accept Merge Request #259: (inp -> master)
Merge Request: Do not write out new keys

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/259?initial=true
2023-06-03 05:40:29 +08:00
WQY\qiong 61afe5522b Do not write out new keys 2023-06-03 05:39:36 +08:00
DingZQ 905dd52198 Add version parameter for export inp 2023-06-02 23:39:57 +08:00
DingZQ 634f9da5e8 Update option methods to v3 2023-06-01 20:16:12 +08:00
DingZQ 217a59cdfe Refine resions when open project 2023-05-27 11:31:57 +08:00
DingZQ 6c9ef0efa2 Refine 2023-05-26 19:20:27 +08:00
DingZQ afeeed6403 Refine method adddistrictmeteringarea 2023-05-26 19:12:05 +08:00
DingZQ 1cf4c40274 Add inflate_delta to generate SA and generate VD 2023-05-25 14:38:10 +08:00
DingZQ 88f022d6df Add inflate_delta to generate dma method 2023-05-25 14:35:12 +08:00
DingZQ 62c6ee1349 Refine 2023-05-21 00:38:10 +08:00
DingZQ eb07ab775c Refine 2023-05-19 23:54:32 +08:00
DingZQ 0e056e9507 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2023-05-19 23:51:05 +08:00
DingZQ dda6b45170 Refine 2023-05-19 23:44:20 +08:00
王琼钰 4469501010 Accept Merge Request #245: (region -> master)
Merge Request: Support inflate delta

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/245?initial=true
2023-05-19 22:59:57 +08:00
WQY\qiong 99c82fcf15 Support inflate delta 2023-05-19 22:59:25 +08:00
DingZQ 636d462ce3 Refine 2023-05-19 21:07:43 +08:00
王琼钰 8542b7b75d Accept Merge Request #244: (region -> master)
Merge Request: Support SA and VD

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/244?initial=true
2023-05-19 18:32:50 +08:00
WQY\qiong 4e14b9398d Support VD 2023-05-17 21:12:05 +08:00
WQY\qiong 2cab278c9f Fix generate sa 2023-05-17 21:11:41 +08:00
WQY\qiong 7218fc5db7 Rename vd file 2023-05-16 21:36:58 +08:00
WQY\qiong 6046cdf01a Support SA 2023-05-16 21:30:23 +08:00
DingZQ 76f3021d46 Refine 2023-05-15 23:39:32 +08:00
DingZQ 2dab018ec5 Refine 2023-05-15 10:52:52 +08:00
DingZQ ee56ad7410 Refine 2023-05-15 10:48:05 +08:00
DingZQ 19ed81e2b5 Refine 2023-05-15 10:26:17 +08:00
DingZQ 22e8798bd2 Refine 2023-05-15 09:53:05 +08:00
DingZQ 4f312a9ff2 Refine 2023-05-14 23:47:18 +08:00
DingZQ ee8178919b Refine 2023-05-14 23:45:35 +08:00
DingZQ 91976172a5 Add more api 2023-05-14 23:39:14 +08:00
DingZQ c17629ee28 Add DMA api 2023-05-14 23:30:44 +08:00
WQY\qiong 47d2fe9ddd Supplement new interface 2023-05-14 13:34:48 +08:00
WQY\qiong 9240708d04 Rename file 2023-05-14 13:32:18 +08:00
王琼钰 b00c870a89 Accept Merge Request #238: (region -> master)
Merge Request: Support DMA

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/238?initial=true
2023-05-14 13:21:28 +08:00
WQY\qiong 58fd285d25 Support DMA 2023-05-14 13:20:54 +08:00
WQY\qiong 1bc73f5bd5 Relax dma parent constraint 2023-05-14 13:12:34 +08:00
WQY\qiong 3247794d5b Support region batch command 2023-05-14 10:31:49 +08:00
WQY\qiong 28cce528e7 Clean code 2023-05-14 08:55:47 +08:00
WQY\qiong 16cb5ccb82 Update script 2023-05-14 08:55:10 +08:00
WQY\qiong 48f4e58029 Add new api, new implementation 2023-05-13 20:06:55 +08:00
WQY\qiong ee3100a61d Clean 2023-05-13 19:12:07 +08:00
WQY\qiong 553dad1d2c Add nodes for generated region 2023-05-13 18:09:17 +08:00
WQY\qiong a4ea72fffa Add wda python file 2023-05-13 18:02:29 +08:00
WQY\qiong 48ecdf7285 Rename file 2023-05-13 18:01:46 +08:00
WQY\qiong b06d8260ff Add script item to create/drop sub region table 2023-05-13 18:01:19 +08:00
WQY\qiong 6d30bf7d70 Add table for sub region type 2023-05-13 18:00:52 +08:00
WQY\qiong 99775af649 Fix sql ddl 2023-05-13 17:59:01 +08:00
WQY\qiong b425881202 Merge branch 'master' into region 2023-05-13 17:28:23 +08:00
王琼钰 4b72a4855d Accept Merge Request #236: (type -> master)
Merge Request: Refine type

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/236?initial=true
2023-05-13 10:41:44 +08:00
WQY\qiong d5a2e006a1 Refine type 2023-05-13 10:41:05 +08:00
DingZQ 3114a0a92e Refine 2023-05-13 09:45:35 +08:00
DingZQ 0ed1bb6af2 Refine 2023-05-13 09:39:39 +08:00
DingZQ b4f3aa2c1b Refine 2023-05-13 09:34:05 +08:00
WQY\qiong 7054dd5457 Add more api for dma 2023-05-13 08:37:05 +08:00
WQY\qiong e6ec0018a0 Support calculate_demand_to_network 2023-05-12 19:47:19 +08:00
DingZQ 69c5bbcb71 Refine 2023-05-11 22:20:41 +08:00
DingZQ 594a6b97f4 Refine 2023-05-11 22:16:51 +08:00
DingZQ eacc3f247b Refine 2023-05-11 22:15:35 +08:00
DingZQ a9c8f3c04c Refine 2023-05-11 22:14:25 +08:00
DingZQ 40f40f7a05 Refine 2023-05-11 22:12:43 +08:00
DingZQ 7679e3839d Refine 2023-05-11 22:10:39 +08:00
DingZQ 1d2a53e77d Refine 2023-05-11 22:06:50 +08:00
DingZQ ad21941239 Refine 2023-05-11 22:05:11 +08:00
DingZQ e587f8cf11 Add scada_db 2023-05-11 21:57:25 +08:00
DingZQ 95078b23ec Refine 2023-05-11 18:34:23 +08:00
DingZQ 6c446afe88 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2023-05-11 18:31:56 +08:00
DingZQ ca9e983911 Refine 2023-05-11 18:31:10 +08:00
WQY\qiong b75a717f95 Add more dma interface 2023-05-10 20:55:11 +08:00
WQY\qiong 49a9865eb2 Add dma convenient interface, no implementation 2023-05-10 20:49:34 +08:00
WQY\qiong 28ea0e3778 Add dma interface, no implementation 2023-05-10 20:42:05 +08:00
王琼钰 b3bdb03ab5 Accept Merge Request #232: (region -> master)
Merge Request: Calculate distribution

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/232?initial=true
2023-05-10 20:35:29 +08:00
WQY\qiong 6794d02ee0 Clean code 2023-05-10 20:34:00 +08:00
DingZQ b8df831d71 Refine 2023-05-10 07:22:29 +08:00
DingZQ c3898576e7 Add API for extension data 2023-05-10 07:11:14 +08:00
WQY\qiong 74b667c7ca Format 2023-05-08 21:09:05 +08:00
WQY\qiong 32c47bb2f2 Support calculate distribution 2023-05-08 20:59:54 +08:00
王琼钰 3d5bbe9e6f Accept Merge Request #231: (region -> master)
Merge Request: Support extension data

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/231?initial=true
2023-05-08 20:46:52 +08:00
WQY\qiong 132eb036fb Support extension data 2023-05-08 20:46:10 +08:00
WQY\qiong 22f548beb9 Format tjnetwork 2023-05-07 12:02:48 +08:00
DingZQ eda4c79cfb Refine 2023-05-07 11:06:04 +08:00
DingZQ a44f3cd18c Refine 2023-05-07 11:03:23 +08:00
DingZQ 27945c3e88 Refine 2023-05-07 10:28:59 +08:00
DingZQ 52d98063a4 Refine 2023-05-07 10:19:07 +08:00
DingZQ 64dcc8b800 Refine 2023-05-07 10:18:11 +08:00
DingZQ f5afb5fb6c Refine 2023-05-07 10:01:19 +08:00
DingZQ 440104d028 Add method getallscadadevices 2023-05-07 09:38:44 +08:00
DingZQ 6dea276464 Add method to get all vertex links and vertices 2023-05-07 09:22:22 +08:00
DingZQ 948f5de767 Update get_all_device_ids 2023-05-06 20:24:29 +08:00
DingZQ 7b736f9812 Refine 2023-05-05 21:34:34 +08:00
王琼钰 126e05cd59 Accept Merge Request #223: (api -> master)
Merge Request: Replace all "numeric" with "float8"

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/223?initial=true
2023-05-05 21:19:29 +08:00
WQY\qiong adeb9dd031 Replace all "numeric" with "float8" 2023-05-05 21:18:56 +08:00
DingZQ 4939312756 Refine 2023-05-05 20:55:51 +08:00
DingZQ 24ce579a7f Refine 2023-05-05 20:53:29 +08:00
DingZQ 91fd53ed23 Refine 2023-05-04 20:33:36 +08:00
王琼钰 3a8bb84617 Accept Merge Request #222: (api -> master)
Merge Request: Fix get scada element

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/222?initial=true
2023-05-02 23:05:36 +08:00
WQY\qiong 62b086d939 Fix get scada element 2023-05-02 23:04:54 +08:00
DingZQ 4ca41aed33 Refine 2023-05-02 22:56:07 +08:00
DingZQ 52ff615f4f Refine 2023-05-02 22:53:02 +08:00
DingZQ ec6c238cf0 Refine 2023-05-02 22:33:42 +08:00
DingZQ 050325beda Refine 2023-05-02 22:24:06 +08:00
DingZQ b64605777e Refine 2023-05-02 22:23:03 +08:00
DingZQ 0398b3a954 Refine 2023-05-02 22:21:35 +08:00
DingZQ 19512339ac Refine 2023-05-02 22:21:02 +08:00
DingZQ 8bceb014c3 Refine 2023-05-02 22:17:58 +08:00
DingZQ 19cfd21e81 Refine export inp, add vertice and scada 2023-05-02 22:17:40 +08:00
DingZQ 9626473b76 Refine 2023-05-02 20:18:05 +08:00
DingZQ 068281b929 Add region APIs 2023-05-02 20:16:03 +08:00
WQY\qiong 599e221712 Clean code 2023-05-02 18:30:24 +08:00
WQY\qiong abcc450781 Support fetch all keys and data 2023-05-02 18:19:13 +08:00
王琼钰 96e61224f1 Accept Merge Request #221: (region -> master)
Merge Request: Support region

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/221?initial=true
2023-05-02 17:48:41 +08:00
WQY\qiong 8a1a49bf6b Add test for UTF8 2023-05-02 17:47:50 +08:00
WQY\qiong 06c8f366fd Add test for dma 2023-05-01 00:08:56 +08:00
WQY\qiong 98b6354165 Support dma calculation 2023-04-30 23:58:22 +08:00
WQY\qiong 2abf861584 Add more info to topology 2023-04-30 23:57:55 +08:00
WQY\qiong 703a2db724 Add metis wrapper lib 2023-04-30 23:57:25 +08:00
WQY\qiong 7abae71351 Prevent hardcode enum 2023-04-30 22:09:38 +08:00
WQY\qiong 7a03c0aa09 Add test for service area 2023-04-30 18:46:49 +08:00
WQY\qiong 7a1a8a3a7b Support service area calculation 2023-04-30 18:43:44 +08:00
WQY\qiong 0656dbe83b Clean code 2023-04-30 18:32:05 +08:00
WQY\qiong 23cb1fe0e8 Dump inp can not open an opening project 2023-04-30 18:00:23 +08:00
WQY\qiong ea60c36b4e Fix angle 2023-04-30 17:56:55 +08:00
WQY\qiong 9b53f7b51f Clean virtual district 2023-04-30 17:44:18 +08:00
WQY\qiong b3a630c5e1 Fix test case name 2023-04-30 14:08:29 +08:00
WQY\qiong e30c81a891 Support water distribution 2023-04-30 14:04:07 +08:00
WQY\qiong 45296960e7 Take care dropping table 2023-04-30 12:23:04 +08:00
WQY\qiong 8d49db2942 Add more test 2023-04-30 12:04:52 +08:00
WQY\qiong a4dc2d62cc Add test for new utils 2023-04-30 11:31:14 +08:00
WQY\qiong 493b46fd4e Refine virtual district 2023-04-30 11:30:47 +08:00
WQY\qiong 93ec527fff Add more algorithms, such as path, inflate 2023-04-30 11:30:33 +08:00
WQY\qiong e033a3c3c7 Add temp link for region calculation 2023-04-30 11:29:31 +08:00
WQY\qiong c4b01fc48c Check if node has coordinate 2023-04-30 11:28:57 +08:00
WQY\qiong 5376ead768 Add more interface to base 2023-04-30 11:28:40 +08:00
WQY\qiong 3c233f6e9c Add clipper2 c wrapper lib 2023-04-30 11:28:17 +08:00
WQY\qiong 04a3d0057e Support more region utils, such as convex hull 2023-04-29 19:15:34 +08:00
WQY\qiong 538284f502 Add more checking for region api 2023-04-29 17:58:29 +08:00
WQY\qiong d66087225f Support database region 2023-04-29 17:51:34 +08:00
WQY\qiong a3e8f693d9 Add test for virtual district 2023-04-29 17:21:29 +08:00
WQY\qiong e407f8ccd7 Clean code 2023-04-29 16:40:20 +08:00
WQY\qiong 2cff3d0a2e Clean code 2023-04-29 16:36:36 +08:00
WQY\qiong a15ccecaa7 Clean code 2023-04-29 16:30:59 +08:00
WQY\qiong 4b452c5f0a Clean code 2023-04-29 15:42:01 +08:00
WQY\qiong 35d2ce08e1 Clean code 2023-04-29 15:01:11 +08:00
WQY\qiong a0166193ef Refactor vd 2023-04-29 14:27:25 +08:00
WQY\qiong a278335bbe Refactor coord 2023-04-29 14:26:49 +08:00
WQY\qiong 12150ef57e Add general region table 2023-04-29 14:25:43 +08:00
WQY\qiong c6518cbae1 Code refactor 2023-04-29 11:18:55 +08:00
WQY\qiong a62700d27e Support to calculate virtual district 2023-04-29 11:11:05 +08:00
WQY\qiong 0ac4a88295 Replace coordinate with postgis geometry 2023-04-29 10:09:12 +08:00
WQY\qiong 089a12e137 Merge branch 'master' into region 2023-04-29 09:28:04 +08:00
DingZQ 23cc2b2df5 Refine 2023-04-25 23:23:22 +08:00
DingZQ c3cd69010b Update 2023-04-25 23:16:24 +08:00
DingZQ 3808d72e32 Refine 2023-04-18 23:31:43 +08:00
DingZQ 6eb4b34de0 Refine 2023-04-18 23:29:40 +08:00
WQY\qiong 83b7177612 Add test for case sensitive database 2023-04-07 23:26:42 +08:00
WQY\qiong 83873b38f3 Drop need case sensitive 2023-04-07 23:24:44 +08:00
WQY\qiong d315dc736c Allow case sensitive 2023-04-07 23:16:49 +08:00
WQY\qiong e320148e99 Remove debug info 2023-04-07 23:03:03 +08:00
DingZQ 8b02b13e7e Refine 2023-04-07 22:46:57 +08:00
DingZQ a5cd90e3f2 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2023-04-07 22:41:06 +08:00
DingZQ 29c70d2a48 Refine 2023-04-07 22:39:42 +08:00
WQY\qiong c0a645ce24 debug 2023-04-07 22:29:28 +08:00
WQY\qiong e4f71b01cf Guard open project 2023-04-07 22:24:38 +08:00
WQY\qiong e59dac81ec Refine project operation 2023-04-07 21:57:44 +08:00
WQY\qiong da80d689ed Refine chain inp 2023-04-07 19:22:42 +08:00
Joey Wang a83b5bf994 Update after_chain.inp 2023-04-07 18:58:23 +08:00
Joey Wang 6976aca0f2 Add after_chain.inp 2023-04-07 18:51:57 +08:00
Joey Wang 3b0f7cfc98 Add before_chain.inp 2023-04-07 18:39:16 +08:00
Joey Wang 1dcfa4f5be Relax valve inp in limitation 2023-04-07 18:38:55 +08:00
Joey Wang f910b07a41 Add script to dump inp 2023-04-07 18:38:33 +08:00
WQY\qiong 87d3f3688a Merge branch 'master' into region 2023-04-06 23:50:59 +08:00
WQY\qiong 450fccee51 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2023-04-06 23:16:06 +08:00
WQY\qiong bfeaa9f8c0 Fix list snapshot 2023-04-06 23:15:47 +08:00
DingZQ 098ba98a7c Refine 2023-04-06 23:14:05 +08:00
DingZQ 8d15df4a3b Print tags 2023-04-06 22:26:01 +08:00
DingZQ 9d6b685439 Add get_tags 2023-04-06 22:16:05 +08:00
WQY\qiong 02b5ef71f2 Add scada model type to scada element 2023-04-06 00:50:44 +08:00
王琼钰 2e4ae1bd9f Accept Merge Request #204: (get_tags -> master)
Merge Request: Support to get all tags

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/204?initial=true
2023-04-05 23:41:45 +08:00
WQY\qiong b0c90fcc07 Support to get all tags 2023-04-05 23:39:07 +08:00
WQY\qiong e57721a29a Add extension back 2023-03-31 15:18:46 +08:00
WQY\qiong dcecce165e Merge branch 'master' into region 2023-03-31 15:13:30 +08:00
WQY\qiong 5f64439f9f Enhance epa3 junction inp in 2023-03-31 15:12:29 +08:00
WQY\qiong 453f3cbfe7 Fix print info 2023-03-31 15:10:04 +08:00
WQY\qiong 900e0cd0ba Merge branch 'master' into region 2023-03-31 15:00:30 +08:00
WQY\qiong a579272ea9 Support batch operation table 2023-03-31 14:51:49 +08:00
WQY\qiong 8de01a3e02 Add test case for batch commands 2023-03-31 13:19:09 +08:00
WQY\qiong 71f7dbc233 Refine test case 2023-03-31 13:02:59 +08:00
WQY\qiong 1f2733aa90 Add info when restore 2023-03-31 10:48:11 +08:00
WQY\qiong f13d32bb0a Open and close when restore 2023-03-31 10:46:51 +08:00
WQY\qiong 610e7ee561 Add api and script to restore 2023-03-31 10:44:15 +08:00
WQY\qiong 924dbc802c Add more test cases for restore 2023-03-31 10:27:21 +08:00
WQY\qiong 976361a3bd Rename script 2023-03-31 10:00:06 +08:00
WQY\qiong 68f30cf171 Move script out to create template 2023-03-31 09:57:22 +08:00
WQY\qiong e0b6ce7559 Start region work 2023-03-31 09:55:06 +08:00
王琼钰 43043556dd Accept Merge Request #203: (region -> master)
Merge Request: Add script to delete project

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/203?initial=true
2023-03-30 08:54:07 +08:00
WQY\qiong 55e52ff4fc Add script to delete project 2023-03-30 08:53:31 +08:00
DingZQ f6e7d88799 Add methods to get scada elemnets and scada devices 2023-03-29 23:36:42 +08:00
王琼钰 836df97865 Accept Merge Request #202: (region -> master)
Merge Request: Fix test case

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/202?initial=true
2023-03-29 22:46:28 +08:00
WQY\qiong 8c70af523d Fix test case 2023-03-29 22:46:04 +08:00
王琼钰 e6eb137077 Accept Merge Request #201: (region -> master)
Merge Request: Get all scada devices

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/201?initial=true
2023-03-29 22:42:15 +08:00
WQY\qiong 6287dcd1fe Get all scada devices 2023-03-29 22:41:51 +08:00
王琼钰 9e608dffe6 Accept Merge Request #200: (region -> master)
Merge Request: Get all scada elements

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/200?initial=true
2023-03-29 22:34:59 +08:00
WQY\qiong 0f1d2d5435 Get all scada elements 2023-03-29 22:34:28 +08:00
WQY\qiong 7451cf9ddd Need insert since it requires update later 2023-03-24 21:17:22 +08:00
WQY\qiong f44876427d Do not insert coordinate when get 2023-03-24 20:56:51 +08:00
WQY\qiong 069e9ac4c5 Add default coordinate 2023-03-24 20:54:16 +08:00
WQY\qiong 7446f89a34 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2023-03-22 22:22:32 +08:00
WQY\qiong e3a7893d29 Simulation is v2 2023-03-22 22:22:13 +08:00
王琼钰 dd31a04cdf Accept Merge Request #189: (guard -> master)
Merge Request: Guard programming

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/189?initial=true
2023-03-22 22:11:02 +08:00
WQY\qiong bcdaddc5cf Add complex test case 2023-03-22 21:55:58 +08:00
WQY\qiong c6d74e3cad Guard coord 2023-03-22 21:19:37 +08:00
WQY\qiong 19a5b860b6 Add curve test 2023-03-22 21:17:46 +08:00
WQY\qiong 4c4a03fcb0 Guard pattern and curve 2023-03-22 21:16:50 +08:00
WQY\qiong 7a2a52b920 Guard mixing 2023-03-22 21:10:36 +08:00
WQY\qiong 26b7f089fd Guard tag 2023-03-22 21:06:14 +08:00
WQY\qiong 39e2316631 Guard valve 2023-03-22 21:02:56 +08:00
WQY\qiong da796f0c7f Guard pump 2023-03-22 21:00:58 +08:00
WQY\qiong 6a304ba7e0 Guard pipe 2023-03-22 20:58:45 +08:00
WQY\qiong 236fcf0612 Guard tank 2023-03-22 20:55:15 +08:00
WQY\qiong 3afb24c816 Guard reservoir 2023-03-22 20:52:47 +08:00
WQY\qiong e68154ea0b Guard junction 2023-03-22 20:47:16 +08:00
WQY\qiong 5e69ada90b Support script to create v3 project 2023-03-22 20:18:16 +08:00
WQY\qiong b9aa6c3e71 Read version 2 by default in script 2023-03-22 20:10:50 +08:00
WQY\qiong b49b916ff8 More .gitignore 2023-03-22 20:04:47 +08:00
WQY\qiong 34750b729d Delete build_db 2023-03-22 20:04:01 +08:00
王琼钰 9d2d7ea51f Accept Merge Request #188: (script -> master)
Merge Request: Add some python scripts

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/188?initial=true
2023-03-22 19:59:09 +08:00
WQY\qiong a59bb224ba Add dev script for client debug 2023-03-22 19:58:31 +08:00
WQY\qiong 7bcba430ce Support script to create project 2023-03-22 19:58:14 +08:00
WQY\qiong 4366125647 Set restore op to current 2023-03-22 19:53:26 +08:00
王琼钰 77a73f5532 Accept Merge Request #187: (script -> master)
Merge Request: Add script to copy project

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/187?initial=true
2023-03-22 19:41:13 +08:00
WQY\qiong cd39018cbd Add script to copy project 2023-03-22 19:40:50 +08:00
王琼钰 00c0a63720 Accept Merge Request #186: (script -> master)
Merge Request: Add python file to clean projects

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/186?initial=true
2023-03-22 19:27:23 +08:00
WQY\qiong 15536a2113 Add python file to clean projects 2023-03-22 19:27:06 +08:00
王琼钰 d32c6e9fdb Accept Merge Request #185: (script -> master)
Merge Request: Support to clean all projects

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/185?initial=true
2023-03-22 19:25:28 +08:00
WQY\qiong d8dff0118b Support to clean all projects 2023-03-22 19:24:50 +08:00
王琼钰 f2bd39a35a Accept Merge Request #184: (parse -> master)
Merge Request: Optimize read inp

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/184?initial=true
2023-03-22 18:53:57 +08:00
WQY\qiong 7727591667 Reset simulation to v2 2023-03-22 18:52:46 +08:00
Joey Wang 6f9d45d982 Run inp should be same with run project 2023-03-22 13:48:59 +08:00
Joey Wang b9c8856528 Rename since we support epa3 2023-03-22 13:40:47 +08:00
Joey Wang c93298a1b2 Pay attention to epa3 simulation output format 2023-03-22 13:39:43 +08:00
Joey Wang 9a65990278 Support run epa3 2023-03-22 13:36:57 +08:00
Joey Wang a73cd3deba Support inp out v3 2023-03-22 13:32:43 +08:00
Joey Wang cf302487d3 Replace section name with pre-defined one 2023-03-22 13:27:47 +08:00
Joey Wang 9c2e314416 Check input version 2023-03-22 13:26:35 +08:00
Joey Wang a8278e50dd Sync code 2023-03-22 13:14:39 +08:00
Joey Wang fe0819654b Clean curve v3 old inp in 2023-03-22 13:01:31 +08:00
Joey Wang 0db93b0637 Clean and support v3 inp in 2023-03-22 13:00:16 +08:00
Joey Wang faec3cef73 Support option v3 inp in 2023-03-22 12:26:54 +08:00
Joey Wang e18c504067 Support option v3 2023-03-22 12:19:43 +08:00
Joey Wang 0fc34ae1ff Comment out pattern and curve v3 version 2023-03-22 12:18:07 +08:00
WQY\qiong 398ba09106 Start to support version read/write 2023-03-21 21:29:33 +08:00
WQY\qiong 6ed7e00e03 Build all database 2023-03-21 21:19:09 +08:00
WQY\qiong 9f9d3227b9 Remove old inp in routine 2023-03-21 21:18:53 +08:00
WQY\qiong 8681a56ed7 Replace inp in file 2023-03-21 21:08:20 +08:00
DingZQ 3a8a6d0ea3 Change deviceid to id 2023-03-21 20:44:19 +08:00
DingZQ 64521182ca Fxied name error 2023-03-21 20:14:39 +08:00
DingZQ da4afe4a92 Add SCADA API 2023-03-21 19:55:30 +08:00
WQY\qiong def44d80b3 Clean inp in new 2023-03-16 19:00:08 +08:00
WQY\qiong 977e706a05 Add any sql to batch 2023-03-16 08:30:32 +08:00
WQY\qiong 04a58bb864 Use sql batch to optimize 2023-03-16 08:30:04 +08:00
WQY\qiong 1d2ac09c92 Print time 2023-03-16 00:39:33 +08:00
WQY\qiong 1d720c5b6c Support inp in coord, vertex, label 2023-03-16 00:15:34 +08:00
WQY\qiong bf1aeff1fa Support inp in quality, source, reaction, mixing 2023-03-16 00:02:11 +08:00
WQY\qiong b35fccad49 Support inp in energy, emitter 2023-03-15 23:47:06 +08:00
WQY\qiong f79a9cdae8 Support inp in tag, demand, status 2023-03-15 22:34:38 +08:00
WQY\qiong 617c66d0cd Support inp in link new 2023-03-15 22:04:39 +08:00
WQY\qiong 81c4e0308b Replace set with list 2023-03-15 21:47:46 +08:00
WQY\qiong 4e6864b2fc Remove duplicated checking 2023-03-15 21:45:12 +08:00
WQY\qiong 2f87d87233 Specialize pattern and curve 2023-03-15 21:29:33 +08:00
WQY\qiong ed6f6daca0 Refine inp in 2023-03-15 20:03:55 +08:00
WQY\qiong 24a39aeca6 Fix option pattern reading 2023-03-10 19:57:52 +08:00
WQY\qiong 696bd55ead Optimize control and rule 2023-03-10 19:57:20 +08:00
DingZQ 5eb414d5e7 Refine unlock project 2023-03-10 16:46:25 +08:00
Joey Wang b3db5bd027 Add level for section 2023-03-10 09:23:27 +08:00
WQY\qiong 1fda14c3cf Optimize read performance 2023-03-10 00:13:17 +08:00
WQY\qiong e41abe362f Second pass scan 2023-03-09 23:24:41 +08:00
WQY\qiong 55febbe163 Remove redundant char 2023-03-09 22:56:43 +08:00
WQY\qiong 96cb99b87a First pass scan 2023-03-09 22:44:13 +08:00
Joey Wang 369e636e13 Start new parser 2023-03-09 13:38:35 +08:00
Joey Wang a2e4649be5 Reduce dependency 2023-03-09 12:55:08 +08:00
Joey Wang d2d27c2ce0 Split inp in & out routine 2023-03-09 09:04:34 +08:00
Joey Wang 5e6b3d02df Rename parser to inp_io 2023-03-09 08:47:27 +08:00
王琼钰 28b2449487 Accept Merge Request #178: (api -> master)
Merge Request: Big change...

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/178?initial=true
2023-03-09 00:36:07 +08:00
WQY\qiong 41f7978788 Update test_option_op case 2023-03-09 00:28:28 +08:00
WQY\qiong e06506d5dd Update test_option case 2023-03-09 00:03:07 +08:00
WQY\qiong 0e1522f8e6 Fix demand model converter 2023-03-09 00:01:48 +08:00
WQY\qiong 402451eb71 Keep default same for option v2 & v3 2023-03-09 00:01:21 +08:00
WQY\qiong a9008b3a86 Fix typo 2023-03-08 23:28:36 +08:00
WQY\qiong 6fa23ac253 Add default value for demand pattern 2023-03-08 23:28:24 +08:00
WQY\qiong 1e51a52e1a Only write out non-empty value 2023-03-08 23:16:07 +08:00
WQY\qiong 6babe1e84c Make use of enum string 2023-03-08 23:09:29 +08:00
WQY\qiong 6a9b4ec87c Add operation and type since set option is batch command 2023-03-08 23:04:20 +08:00
WQY\qiong 9d5db44b22 Remove some checking 2023-03-08 22:59:52 +08:00
WQY\qiong 690e3aa09a Refine option to support batch command 2023-03-08 22:56:29 +08:00
WQY\qiong 3c3c29de90 Code refactor for option 2023-03-08 22:43:21 +08:00
WQY\qiong 7c9eb1555a Refine set option to sync v2 & v3 2023-03-08 22:18:28 +08:00
DingZQ b20dc6ab8a Use gzip middleware 2023-03-08 17:20:15 +08:00
WQY\qiong ca1a065d54 Support to convert between option v2 & v3 2023-03-07 21:16:30 +08:00
王琼钰 11a3cb7a78 Accept Merge Request #171: (list-snapshot -> master)
Merge Request: Support to list snapshot

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/171?initial=true
2023-03-03 20:40:38 +08:00
WQY\qiong dde045b6b8 Support to list snapshot 2023-03-03 20:40:05 +08:00
WQY\qiong 04986a1363 Format enum 2023-03-03 20:22:47 +08:00
WQY\qiong 998e81aed3 Sync to v3 for v2 2023-03-03 20:16:48 +08:00
WQY\qiong 5ad42429e5 Add EPANET3 option api 2023-03-03 20:15:49 +08:00
WQY\qiong c813c3d6fb Implement option v3 in 2023-03-03 19:51:21 +08:00
WQY\qiong 9ad47d27cc Refine option v2 2023-03-03 19:50:49 +08:00
WQY\qiong ece98ae07c Support EPANET3 [PATTERNS] out 2023-03-03 19:06:56 +08:00
WQY\qiong e9fa51cd4e Support EPANET3 [CURVES] out 2023-03-03 19:01:50 +08:00
Joey Wang 7d47dcd1b6 Prepare EPANET3 option api 2023-03-03 13:37:59 +08:00
Joey Wang 6b1c5a8269 Format option 2023-03-03 13:36:52 +08:00
Joey Wang 71976f42fb Split [CURVES] in/out 2023-03-03 13:36:17 +08:00
Joey Wang 6951ffa7c7 Split [PATTERNS] in/out 2023-03-03 13:33:52 +08:00
WQY\qiong 3f3b2508b1 Rename options v3 table 2023-03-02 22:39:47 +08:00
WQY\qiong 1e300ae258 Add EPANET3 option table 2023-03-02 22:33:09 +08:00
王琼钰 7f23208022 Accept Merge Request #167: (fix-snapshot -> master)
Merge Request: Fix snapshot api

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/167?initial=true
2023-03-02 21:33:14 +08:00
WQY\qiong 43e0f8189a Try query 2023-03-02 21:32:22 +08:00
WQY\qiong b93fcb22e9 EPANET3 support EPANET2 [TIMES] keyword 2023-03-02 20:53:27 +08:00
WQY\qiong 866019e6cb Do not support EPANET3 [REPORT] TRIALS 2023-03-02 20:39:29 +08:00
WQY\qiong 0344e262b4 Fix typo 2023-03-02 20:34:04 +08:00
WQY\qiong 1a050eb9c0 Add EPANET3 format 2023-03-02 20:31:12 +08:00
WQY\qiong 0ecc706000 Add EPANET3 format 2023-03-02 20:28:47 +08:00
WQY\qiong a6627c0e34 Allow to read EPANET3 pattern 2023-03-02 20:19:59 +08:00
WQY\qiong 406d88a6b8 Allow to read EPANET3 curve 2023-03-02 20:01:51 +08:00
WQY\qiong 2f395986c3 Add EPANET3 format 2023-03-02 19:51:24 +08:00
WQY\qiong 844e86b34d Add EPANET3 format 2023-03-02 19:43:10 +08:00
Joey Wang b2431d4ed9 Add EPANET3 format 2023-03-02 19:20:20 +08:00
Joey Wang d514439bd2 Relax tank read limitation 2023-03-02 13:28:04 +08:00
Joey Wang 013a86ac3a Remove redundant comment 2023-03-02 12:55:47 +08:00
WQY\qiong fa79522054 Supplement options 2023-03-01 23:56:09 +08:00
WQY\qiong 3665e15c92 Setting of valve [GPV] should be string 2023-03-01 23:20:04 +08:00
WQY\qiong 628258ef52 Drop demand & pattern from junction 2023-03-01 22:59:40 +08:00
王琼钰 bb3cd2bb7f Accept Merge Request #166: (api -> master)
Merge Request: Add EPANET2 output format

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/166?initial=true
2023-03-01 22:06:15 +08:00
WQY\qiong e83d3a83a5 Add EPANET2 output format 2023-03-01 22:05:34 +08:00
WQY\qiong cf76427562 Add placeholder for tank 2023-03-01 22:04:27 +08:00
DingZQ fe04ccd9d0 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2023-03-01 21:35:38 +08:00
DingZQ de48af215f Refine unlock 2023-03-01 21:35:31 +08:00
WQY\qiong 0fd75701f0 Add placeholder for tank 2023-03-01 21:20:46 +08:00
WQY\qiong 1f85a52378 Remove deprecated keyword 2023-02-28 23:10:48 +08:00
WQY\qiong ecbd4134fb Ignore TODO.md 2023-02-28 23:06:58 +08:00
王琼钰 69c25bb008 Accept Merge Request #165: (api -> master)
Merge Request: Add EPANET2 input format

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/165?initial=true
2023-02-28 23:05:26 +08:00
WQY\qiong ee829c0731 Add EPANET2 input format 2023-02-28 23:04:46 +08:00
DingZQ 2f66ae4dff Change operationid to operation 2023-02-27 23:31:36 +08:00
DingZQ 2e5705e2f2 Add method for tag and operation 2023-02-27 23:17:35 +08:00
DingZQ 0e8388859a Add method fastapi_set_restore_operation 2023-02-27 22:41:38 +08:00
DingZQ 87c9790d3b Refine 2023-02-24 22:48:09 +08:00
DingZQ 98cf6277a8 Refine 2023-02-24 22:44:31 +08:00
DingZQ 9a12091691 Refine 2023-02-24 22:42:08 +08:00
DingZQ 2abc190a65 Refine 2023-02-24 22:41:12 +08:00
DingZQ 0b9ccf5d5c Refine 2023-02-24 22:40:13 +08:00
DingZQ b35a0865b9 Return empty if cna't be locked 2023-02-24 22:36:02 +08:00
DingZQ 6b956e6c09 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2023-02-24 20:52:09 +08:00
DingZQ ed23f119f9 Update exportinp 2023-02-24 20:49:41 +08:00
WQY\qiong 97c409535f Add epanet3 file 2023-02-24 18:11:14 +08:00
王琼钰 3f7bfb6554 Accept Merge Request #164: (api -> master)
Merge Request: Refine scada api

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/164?initial=true
2023-02-24 18:08:33 +08:00
WQY\qiong 7aae885ef1 Add epanet version 2 2023-02-24 15:38:20 +08:00
WQY\qiong 1e5cfbe595 Test new scada api 2023-02-24 15:23:41 +08:00
WQY\qiong 0fb621112a Optimize scada sql 2023-02-24 15:22:38 +08:00
WQY\qiong aac46ac056 Fix scada device update api 2023-02-24 14:43:02 +08:00
WQY\qiong 6645c7341a Update build_db script 2023-02-24 14:05:02 +08:00
WQY\qiong db8329aa70 Support scada batch operation 2023-02-24 14:02:59 +08:00
WQY\qiong fd03105614 Refine scada api 2023-02-24 13:47:59 +08:00
WQY\qiong cef631b41b Refine scada tables 2023-02-24 13:47:24 +08:00
WQY\qiong c3ae9b306f Fix snapshot api and test 2023-02-24 13:46:19 +08:00
DingZQ c5c526286b Set response type to plaintext 2023-02-19 21:46:17 +08:00
DingZQ 39dfe2bd98 Refine import inp 2023-02-19 21:34:37 +08:00
DingZQ 2199f54428 Add fastapi for import inp and export inp 2023-02-19 21:24:49 +08:00
DingZQ 26ca2eba02 不能通过IP + Port来定位 client,port每个request都会变化 2023-02-19 17:59:32 +08:00
DingZQ 52c0e8d624 Fixed error of LockPorject 2023-02-19 17:56:13 +08:00
DingZQ 127a6ed606 Add method islockedbyme 2023-02-19 17:46:47 +08:00
DingZQ ebe58aef14 Use ip + port to identify the client 2023-02-19 17:31:47 +08:00
DingZQ 064b165678 Add method to lock/unlock 2023-02-19 17:12:54 +08:00
WQY\qiong e88478fc13 Improve update snapshot 2023-02-17 21:35:08 +08:00
WQY\qiong 68b42ef2a0 Add comment for scada data api 2023-02-17 21:29:17 +08:00
王琼钰 178ab15318 Accept Merge Request #153: (api -> master)
Merge Request: Add more api for snapshot

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/153
2023-02-17 19:52:06 +08:00
WQY\qiong 28480500d7 Add more api for snapshot 2023-02-17 19:51:44 +08:00
王琼钰 1ce85146d5 Accept Merge Request #151: (api -> master)
Merge Request: Support to import inp

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/151
2023-02-16 23:29:01 +08:00
WQY\qiong 78f56ac025 Support to import inp 2023-02-16 23:28:22 +08:00
王琼钰 7556f5e1c2 Accept Merge Request #150: (api -> master)
Merge Request: Do not close an open project

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/150
2023-02-16 22:44:07 +08:00
WQY\qiong 1984c537a1 Do not close an open project 2023-02-16 22:43:43 +08:00
王琼钰 366f75c78b Accept Merge Request #149: (api -> master)
Merge Request: Support to export inp

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/149
2023-02-16 22:39:58 +08:00
WQY\qiong b40e8862a7 Support to export inp 2023-02-16 22:39:31 +08:00
王琼钰 83f64963f4 Accept Merge Request #148: (api -> master)
Merge Request: More scada api and set restore operation

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/148
2023-02-16 22:20:28 +08:00
WQY\qiong 8f55b21275 Support to set restore operation 2023-02-16 22:18:51 +08:00
WQY\qiong 3c7903e742 Test more api for scada_data 2023-02-16 22:14:31 +08:00
WQY\qiong 11a5bb4333 Add more api for scada_data 2023-02-16 22:14:07 +08:00
WQY\qiong 93f5321c89 Test scada_model.status 2023-02-16 21:21:37 +08:00
WQY\qiong e62107da07 Support scada_model.status in API 2023-02-16 21:21:21 +08:00
WQY\qiong 6ba4dc2ed7 Add 'status' field to scada_model 2023-02-16 21:20:21 +08:00
王琼钰 61dfd0ffc0 Accept Merge Request #135: (api -> master)
Merge Request: Add scada API

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/135
2023-02-10 08:45:15 +08:00
WQY\qiong 8c21ebb48c Fix order 2023-02-10 01:47:46 +08:00
WQY\qiong e5f67af36b Add comment for iso time format 2023-02-10 01:47:19 +08:00
WQY\qiong 62a1303b8f Add scada_data test 2023-02-10 01:45:23 +08:00
WQY\qiong 5edd986ce6 Add scada_data api 2023-02-10 01:44:29 +08:00
WQY\qiong 14c57bb313 Add scada_model test 2023-02-10 00:47:53 +08:00
WQY\qiong e18a2a529e Add scada_model api 2023-02-10 00:47:37 +08:00
WQY\qiong 1c5de25694 Deploy scada table 2023-02-10 00:47:04 +08:00
WQY\qiong de6e4284d7 Add table scada_data 2023-02-10 00:46:32 +08:00
WQY\qiong 1520847813 Add table scada_model 2023-02-10 00:46:16 +08:00
王琼钰 e0d8649839 Accept Merge Request #134: (api -> master)
Merge Request: Build suzhouhe

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/134
2023-02-09 00:11:42 +08:00
WQY\qiong d04dea6b60 Build suzhouhe 2023-02-09 00:11:08 +08:00
王琼钰 9033c2731d Accept Merge Request #133: (api -> master)
Merge Request: Fix reading inp bug

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/133
2023-02-09 00:08:57 +08:00
WQY\qiong 8979885eac Fix reading inp bug 2023-02-09 00:08:36 +08:00
王琼钰 c5f269ac5b Accept Merge Request #126: (api -> master)
Merge Request: Add suzhouhe.inp

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/126
2023-02-04 21:25:23 +08:00
WQY\qiong 7f9c9a8e8c Add suzhouhe.inp 2023-02-04 21:24:33 +08:00
王琼钰 61ce7f7744 Accept Merge Request #125: (api -> master)
Merge Request: Test cascade deletion

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/125
2023-02-04 16:27:04 +08:00
WQY\qiong 73ec2faa97 Test curve cascade deletion 2023-02-04 16:26:05 +08:00
WQY\qiong 719e63e572 Test pattern cascade deletion 2023-02-04 16:25:45 +08:00
WQY\qiong ae793d8ab5 Workaround to prevent pump (cascade) deletion... 2023-02-04 16:25:14 +08:00
WQY\qiong 133ba32cdf Fix unset_demand_by_pattern cs 2023-02-04 16:00:44 +08:00
WQY\qiong d3e8e63e1b Fix unset_pump_energy_by_curve return value 2023-02-04 16:00:23 +08:00
WQY\qiong 41b923a838 Fix unset_pump_energy_by_pattern return value 2023-02-04 15:59:34 +08:00
WQY\qiong 2d0598ca5d Fix test name 2023-02-04 15:58:51 +08:00
WQY\qiong 6e6fc67677 Test valve cascade deletion 2023-02-04 15:08:27 +08:00
WQY\qiong 685c8a3a33 Test pump cascade deletion 2023-02-04 15:08:08 +08:00
WQY\qiong 63175956ef Test pipe cascade deletion 2023-02-04 15:07:36 +08:00
WQY\qiong 15d14765bd Remove debug info 2023-02-04 15:07:01 +08:00
WQY\qiong 2c584f1a15 Test tank cascade deletion 2023-02-04 14:46:24 +08:00
WQY\qiong b3b59eb4e0 Test reservoir cascade deletion 2023-02-04 14:46:06 +08:00
WQY\qiong 4f83e59f0d Test junction cascade deletion 2023-02-04 14:45:41 +08:00
WQY\qiong edc6d423ec Get source guard 2023-02-04 14:44:32 +08:00
王琼钰 32d81cb6f2 Accept Merge Request #124: (api -> master)
Merge Request: Prevent duplicate cascade

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/124
2023-02-04 14:06:26 +08:00
WQY\qiong 517c8dd261 Prevent duplicate cascade 2023-02-04 14:05:59 +08:00
王琼钰 0d5502a752 Accept Merge Request #123: (api -> master)
Merge Request: Support cascade deletion

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/123
2023-02-04 13:46:21 +08:00
WQY\qiong 804e1c7796 Support cascade deletion 2023-02-04 13:45:14 +08:00
WQY\qiong 633a616d39 Figure out references 2022-12-17 11:10:15 +08:00
王琼钰 591b52a718 Accept Merge Request #122: (api -> master)
Merge Request: Supplement tag test

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/122
2022-12-17 10:54:17 +08:00
WQY\qiong 4f103be303 Supplement tag test 2022-12-17 10:36:19 +08:00
王琼钰 e1bc826ab6 Accept Merge Request #121: (api -> master)
Merge Request: Fix bug when dump resource

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/121
2022-12-17 10:14:52 +08:00
WQY\qiong 58959786c6 Fix bug when dump resource 2022-12-17 10:14:24 +08:00
王琼钰 4ab35709a8 Accept Merge Request #120: (api -> master)
Merge Request: Code refactor for change set

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/120
2022-12-17 10:10:03 +08:00
WQY\qiong 4238a550f9 Fix bug for demand 2022-12-17 09:50:53 +08:00
WQY\qiong fde25105d7 Code refactor for command 2022-12-17 09:42:56 +08:00
WQY\qiong 7a784d12c5 Code refactor for demand 2022-12-17 09:30:31 +08:00
WQY\qiong b715f330dd Code refactor for change set 2022-12-17 09:23:08 +08:00
王琼钰 dcf3398d47 Accept Merge Request #119: (api -> master)
Merge Request: Add simulation result and report to output json

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/119
2022-12-10 19:27:15 +08:00
WQY\qiong f6c14f1c81 Add simulation result and report to output json 2022-12-10 19:26:43 +08:00
WQY\qiong 6dce827f1c Add error code 2022-12-10 19:24:03 +08:00
王琼钰 08022bab27 Accept Merge Request #118: (api -> master)
Merge Request: Remove debug code

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/118
2022-12-10 18:48:55 +08:00
WQY\qiong a799e30b8c Remove debug code 2022-12-10 18:48:33 +08:00
王琼钰 4bdff4fc1d Accept Merge Request #117: (api -> master)
Merge Request: Fill demand with junction data

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/117
2022-12-10 18:44:19 +08:00
WQY\qiong 21c820cdd3 Fill demand with junction data 2022-12-10 18:43:40 +08:00
DingZQ 78bb270c05 Add pickoperation 2022-12-03 14:27:14 +08:00
DingZQ e140ce14c6 Add get node links 2022-12-03 14:09:03 +08:00
DingZQ 316966b202 Refine havesnapshot 2022-12-03 10:56:53 +08:00
DingZQ ce7139a9e9 Refine fastapi code 2022-12-03 10:16:52 +08:00
王琼钰 5d9323cbf2 Accept Merge Request #116: (api -> master)
Merge Request: Fix demands undo bug

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/116
2022-11-27 00:11:25 +08:00
WQY\qiong 26086596b1 Fix demands undo bug 2022-11-27 00:10:49 +08:00
DingZQ 8be14377c8 Fixed 2022-11-26 23:44:55 +08:00
DingZQ 251d544978 Add restore method 2022-11-26 23:43:06 +08:00
DingZQ 832e06108b Fixed emitter error 2022-11-26 14:40:12 +08:00
王琼钰 859ad5403c Accept Merge Request #108: (api -> master)
Merge Request: Fix path

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/108
2022-11-26 00:27:49 +08:00
WQY\qiong 6ff2436cea Fix path 2022-11-26 00:27:23 +08:00
王琼钰 9118d67a14 Accept Merge Request #107: (api -> master)
Merge Request: Prevent memory leak

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/107
2022-11-26 00:16:15 +08:00
WQY\qiong f571a36d34 Prevent memory leak 2022-11-26 00:15:43 +08:00
王琼钰 fc046358c7 Accept Merge Request #106: (api -> master)
Merge Request: Support restore operation

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/106
2022-11-26 00:13:22 +08:00
WQY\qiong 008cd2b3a2 Support restore operation 2022-11-25 23:52:18 +08:00
王琼钰 eb74e641e2 Accept Merge Request #105: (api -> master)
Merge Request: Fix dump key work error

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/105
2022-11-25 23:35:48 +08:00
WQY\qiong e13bb53b98 Fix dump key work error 2022-11-25 23:35:29 +08:00
王琼钰 fc71b5f7b2 Accept Merge Request #104: (api -> master)
Merge Request: Add time series result

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/104
2022-11-25 23:25:41 +08:00
WQY\qiong 181ecf0cd5 Add time series result 2022-11-25 23:24:55 +08:00
WQY\qiong f3db2657ba Fix output dll bug 2022-11-25 23:24:15 +08:00
DingZQ 0a0e71b429 Refine setnodeproperties 2022-11-25 20:33:12 +08:00
DingZQ 9396b58638 refine runproject 2022-11-25 10:38:12 +08:00
DingZQ e002c1d653 Fixed run project result 2022-11-25 09:59:08 +08:00
DingZQ 407f8eaf99 Print result 2022-11-25 09:54:25 +08:00
王琼钰 2a470446b5 Accept Merge Request #103: (api -> master)
Merge Request: Fix path issue, since api dir is different from internal

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/103
2022-11-24 19:44:54 +08:00
WQY\qiong 5f5cf961ef Try simulation by api 2022-11-24 19:44:05 +08:00
WQY\qiong 3ec966d491 Fix path issue, since api dir is different from internal 2022-11-24 19:43:41 +08:00
DingZQ 110def2c05 Add more methods 2022-11-22 00:15:20 +08:00
DingZQ ad96003e8e Add more methods 2022-11-22 00:15:03 +08:00
DingZQ 410f0daca0 Fixed time error 2022-11-19 19:44:35 +08:00
WQY\qiong c314eaed6d Dump reactions to json 2022-11-19 18:59:37 +08:00
王琼钰 c2a62bb297 Accept Merge Request #102: (api -> master)
Merge Request: Add api for simulation and dump output

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/102
2022-11-19 18:46:20 +08:00
WQY\qiong 80b88976a4 Add api for simulation and dump output 2022-11-19 18:41:49 +08:00
WQY\qiong e2a0f6f95c Support more analyze apis 2022-11-19 18:32:19 +08:00
WQY\qiong 94e35851cd Code refactor 2022-11-19 18:30:45 +08:00
WQY\qiong 25b5de9e2d Code refactor 2022-11-19 18:16:52 +08:00
WQY\qiong dbc8b04114 Support dump output to json 2022-11-19 18:09:48 +08:00
WQY\qiong 0d946af50b Fix typo 2022-11-19 18:09:17 +08:00
WQY\qiong 0b4d01d2ad Refine git ignore 2022-11-19 18:08:47 +08:00
WQY\qiong 36d121a425 Read data from output 2022-11-19 17:08:12 +08:00
WQY\qiong 5faab15da6 Add closure 2022-11-19 11:26:04 +08:00
WQY\qiong 74509acc50 Start to read output 2022-11-19 11:11:56 +08:00
WQY\qiong 2c43e5ad6d Add epanet module 2022-11-19 10:22:35 +08:00
WQY\qiong eea0001458 Ignore db inp 2022-11-19 10:22:18 +08:00
王琼钰 62a84f571b Accept Merge Request #101: (api -> master)
Merge Request: Distinguish normal inp and db inp

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/101
2022-11-19 10:20:40 +08:00
WQY\qiong 2897770d4f Distinguish normal inp and db inp 2022-11-19 10:20:01 +08:00
王琼钰 02c1defba0 Accept Merge Request #100: (api -> master)
Merge Request: Add back guard...

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/100
2022-11-19 10:13:22 +08:00
WQY\qiong b6a792f7b4 Add back guard... 2022-11-19 10:12:52 +08:00
王琼钰 b45fb99cea Accept Merge Request #99: (api -> master)
Merge Request: Start simulation

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/99
2022-11-19 10:11:24 +08:00
WQY\qiong 1c612108b9 Remove git guard 2022-11-19 10:10:52 +08:00
WQY\qiong ec062ace48 Support database simulation 2022-11-19 10:09:58 +08:00
WQY\qiong 7b0e79fb9a Add temp for git visibility 2022-11-19 09:53:18 +08:00
WQY\qiong 1b64dce365 Start simulation 2022-11-19 09:51:52 +08:00
王琼钰 4c40824b9d Accept Merge Request #98: (api -> master)
Merge Request: Rename parser

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/98
2022-11-19 08:37:28 +08:00
WQY\qiong 971c07e2f2 Rename parser 2022-11-19 08:37:09 +08:00
王琼钰 72a17888c9 Accept Merge Request #97: (api -> master)
Merge Request: Add epanet component

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/97
2022-11-19 08:31:00 +08:00
WQY\qiong e8ef643c77 Refine build_db 2022-11-19 08:30:26 +08:00
WQY\qiong 000e7f5dd8 Add epanet component 2022-11-19 08:29:53 +08:00
WQY\qiong 6b3798dd5c Release memory timely 2022-11-19 08:22:59 +08:00
王琼钰 1ec72bc220 Accept Merge Request #96: (api -> master)
Merge Request: Update nanjing inp

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/96
2022-11-19 07:55:17 +08:00
WQY\qiong d144b83a61 Update nanjing inp 2022-11-19 07:54:51 +08:00
王琼钰 5046f7d246 Accept Merge Request #95: (api -> master)
Merge Request: Refactor parser

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/95
2022-11-19 00:22:52 +08:00
WQY\qiong 9d4cdfb915 Refactor parser 2022-11-19 00:22:13 +08:00
WQY\qiong c077598a3e Fix option exception 2022-11-19 00:17:41 +08:00
王琼钰 bb9852cd8d Accept Merge Request #94: (api -> master)
Merge Request: Support to dump inp

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/94
2022-11-19 00:06:38 +08:00
WQY\qiong 32ade1ed5f Fix option exception 2022-11-19 00:05:16 +08:00
WQY\qiong d73b90c20a Support to dump inp 2022-11-18 23:47:35 +08:00
WQY\qiong 55a511f7fd Fix pipe field 2022-11-18 23:47:09 +08:00
WQY\qiong c0a79f49d8 Support inp out [REPORT] 2022-11-18 23:46:51 +08:00
WQY\qiong 191b270804 Fix energy inp out 2022-11-18 23:46:27 +08:00
王琼钰 587dcd49a9 Accept Merge Request #93: (api -> master)
Merge Request: Prepare more inps into database

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/93
2022-11-18 23:18:04 +08:00
WQY\qiong 6a9221df7b Prepare more inps into database 2022-11-18 23:04:10 +08:00
王琼钰 9af20f6967 Accept Merge Request #92: (api -> master)
Merge Request: Test more inp and add to database

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/92
2022-11-18 22:59:29 +08:00
WQY\qiong 72bd9eb4b0 Test more inp and add to database 2022-11-18 22:58:49 +08:00
王琼钰 37d7b0192b Accept Merge Request #91: (api -> master)
Merge Request: Refine control and rule

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/91
2022-11-18 22:50:55 +08:00
WQY\qiong 315e94f4ad Refine control and rule 2022-11-18 22:50:33 +08:00
王琼钰 2c1d323cde Accept Merge Request #90: (api -> master)
Merge Request: Prepare database

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/90
2022-11-18 22:19:07 +08:00
WQY\qiong 0b180af5d5 Prepare database 2022-11-18 22:17:33 +08:00
WQY\qiong 7c3d274817 Clean inp file 2022-11-18 22:17:08 +08:00
WQY\qiong f164e10619 Move exnet to done folder 2022-11-18 22:14:10 +08:00
WQY\qiong 7e7592077f Move jbh to done folder 2022-11-18 22:12:22 +08:00
WQY\qiong c620faadca Group inp files 2022-11-18 22:02:42 +08:00
王琼钰 661f05ca46 Accept Merge Request #89: (api -> master)
Merge Request: Prioritize section parser

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/89
2022-11-18 21:59:21 +08:00
WQY\qiong 53ffcb6e0f Prioritize section parser 2022-11-18 21:58:58 +08:00
WQY\qiong 7a1462e547 Add error log 2022-11-18 21:58:34 +08:00
WQY\qiong 2e5f2d8d0b Rename inp file 2022-11-18 21:27:11 +08:00
王琼钰 2781d75ceb Accept Merge Request #88: (api -> master)
Merge Request: Clean inp file

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/88
2022-11-18 21:22:07 +08:00
WQY\qiong d7cebd6e63 Clean inp file 2022-11-18 21:21:23 +08:00
WQY\qiong eb19b193bd Clean inp file 2022-11-18 21:16:36 +08:00
WQY\qiong b247f8cc0c Rename file 2022-11-18 21:13:05 +08:00
WQY\qiong d4acd75955 Add error log 2022-11-18 21:12:44 +08:00
王琼钰 dd78371f9a Accept Merge Request #87: (api -> master)
Merge Request: Finish inp reader

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/87
2022-11-18 21:10:33 +08:00
WQY\qiong 1e2f19e3c8 Finish inp reader 2022-11-18 21:10:01 +08:00
WQY\qiong 1ee4a83d4c Upper key word 2022-11-18 21:07:06 +08:00
WQY\qiong 466903c900 Fix typo 2022-11-18 20:46:45 +08:00
WQY\qiong 1c63ee0d49 Guard coding for ";" line 2022-11-18 20:29:20 +08:00
WQY\qiong 1193b656a4 Support to dump change set 2022-11-18 20:28:31 +08:00
WQY\qiong f59c6e4d52 Open report status 2022-11-18 20:11:27 +08:00
WQY\qiong 1348c6626d Fix energy parser 2022-11-18 20:10:32 +08:00
WQY\qiong 0b5ce70966 Debug pattern & curve description 2022-11-18 19:42:39 +08:00
WQY\qiong 0acf2d98ed Fix status parser 2022-11-18 19:37:23 +08:00
WQY\qiong 4f619c649f Fix title parser 2022-11-18 19:37:07 +08:00
王琼钰 3c254aca7d Accept Merge Request #86: (api -> master)
Merge Request: Add back label api

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/86
2022-11-18 19:10:24 +08:00
WQY\qiong f0c0577ed3 Add back label api 2022-11-18 19:09:57 +08:00
王琼钰 285e6d3b9c Accept Merge Request #85: (api -> master)
Merge Request: Parse more sections

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/85
2022-11-18 18:56:26 +08:00
WQY\qiong 7a85867483 Use operation prefix 2022-11-17 18:47:14 +08:00
WQY\qiong b97d795484 Parse [BACKDROP] 2022-11-17 18:34:16 +08:00
WQY\qiong b2d0a727d2 Parse [LABELS] 2022-11-17 18:33:49 +08:00
WQY\qiong 2cd78ceaf0 Parse [VERTICES] 2022-11-17 18:33:34 +08:00
WQY\qiong 6e84aef52a Parse [REACTIONS] 2022-11-17 18:33:04 +08:00
WQY\qiong 4837d4ee45 Parse [ENERGY] 2022-11-17 18:32:40 +08:00
DingZQ bc030fe8dc Update source 2022-11-16 23:17:51 +08:00
DingZQ eacbca6eda Refine method name 2022-11-15 23:05:09 +08:00
DingZQ 98d5ffe58a Add mixing 2022-11-15 22:57:03 +08:00
DingZQ 0fa51935d6 Add source methods 2022-11-15 22:00:35 +08:00
WQY\qiong 7f691de6e7 Fix coord typo 2022-11-14 21:44:24 +08:00
WQY\qiong 4b1943ce25 Parse [MIXING] 2022-11-14 21:43:06 +08:00
WQY\qiong e4023717a5 Fix source type 2022-11-14 21:42:51 +08:00
WQY\qiong 1865d111df Parse [REACTIONS], only support inp out 2022-11-14 21:37:49 +08:00
WQY\qiong 2f8f7c4918 Parse [SOURCES] 2022-11-14 21:36:41 +08:00
WQY\qiong 2002b1f246 Parse [QUALITY] 2022-11-14 21:24:00 +08:00
WQY\qiong 3c9192b8d9 Parse [EMITTERS] 2022-11-14 21:21:17 +08:00
WQY\qiong d190f790bb Parse [ENERGY], only support inp out 2022-11-14 21:11:20 +08:00
王琼钰 02813e84a6 Accept Merge Request #84: (api -> master)
Merge Request: Refine energy and reaction

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/84
2022-11-14 20:46:31 +08:00
WQY\qiong 9c0cbc885b Fix api name 2022-11-14 20:45:45 +08:00
WQY\qiong 11e732f9b1 Refine reaction api 2022-11-14 20:45:22 +08:00
WQY\qiong 45c69033a8 Refine energy api 2022-11-14 20:38:05 +08:00
WQY\qiong 2e15bdce4b Parse [TAGS] 2022-11-14 20:13:04 +08:00
WQY\qiong 2891c1a4db Refine options table 2022-11-14 20:09:47 +08:00
WQY\qiong 08fde4a2d4 Refine report table 2022-11-14 20:09:31 +08:00
WQY\qiong 75a825da37 Refine times table 2022-11-14 20:09:00 +08:00
WQY\qiong 8e85928827 Refine reactions table 2022-11-14 20:08:15 +08:00
WQY\qiong dff73445c5 Update energy value 2022-11-14 20:05:31 +08:00
WQY\qiong b0815f75eb Refine energy table 2022-11-14 20:02:45 +08:00
DingZQ f7dea58cbe Change id to link 2022-11-12 19:34:37 +08:00
DingZQ e5c5f6a1e5 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2022-11-12 19:20:53 +08:00
DingZQ 106db09474 Refine status api 2022-11-12 19:20:42 +08:00
王琼钰 41ba0e4c71 Accept Merge Request #81: (api -> master)
Merge Request: Add essential parser

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/81
2022-11-12 18:55:12 +08:00
WQY\qiong 1534301618 Relax type indicator 2022-11-12 18:53:57 +08:00
WQY\qiong 6bd9afdc2e Support batch command for existing APIs 2022-11-12 18:50:20 +08:00
WQY\qiong 7648b2d116 Parse [COORDINATES] 2022-11-12 17:39:11 +08:00
WQY\qiong 1dae56939f Parse [OPTIONS] 2022-11-12 17:29:42 +08:00
WQY\qiong 27c112d1fb Parse [TIMES] 2022-11-12 17:26:42 +08:00
WQY\qiong 30131ac1d7 Parse [RULES] 2022-11-12 17:09:41 +08:00
WQY\qiong 0d3f9e7244 Parse [CONTROLS] 2022-11-12 17:08:47 +08:00
WQY\qiong 26c4fc431b Parse [CURVES] 2022-11-12 17:04:48 +08:00
WQY\qiong 236c0f9f9b Fix typo 2022-11-12 17:04:33 +08:00
WQY\qiong 9d7420df8f Parse [PATTERNS] 2022-11-12 16:42:30 +08:00
DingZQ d54ce2df71 Update setdemand 2022-11-12 16:24:12 +08:00
WQY\qiong 375001acd6 Parse [STATUS] 2022-11-12 16:21:49 +08:00
WQY\qiong 8cc6f60398 Fix demand operation 2022-11-12 16:21:35 +08:00
WQY\qiong 22545f5626 Parse [DEMANDS] 2022-11-12 15:59:22 +08:00
DingZQ 94f5fae812 Remove print 2022-11-12 15:30:59 +08:00
WQY\qiong 2403a1a75d Parse [VALVES] 2022-11-12 15:30:43 +08:00
DingZQ fe3345316a Print props for addPattern 2022-11-12 15:25:56 +08:00
WQY\qiong 8fa9fc5b98 Parse [PUMPS] 2022-11-12 15:25:33 +08:00
WQY\qiong c160ae974a Skip comment 2022-11-12 15:25:15 +08:00
DingZQ 2bd17d6dd7 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2022-11-12 15:15:27 +08:00
DingZQ 73cc6f7e62 Fixed addpattern error 2022-11-12 15:15:22 +08:00
WQY\qiong e9b1aaa539 Parse [PIPES] 2022-11-12 15:08:48 +08:00
WQY\qiong 48f14c6dad Parse [TANKS] 2022-11-12 15:02:56 +08:00
WQY\qiong 801f5bb878 Fix reservoir type convert error 2022-11-12 14:52:57 +08:00
WQY\qiong fb9bef103a Parse [RESERVOIRS] 2022-11-12 14:51:03 +08:00
WQY\qiong 749361fd47 Parse [JUNCTIONS] 2022-11-12 14:44:31 +08:00
WQY\qiong 6e433e7120 Parse [TITLE] 2022-11-12 14:17:39 +08:00
WQY\qiong a7140813a0 Start parse all sections 2022-11-12 14:17:11 +08:00
WQY\qiong 69eb6660fb Rename parser 2022-11-12 10:25:12 +08:00
WQY\qiong 047de0b792 Support new parser to bypass api 2022-11-12 10:16:12 +08:00
王琼钰 356c34551e Accept Merge Request #80: (api -> master)
Merge Request: Add more APIs

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/80
2022-11-12 10:04:20 +08:00
WQY\qiong 67c95069c1 Add more examples 2022-11-12 10:03:23 +08:00
WQY\qiong 1631592b0a Add reaction api and test 2022-11-12 10:00:08 +08:00
DingZQ 0b4b81cb5b Add fastapi 2022-11-12 08:42:17 +08:00
WQY\qiong 3d6d8995ef Hardcode report section 2022-11-12 08:26:37 +08:00
WQY\qiong 14a7bd3cf1 Refine report schema 2022-11-12 08:26:10 +08:00
WQY\qiong 453e6391e2 Refine reaction schema 2022-11-11 21:13:39 +08:00
WQY\qiong cd857009ff Add mixing api and test 2022-11-11 21:00:03 +08:00
WQY\qiong 026e582b84 Add source api and test 2022-11-11 20:14:24 +08:00
WQY\qiong a715b2a9ee Refine report schema 2022-11-11 19:45:03 +08:00
WQY\qiong 9d6b7f2b4a Refine report schema 2022-11-11 19:43:52 +08:00
DingZQ cd9f2985a4 Refine the method name 2022-11-08 08:35:23 +08:00
DingZQ a4b4634bed Add method of backdrop 2022-11-07 22:11:21 +08:00
DingZQ 3a9b0d2dbc Add methods of label 2022-11-07 22:08:33 +08:00
DingZQ 2e4661d716 Add methods for vertex 2022-11-07 22:03:38 +08:00
DingZQ 0dba1e0b6d Add energy methods 2022-11-06 12:35:11 +08:00
DingZQ 2d51c4494c Refine control rule 2022-11-06 12:23:09 +08:00
DingZQ 8c7b1f63d7 Add method for control and rule 2022-11-06 12:10:21 +08:00
DingZQ 3cd0a88eba Add method add_pattern and delete_pattern 2022-11-05 09:43:57 +08:00
DingZQ c31cfd319c Add method addcurve and deletecurve 2022-11-05 09:42:04 +08:00
王琼钰 21474192be Accept Merge Request #79: (api -> master)
Merge Request: Add quality api and test

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/79
2022-11-04 23:14:58 +08:00
WQY\qiong 03ac80a285 Add quality api and test 2022-11-04 23:08:58 +08:00
王琼钰 9b40220477 Accept Merge Request #78: (api -> master)
Merge Request: Support more APIs

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/78
2022-11-04 22:51:51 +08:00
WQY\qiong 5de5f2f9dc Update pattern and curve example 2022-11-04 22:50:28 +08:00
WQY\qiong 0e45c8e825 Update curve sql 2022-11-04 22:49:41 +08:00
WQY\qiong 36f5112f32 Refine pattern 2022-11-04 22:49:24 +08:00
WQY\qiong f9de76ee1d Refine curve to add type 2022-11-04 22:18:01 +08:00
WQY\qiong 91ae7608a3 Add vertex api and test 2022-11-04 16:30:09 +08:00
WQY\qiong a5818ec463 Rename energy table field 2022-11-04 16:12:34 +08:00
WQY\qiong 1d465b8a64 Add label api and test 2022-11-04 16:01:28 +08:00
WQY\qiong 789136a881 Add backdrop api and test 2022-11-04 14:39:30 +08:00
WQY\qiong 865d1d2cf1 Nothing to do for end 2022-11-04 13:42:14 +08:00
WQY\qiong 47e029e118 Add energy api and test 2022-11-04 13:10:31 +08:00
DingZQ 63e1b31c76 Fixed title error 2022-10-31 23:36:06 +08:00
DingZQ 6cf57e4850 Add method list_projects 2022-10-30 23:12:40 +08:00
DingZQ 851421b14b Add title api 2022-10-30 23:10:30 +08:00
王琼钰 c697fb158b Accept Merge Request #77: (api -> master)
Merge Request: Support to list project

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/77
2022-10-30 23:06:27 +08:00
WQY\qiong e8abbef5a6 Support to list project 2022-10-30 23:05:34 +08:00
DingZQ a9704fe4b1 'Merge 2022-10-30 23:02:28 +08:00
DingZQ 8bd59827e4 Add pickoperation 2022-10-30 23:00:01 +08:00
WQY\qiong 23ce9d73d0 Add batch change set for set demand 2022-10-30 14:43:22 +08:00
WQY\qiong 7860faadd7 Let set_demand be a batch command 2022-10-30 14:19:20 +08:00
王琼钰 d3319a715f Accept Merge Request #76: (api -> master)
Merge Request: Add more api and test

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/76
2022-10-30 13:38:36 +08:00
WQY\qiong 1732927f6a Support add/delete pattern & curve 2022-10-30 13:37:27 +08:00
DingZQ 39b53a3713 Remove symbol 2022-10-30 09:03:17 +08:00
DingZQ 019c0e12f1 Add method addpattern and deletapattern 2022-10-30 09:02:16 +08:00
DingZQ 7f14b541ed Add all fastapi methods 2022-10-29 22:48:55 +08:00
WQY\qiong 42eebf5bd3 Fix option schema 2022-10-29 22:32:42 +08:00
WQY\qiong 1109ca4b6d Add rule api and test 2022-10-29 19:09:34 +08:00
WQY\qiong 44475cf005 Add control api and test 2022-10-29 19:04:42 +08:00
WQY\qiong a04dcdd1ee Add tag api and test 2022-10-29 18:40:55 +08:00
王琼钰 ba7d99eba7 Accept Merge Request #63: (api -> master)
Merge Request: Order demand, pattern and curve

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/63
2022-10-29 09:29:03 +08:00
WQY\qiong e0e209ddd1 Clean valve 2022-10-29 09:16:52 +08:00
WQY\qiong 2f26cb58ad Order demand, pattern and curve 2022-10-29 09:11:27 +08:00
DingZQ 1e41be4ff9 Fix havesnapshot 2022-10-27 07:31:25 +08:00
DingZQ f68ff0f312 Add power to AddPump 2022-10-26 23:14:15 +08:00
DingZQ 9aaf3475fe Add api compressedbatch 2022-10-26 22:12:59 +08:00
WQY\qiong 89cc1acc66 Support compress batch commands as one command 2022-10-26 20:42:53 +08:00
WQY\qiong 65e2ce2541 Refine api to support one batch command 2022-10-26 19:35:04 +08:00
WQY\qiong 78d7be1d9a Supplement example 2022-10-26 18:47:43 +08:00
王琼钰 6307ecf074 Accept Merge Request #61: (api -> master)
Merge Request: Merge many apis

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/61
2022-10-26 18:39:12 +08:00
王琼钰 31a9f05b51 Accept Merge Request #60: (pick_operation -> master)
Merge Request: Add pick_operation to sync with client

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/60
2022-10-24 21:57:29 +08:00
WQY\qiong 934de535d9 Add pick_operation to sync with client 2022-10-24 21:57:11 +08:00
DingZQ 3dbb6e280c Fixed takesnapshot 2022-10-24 21:55:12 +08:00
WQY\qiong 8cfd22e17d Add pick_operation to sync with client 2022-10-24 21:52:35 +08:00
DingZQ 4371d551f9 Add tack snapshot and pick snapshot 2022-10-24 21:44:05 +08:00
WQY\qiong 5597d3afd0 Read new types into inp 2022-10-22 17:43:46 +08:00
WQY\qiong 3dae83b18d Add option api and test 2022-10-22 15:31:22 +08:00
WQY\qiong 15ed42e5ac Add time api and test 2022-10-22 14:53:18 +08:00
DingZQ e8fea3878d Refine fastapi api 2022-10-22 14:15:40 +08:00
WQY\qiong e3d3021286 Clean importer 2022-10-22 14:08:30 +08:00
WQY\qiong 50d6341510 Refine .gitignore 2022-10-22 14:04:03 +08:00
WQY\qiong 20d1fbe263 Ignore some temp test file 2022-10-22 14:02:04 +08:00
WQY\qiong eca95309ed Add emitter api and test 2022-10-22 14:00:22 +08:00
WQY\qiong 40e97fd166 Add test for unset 2022-10-22 13:43:14 +08:00
WQY\qiong 85b879e2c4 Support to unset 2022-10-22 13:37:20 +08:00
WQY\qiong 9e8a84c4c0 Add curve api and test 2022-10-22 13:17:42 +08:00
WQY\qiong bb1d772eaa Add pattern api and test 2022-10-22 12:55:16 +08:00
WQY\qiong c1b99bc7eb Add status api and test 2022-10-22 11:51:48 +08:00
WQY\qiong 6fa2f77a60 Make enum uppercase 2022-10-22 11:27:08 +08:00
WQY\qiong 4ba2e210d8 Refine option schema 2022-10-22 11:02:05 +08:00
WQY\qiong b377d5f70c Refine report schema 2022-10-22 11:01:52 +08:00
WQY\qiong d93d72a03e Refine time schema 2022-10-22 11:01:34 +08:00
WQY\qiong be850e2bf6 Refine mixing schema 2022-10-22 11:01:17 +08:00
WQY\qiong 8c8674e4ec Refine reaction schema 2022-10-22 11:01:01 +08:00
WQY\qiong b6496348bf Refine source schema 2022-10-22 11:00:45 +08:00
WQY\qiong e68cd998b3 Refine energy schema 2022-10-22 11:00:28 +08:00
WQY\qiong 5613e0eb14 Refine valve schema 2022-10-22 10:59:52 +08:00
WQY\qiong 1d4699a107 Refine pipe schema 2022-10-22 10:59:32 +08:00
WQY\qiong 2a99f97b7f Refine tank schema 2022-10-22 10:59:16 +08:00
WQY\qiong 78a098818f Refine pump schema 2022-10-22 10:58:50 +08:00
WQY\qiong 95ec7d3eaf Refine rule schema 2022-10-22 10:58:25 +08:00
WQY\qiong 9fabfbbbd3 Refine control schema 2022-10-22 10:58:08 +08:00
WQY\qiong 508822c91c Refine status schema 2022-10-22 10:57:35 +08:00
WQY\qiong 2341d7f675 Add update demand into batch command 2022-10-21 23:27:17 +08:00
WQY\qiong b15348f857 Add demand api and test 2022-10-21 23:25:13 +08:00
WQY\qiong 9254427915 Format pump schema 2022-10-21 22:11:48 +08:00
WQY\qiong 30e6813c0c Ehance pump schema 2022-10-21 21:41:35 +08:00
WQY\qiong 99f673c98e Refine pump schema 2022-10-21 21:26:30 +08:00
WQY\qiong a48c21f49b Format schema 2022-10-21 20:31:06 +08:00
DingZQ 2373353822 Add api deletenode and deletelink 2022-10-21 20:18:14 +08:00
WQY\qiong a3e491091e More test about batch command 2022-10-21 20:17:24 +08:00
DingZQ 6a66f8c967 Remove print 2022-10-21 20:03:56 +08:00
王琼钰 a8cd417188 Accept Merge Request #55: (api -> master)
Merge Request: Fix delete junction batch command

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/55
2022-10-21 20:00:35 +08:00
DingZQ 2f474b160f Print operations 2022-10-21 19:59:59 +08:00
WQY\qiong 6afd6c3411 Fix delete junction batch command 2022-10-21 19:59:58 +08:00
DingZQ 77d0a5f91e Fixed deletereservoir error 2022-10-20 21:06:51 +08:00
DingZQ 5a2d97b534 Fixed setProperties error 2022-10-19 19:23:06 +08:00
DingZQ 0222a49cc3 Refine setjuncitonproperties 2022-10-19 19:13:46 +08:00
DingZQ b867c88bf2 Fixed reservoir error 2022-10-19 12:42:20 +08:00
DingZQ 76c598a167 Update reservoir set methods 2022-10-19 12:39:28 +08:00
DingZQ d04ac2ea9d Fxied type error 2022-10-16 13:16:25 +08:00
DingZQ 5bbcc1f728 Change type to v_type for valve 2022-10-16 13:12:34 +08:00
DingZQ d87d8243a6 Refine 2022-10-16 11:57:50 +08:00
DingZQ 4dc3657c34 print add pipe meg 2022-10-16 11:51:30 +08:00
DingZQ 01efc2136c add valve 2022-10-16 11:36:22 +08:00
DingZQ 23be653faa Add pipe 2022-10-16 11:31:09 +08:00
DingZQ 469735d23f Fixed Add pipe error 2022-10-16 11:27:27 +08:00
DingZQ 2f6409e130 Add tank 2022-10-16 11:24:54 +08:00
DingZQ 8c00bf9a6f Refine node and link operatons 2022-10-16 09:48:21 +08:00
DingZQ 62e29245f4 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2022-10-16 08:43:38 +08:00
DingZQ e1f63fa403 Delete pipe 2022-10-16 08:43:16 +08:00
王琼钰 3cefd9dc89 Accept Merge Request #51: (api -> master)
Merge Request: Add inps

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/51
2022-10-15 20:10:06 +08:00
WQY\qiong 75aec8e40e Add inps 2022-10-15 18:06:10 +08:00
DingZQ a5badd1d32 Refine code 2022-10-15 18:05:18 +08:00
DingZQ 56d5e7f6a1 Refine CS 2022-10-15 18:02:15 +08:00
DingZQ 6b92952d96 Print operations 2022-10-15 18:00:15 +08:00
DingZQ 402368ce58 Print changeset 2022-10-15 17:54:45 +08:00
DingZQ a813afcd02 Use append to add dict to cs 2022-10-15 17:53:09 +08:00
DingZQ 419fa188bf Batch 2022-10-15 17:50:54 +08:00
DingZQ fc6ff82bed Execute batch 2022-10-15 17:49:03 +08:00
DingZQ e4853906b3 Print jsTxt type 2022-10-15 17:46:34 +08:00
DingZQ c8ddc79308 Print jsTxt 2022-10-15 17:44:11 +08:00
DingZQ f880abf10d Batch 2022-10-15 17:42:48 +08:00
DingZQ a31cf07262 Batch 2022-10-15 17:36:47 +08:00
DingZQ a8e1e7a1a0 Remove network 2022-10-15 17:31:06 +08:00
DingZQ d095a7d670 Print payload 2022-10-15 17:28:39 +08:00
DingZQ 803d7c12f1 Add body 2022-10-15 17:28:00 +08:00
DingZQ 99a2fd4b65 Batch 2022-10-15 17:19:54 +08:00
DingZQ edbe03a759 Print request 2022-10-15 17:18:08 +08:00
DingZQ 5203cb9989 Add Request 2022-10-15 17:16:49 +08:00
DingZQ a24a00adbe Add batch 2022-10-15 17:10:59 +08:00
DingZQ 10814ebf17 Add pump 2022-10-15 15:32:46 +08:00
DingZQ 341e38603c Add pipe 2022-10-15 15:16:40 +08:00
DingZQ 3ee688d0d6 Add pipe 2022-10-15 15:10:52 +08:00
DingZQ 6782fae984 Add pipe 2022-10-15 15:08:21 +08:00
DingZQ 80ecb4a212 Change , to : 2022-10-15 15:02:52 +08:00
DingZQ 91d99d4024 Fixed url error 2022-10-15 14:53:02 +08:00
DingZQ 981a1765ea Pass changeset to junction set methods 2022-10-15 14:38:02 +08:00
DingZQ 848f713848 Pass junction id to junction set methods 2022-10-15 14:34:57 +08:00
DingZQ 9834d2b1ea Fxied junction error 2022-10-15 13:26:46 +08:00
DingZQ 7bf1493a41 Refine add and delete junction 2022-10-15 09:19:11 +08:00
DingZQ 4a6cdb9d07 Refine addjunciton and deletejunction 2022-10-15 09:16:37 +08:00
DingZQ 17e70bb0a3 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2022-10-15 08:03:42 +08:00
DingZQ 73dfc6852e Change coord to x and y 2022-10-15 08:03:25 +08:00
王琼钰 4f66e8e684 Accept Merge Request #50: (api -> master)
Merge Request: Quick parser to load inp

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/50
2022-10-15 01:21:23 +08:00
WQY\qiong 93266be581 Quick parser to load inp 2022-10-15 01:20:13 +08:00
王琼钰 df35215b5f Accept Merge Request #49: (api -> master)
Merge Request: Add test for snapshot

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/49
2022-10-15 00:01:04 +08:00
WQY\qiong 37bafed958 Add test for snapshot 2022-10-15 00:00:41 +08:00
王琼钰 4f25352e32 Accept Merge Request #48: (api -> master)
Merge Request: Fix sync_with_server bug

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/48
2022-10-14 23:42:29 +08:00
WQY\qiong fa34d86b8a Fix bug 2022-10-14 23:40:55 +08:00
王琼钰 abb01fa49e Accept Merge Request #47: (api -> master)
Merge Request: Huge refactor and add batch api

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/47
2022-10-14 23:19:20 +08:00
WQY\qiong c5480d55ca Huge refactor to api and add batch api 2022-10-14 23:18:01 +08:00
WQY\qiong 200aaaca99 Remove research code 2022-10-14 09:27:18 +08:00
WQY\qiong 231304f73a Refine api style for change set 2022-10-09 23:25:31 +08:00
WQY\qiong 69db29d981 Try new operation table 2022-10-09 18:27:35 +08:00
WQY\qiong 02cdbbf100 Fix bug 2022-10-09 18:25:37 +08:00
WQY\qiong 9accb467b2 Format sql 2022-10-09 11:06:19 +08:00
WQY\qiong d99c0eacbf Remove redundant code 2022-10-09 10:21:46 +08:00
DingZQ 507783b1bc Refine unlock method 2022-10-03 19:08:24 +08:00
DingZQ 6db9f03b19 Add get***schema 2022-10-03 17:13:49 +08:00
DingZQ 48e149b7b4 Change operationId to operationid 2022-10-02 23:00:54 +08:00
DingZQ f8c5bb85b3 Add sync_with_server 2022-10-02 22:52:38 +08:00
DingZQ fdf02979ee Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2022-10-02 22:11:36 +08:00
DingZQ a4489ab5cf Return ChangeSet for undo and redo 2022-10-02 22:11:30 +08:00
王琼钰 cf7d3abaea Accept Merge Request #39: (api -> master)
Merge Request: Expose node specific api

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/39
2022-09-30 21:02:48 +08:00
wqy 85f27cf655 Expose node specific api 2022-09-30 21:01:57 +08:00
wqy 964e9af033 Add more test about coord 2022-09-30 20:15:25 +08:00
DingZQ 950875e4db Add getProperties and setProperties 2022-09-27 22:40:42 +08:00
DingZQ 78573a32c5 Fixed undo/redo 2022-09-27 22:22:14 +08:00
DingZQ a469f1c42c Refine main.py 2022-09-27 21:07:03 +08:00
DingZQ c7269a960e Fixed set junction coords error 2022-09-25 13:26:10 +08:00
DingZQ 843d22cc3f Add get_junction_properites and set_junciton_properties 2022-09-25 13:03:16 +08:00
DingZQ 7d7a525f54 Refine junction methods 2022-09-25 12:38:36 +08:00
DingZQ 22784dd796 Fixed lock/unlock error 2022-09-25 12:17:02 +08:00
DingZQ c050aabd78 use dict.get() to check key 2022-09-25 12:12:58 +08:00
DingZQ 9c83b4c643 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2022-09-25 12:10:43 +08:00
DingZQ 6b14f7bd57 Use has_key to check whether exists key 2022-09-25 12:10:34 +08:00
王琼钰 c9ffd646ce Accept Merge Request #38: (api -> master)
Merge Request: Trivial fix 

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/38
2022-09-25 01:43:09 +08:00
wqy d981b74485 Fix change set return type 2022-09-25 01:41:16 +08:00
wqy 9730dc980e Fix number 2022-09-25 01:35:33 +08:00
王琼钰 7a18752de4 Accept Merge Request #37: (api -> master)
Merge Request: Big api refactor

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/37
2022-09-25 01:30:07 +08:00
wqy cd467b71c8 Try operation api 2022-09-25 01:28:54 +08:00
wqy 3de39975ca Update valve api and test 2022-09-25 01:27:35 +08:00
wqy dce54171fb Update pump api and test 2022-09-25 01:05:08 +08:00
wqy 3b20811605 Update pipe api and test 2022-09-25 00:54:11 +08:00
wqy 79fa7d3fae Update tank api and test 2022-09-25 00:25:04 +08:00
wqy 277f5a3501 Update reservoir api and test 2022-09-24 23:51:00 +08:00
wqy baeef6c859 Update title and junction test 2022-09-24 23:30:17 +08:00
wqy c7f47bf06d Lib has finish the split work 2022-09-24 23:16:24 +08:00
wqy 11e30cc49f Big change to operation! 2022-09-24 23:00:55 +08:00
wqy 46df1beedb Add simp read/write sql 2022-09-24 23:00:20 +08:00
wqy 1938d3d522 Add schema utility 2022-09-24 22:59:45 +08:00
wqy 1a584424d9 Update change set 2022-09-24 22:59:04 +08:00
wqy 65df8c9f8d Update operation schema 2022-09-24 22:57:48 +08:00
DingZQ 579a4be000 Add method lockproject, unlockproject, isprojectlocked 2022-09-24 22:09:12 +08:00
DingZQ cb087ec7df Refine fastapi api 2022-09-23 00:10:04 +08:00
DingZQ cef4e90704 Support reservoir coord method 2022-09-22 22:33:27 +08:00
DingZQ 9d01fabf27 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2022-09-22 22:23:45 +08:00
DingZQ 3f1f2435eb Fixed error of addreservoir 2022-09-22 22:23:40 +08:00
王琼钰 62642e4356 Accept Merge Request #36: (api -> master)
Merge Request: Refine link getter api

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/36
2022-09-22 22:14:57 +08:00
wqy 6f088bb291 Refine link getter api 2022-09-22 22:14:17 +08:00
王琼钰 23050fd3d1 Accept Merge Request #35: (api -> master)
Merge Request: Include links property

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/35
2022-09-22 21:46:03 +08:00
wqy 9905d7f022 Include links property 2022-09-22 21:45:25 +08:00
王琼钰 b30aa7cbca Accept Merge Request #34: (api -> master)
Merge Request: Refine node getter api

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/34
2022-09-22 21:42:42 +08:00
wqy 90f02752e9 Refine node getter api 2022-09-22 21:41:26 +08:00
wqy 345ce9a98d Get link of node 2022-09-22 21:05:30 +08:00
DingZQ edb8f2cc8b Rename name to network 2022-09-21 23:07:30 +08:00
DingZQ 886b37630c Add API getcurrentoperationid 2022-09-21 21:02:35 +08:00
DingZQ aa1c967812 Add async to fastapi apis 2022-09-21 19:49:48 +08:00
DingZQ e050cccaa2 Refine pump 2022-09-21 00:00:46 +08:00
DingZQ 9c2ae2b6b2 Change name to network 2022-09-20 23:57:04 +08:00
DingZQ 287fa463cf Change minor_loss to minorloss 2022-09-20 23:42:16 +08:00
DingZQ b23fd91dc9 Refine param name 2022-09-20 23:37:32 +08:00
DingZQ 73836d728b Refine fastapi 2022-09-18 22:09:14 +08:00
DingZQ a30de5d8e1 Add pump and valve api 2022-09-18 09:43:34 +08:00
DingZQ 0144295b43 Add pipe fastapi 2022-09-18 09:35:11 +08:00
王琼钰 5c732e3e77 Accept Merge Request #33: (api -> master)
Merge Request: Let client know project open count

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/33
2022-09-18 00:41:17 +08:00
wqy 28fa751fbf Add project test case 2022-09-18 00:38:37 +08:00
wqy 9454870d5c Let client know project open count 2022-09-18 00:38:25 +08:00
王琼钰 d15000d291 Accept Merge Request #32: (api -> master)
Merge Request: Add project ref count to allow multiple connections

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/32
2022-09-18 00:28:22 +08:00
wqy a65b9b1cc5 Add project ref count to allow multiple connections 2022-09-18 00:26:58 +08:00
wqy ebba7c31d8 Add api meta data to database 2022-09-18 00:08:48 +08:00
wqy 6a8db3f661 Expose current operation to client 2022-09-17 23:56:53 +08:00
王琼钰 0b2fb70f5e Accept Merge Request #31: (api -> master)
Merge Request: Clean repo

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/31
2022-09-17 23:52:16 +08:00
wqy 5f87bb9e97 Clean repo 2022-09-17 23:51:43 +08:00
王琼钰 2c2217b784 Accept Merge Request #30: (api -> master)
Merge Request: Refine change set

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/30
2022-09-17 23:44:03 +08:00
wqy fc0283b417 Refine change set 2022-09-17 23:43:03 +08:00
王琼钰 5670ea3a6f Accept Merge Request #29: (api -> master)
Merge Request: Add new pip package

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/29
2022-09-17 23:22:56 +08:00
wqy 36a8fa2f79 Add new pip package 2022-09-17 23:22:21 +08:00
王琼钰 d94f853cb3 Accept Merge Request #28: (api -> master)
Merge Request: Add all node & link api

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/28
2022-09-17 23:20:19 +08:00
wqy 19917fe119 Remove coordinate api 2022-09-17 23:15:39 +08:00
wqy 96c01d7fc8 Add valve test case 2022-09-17 23:09:43 +08:00
wqy ec3dff12b0 Add pump test case 2022-09-17 22:57:42 +08:00
wqy 22e815cc6f Add pipe test case 2022-09-17 22:48:20 +08:00
wqy a645195eb5 Fix return type mismatch 2022-09-17 22:34:27 +08:00
wqy 2aa967546a Check link node 2022-09-17 22:29:23 +08:00
wqy 939cef4f1e Add more checking for link api 2022-09-17 22:26:03 +08:00
wqy 5dd83d7652 Fix base bug 2022-09-17 22:18:17 +08:00
wqy edd731904e Add tank test case 2022-09-17 22:02:53 +08:00
wqy 6ebaa21a7c Return change set 2022-09-17 20:36:23 +08:00
wqy 35c5881114 Ignore pytest cache 2022-09-17 19:54:47 +08:00
wqy b8d2efdc66 Add reservoir test case 2022-09-17 19:38:43 +08:00
wqy 89ffd15b7b Add title & junction test case 2022-09-17 19:19:39 +08:00
wqy 3f8c218bb0 Add test framework 2022-09-17 18:53:57 +08:00
wqy 8cf5895f1d Fix module import 2022-09-17 18:42:12 +08:00
wqy 47e0b48025 Add host for linux 2022-09-17 18:41:10 +08:00
wqy b52515b26f Return empty change set 2022-09-17 18:31:00 +08:00
wqy a72115e15f Add valve api to tjnetwork 2022-09-17 18:30:04 +08:00
wqy 58e1e447f9 Add valve api 2022-09-17 18:03:28 +08:00
wqy 391ea0386c Fix enum checking 2022-09-17 18:02:48 +08:00
DingZQ 032932a901 Fxied url error of getlinks 2022-09-17 14:42:55 +08:00
DingZQ 6f1d836df1 Add more fastapi API 2022-09-17 14:01:44 +08:00
wqy 4350b5d19e Add pump api to tjnetwork 2022-09-17 11:16:45 +08:00
wqy 3250d58733 Add pump api 2022-09-17 11:10:28 +08:00
wqy 311f067c22 Add pipe api to tjnetwork 2022-09-17 10:45:35 +08:00
wqy 01225c309c Fix utility update 2022-09-17 10:39:53 +08:00
wqy ab11d2d134 Add pipe api 2022-09-17 10:21:17 +08:00
wqy 62961cba70 Make use of utility decorate 2022-09-17 10:11:47 +08:00
wqy 4761a0d60b Add link in base 2022-09-17 09:56:54 +08:00
wqy 435411356f Lower tank overflow enum 2022-09-17 09:49:07 +08:00
wqy efe5b796f3 Clean tank 2022-09-17 09:46:58 +08:00
wqy 4f9ffc3b11 Clean tank 2022-09-17 09:45:31 +08:00
wqy 146048ac62 Clean junction and reservoir 2022-09-17 09:44:05 +08:00
wqy 64b6a1521d Refactor tank 2022-09-17 09:43:48 +08:00
wqy ee39d96120 Make use of utility decorate 2022-09-17 09:29:05 +08:00
wqy bb93a51817 Refactor reservoir 2022-09-17 09:25:21 +08:00
wqy 7e34c0bd86 More refactor of junction 2022-09-17 00:20:11 +08:00
wqy 732990237b Refactor junction 2022-09-17 00:06:20 +08:00
wqy 2575fec547 Add utility 2022-09-17 00:04:50 +08:00
wqy 41acd13995 Refactor base 2022-09-17 00:04:38 +08:00
wqy 2fe4826bf2 Lower type string 2022-09-16 23:15:16 +08:00
wqy 3ed4afeb85 Add new api to tjnetwork 2022-09-16 23:01:43 +08:00
wqy 7f26c21435 Refactor coordinate api 2022-09-16 23:01:16 +08:00
wqy 06240f1eba Add new api to tjnetwork 2022-09-16 22:49:50 +08:00
wqy cef6ea2cf7 Add tank api 2022-09-16 22:48:35 +08:00
wqy 4b398e2619 Add reservoir api 2022-09-16 22:48:06 +08:00
wqy fd03bdec41 Fix junction pattern type 2022-09-16 22:47:27 +08:00
wqy 8ffb4ed720 Rename some field of table 2022-09-16 22:41:11 +08:00
DingZQ f4fa121456 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2022-09-15 23:54:50 +08:00
DingZQ e488686c3c Add node methods 2022-09-15 23:54:45 +08:00
王琼钰 f01079cc90 Accept Merge Request #24: (api -> master)
Merge Request: Format

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/24
2022-09-15 23:16:40 +08:00
wqy cd98c27de4 Format 2022-09-15 23:15:24 +08:00
王琼钰 3ecbb8b523 Accept Merge Request #23: (api -> master)
Merge Request: Restore 2 DLLs

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/23
2022-09-15 23:00:41 +08:00
wqy 28615df1b6 Restore 2 dll... 2022-09-15 22:59:43 +08:00
王琼钰 e7cb70f2fa Accept Merge Request #22: (api -> master)
Merge Request: Clean repo

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/22
2022-09-15 22:57:38 +08:00
wqy 592826b0f4 Clean repo 2022-09-15 22:56:32 +08:00
DingZQ a66cdf9654 Refine fastapi 2022-09-15 22:24:51 +08:00
王琼钰 e197b0d0e3 Remove experiment script 2022-09-15 22:22:52 +08:00
王琼钰 ee0d0000f1 Fix typo 2022-09-15 22:22:30 +08:00
王琼钰 713ff0bedb Remove import 2022-09-15 22:22:02 +08:00
DingZQ 26cbd15a70 merge 2022-09-15 22:13:24 +08:00
DingZQ 844d4b9abc Refine junction methods 2022-09-15 22:12:14 +08:00
DingZQ c6ba5bb886 Refine junction methods 2022-09-15 22:10:23 +08:00
DingZQ 904c49d94c Refine fastapi method 2022-09-15 14:36:25 +08:00
DingZQ 7940b34386 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2022-09-15 14:16:55 +08:00
DingZQ f7f1d9c0f3 Refine http method 2022-09-15 14:16:48 +08:00
王琼钰 bb859bb946 Fix delete_junction undo sql 2022-09-15 14:10:26 +08:00
王琼钰 b3bb99b18f Junction type is fixed enum value 2022-09-15 14:06:31 +08:00
王琼钰 7455fc25bb fix coordinates sql 2022-09-15 13:55:50 +08:00
DingZQ 679e50354f Add junction methods 2022-09-15 13:18:54 +08:00
DingZQ 00606f16fc Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2022-09-14 23:09:07 +08:00
DingZQ 06c4b2f1c2 Add getnodes 2022-09-14 23:08:42 +08:00
DingZQ 25243d5111 Merge 2022-09-14 22:53:15 +08:00
DingZQ c304627c8e Add getjuncitoncorrd 2022-09-14 22:48:44 +08:00
DingZQ 5e5072d365 Add getjuncitoncorrd 2022-09-14 22:47:46 +08:00
DingZQ 63f8aa3ac7 Refine code 2022-09-13 23:30:36 +08:00
DingZQ 846b3fc090 Add api addjunction 2022-09-13 23:03:25 +08:00
王琼钰 35f66d88f5 Accept Merge Request #21: (api -> master)
Merge Request: Enhance update change set

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/21
2022-09-07 20:54:42 +08:00
wqy fb28c3c610 Ehance update change set 2022-09-07 20:53:55 +08:00
王琼钰 7f51f8accf Accept Merge Request #20: (api -> master)
Merge Request: Change pip source

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/20
2022-09-07 20:35:40 +08:00
wqy bca5f61fff Change pip source 2022-09-07 20:35:14 +08:00
王琼钰 a588b42778 Accept Merge Request #19: (api -> master)
Merge Request: Add python 3.10.6 installer

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/19
2022-09-07 20:27:44 +08:00
wqy 6721b043d8 Upload python 3.10.6 installer 2022-09-07 20:27:01 +08:00
王琼钰 9a11f5756f Accept Merge Request #18: (api -> master)
Merge Request: Fix bug

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/18
2022-09-07 20:23:13 +08:00
wqy 66e2e50321 Fix bug 2022-09-07 20:21:58 +08:00
王琼钰 5d4474204c Accept Merge Request #17: (api -> master)
Merge Request: Merge api to master

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/17
2022-09-06 21:54:51 +08:00
wqy 3a694d5e03 Make use of change set 2022-09-06 21:36:20 +08:00
wqy c99a1d728b Try change set to record result 2022-09-06 21:33:09 +08:00
DingZQ 910fecf24f Change int to double for x,y 2022-09-03 19:37:45 +08:00
DingZQ c3b87db9ff Refine json for method getnodecoord 2022-09-03 19:31:45 +08:00
DingZQ cd8585ceca Refine json for method getlinknodes 2022-09-03 19:28:38 +08:00
wqy 57728681b6 Implement redo by PL/pgSQL 2022-09-03 17:29:50 +08:00
wqy 03a0d6cf64 Implement undo in by PL/pgSQL 2022-09-03 17:07:46 +08:00
DingZQ c70d798ee0 Refine Json for getLinks 2022-09-03 16:34:21 +08:00
wqy 133feef4d2 Script for tj_project template 2022-09-03 14:51:13 +08:00
wqy 2b23d52ed3 Can not add schema for index... 2022-09-03 14:50:51 +08:00
wqy 7d2cc5c1fe Put all table & enum into namespace(postgres schema) 'tj' 2022-09-03 14:38:49 +08:00
wqy 19b690b665 Start database api work 2022-09-03 14:04:44 +08:00
DingZQ be39975756 Import json response 2022-09-03 13:33:28 +08:00
DingZQ 8f26827a90 Add method for testing json 2022-09-03 13:23:43 +08:00
wqy 45bb0483ce Refine demo 2022-09-03 11:12:54 +08:00
wqy 8fe2eebe03 Get api 2022-09-03 11:08:05 +08:00
wqy c3dcf70a04 Reuse s0 defined enum 2022-09-03 10:38:15 +08:00
wqy f16a16991e Update comment 2022-09-03 10:33:27 +08:00
wqy 0832e557fe Update comment 2022-09-03 10:27:51 +08:00
wqy a44977974d Add transaction demo 2022-09-03 10:19:08 +08:00
wqy 8430b02785 Expose transaction api 2022-09-03 09:52:36 +08:00
wqy bf188494f5 Enhance transaction 2022-09-03 09:52:08 +08:00
wqy 5f3db14067 More snapshot api 2022-09-03 09:37:31 +08:00
wqy 555cab3627 Support transaction to commit or rollback, this will modify operation tree 2022-09-03 09:30:57 +08:00
wqy dc07e4811c Drop if exists 2022-09-03 09:30:19 +08:00
wqy e68c1776c4 Code refactor 2022-09-03 08:31:17 +08:00
wqy eaefc61a87 Support to copy project 2022-09-03 08:28:13 +08:00
wqy bdb08c6798 Support random check out different snapshot 2022-09-03 08:23:28 +08:00
wqy efc33a7a86 Support more operation in database 2022-09-03 08:22:32 +08:00
wqy b5a8051c61 Fix operation tree bug 2022-09-03 08:21:43 +08:00
wqy 122f3ebddc Format api file 2022-09-03 01:13:31 +08:00
wqy cb429ad822 Add demo for new api 2022-09-03 01:07:27 +08:00
wqy 0eb42567d2 Format package 2022-09-03 01:07:04 +08:00
wqy 12e61b7b9c Refine tjnetwork_new and add junction api 2022-09-03 00:43:09 +08:00
wqy e79f887542 Refine __init__.py 2022-09-03 00:35:37 +08:00
wqy 1a23664028 Support junctions api 2022-09-03 00:35:02 +08:00
wqy cce5f55a24 Title need undo redo 2022-09-03 00:34:25 +08:00
wqy 9b2b54e9ab Base is readonly 2022-09-03 00:33:58 +08:00
wqy 2955dc94d7 Remove some tests 2022-09-03 00:33:31 +08:00
wqy 3eb7aa5a46 No undo redo for project api 2022-09-03 00:32:30 +08:00
wqy 069e49430e Enhance table junctions and coordinates 2022-09-03 00:31:46 +08:00
wqy 99bcdde8ca Clean __init__.py 2022-09-02 20:31:02 +08:00
wqy 013d037b0a Use __init__.py to manage api module 2022-09-02 20:29:22 +08:00
wqy 99958a24ca Update admin script => template 2022-09-02 20:28:37 +08:00
wqy 71c454c8cf Multiple operation into a api 2022-09-02 20:28:03 +08:00
wqy 49bea820b8 Add api to query type 2022-09-02 19:01:59 +08:00
wqy d90f7d6f8e Add new api in terms of database rather than epanet/memory 2022-09-02 18:38:11 +08:00
wqy bed5741558 Fix module in terms of python style 2022-09-02 18:33:33 +08:00
wqy 4474f12d2c Implement operation tree in database 2022-09-02 18:32:22 +08:00
wqy 248f56cbea Always has title 2022-09-02 18:30:46 +08:00
wqy a7294bf6a3 Update operation tree 2022-09-02 18:30:23 +08:00
DingZQ 3ca92b6c46 Refine getLinks 2022-09-02 10:00:23 +08:00
DingZQ 0479a7e3ca Refine 2022-09-02 09:38:01 +08:00
wqy b8eed02366 Start to implement database transaction tree 2022-09-01 23:32:14 +08:00
wqy 1fa8a4ac2a Get base 2022-09-01 22:56:19 +08:00
wqy 1c5d0984bf Get title 2022-09-01 22:38:01 +08:00
wqy 5e2b5acd9a Add section [TITLE] 2022-09-01 22:20:38 +08:00
wqy c42d705232 Add base test 2022-09-01 22:06:22 +08:00
wqy a177edbfcb Add base api 2022-09-01 22:03:09 +08:00
wqy 5ac03bb9bf Refine script 2022-09-01 22:01:10 +08:00
DingZQ 6c6bd50c67 Index of link should start from 1 2022-08-29 23:11:30 +08:00
DingZQ 7f5b4c9d74 Merge branch 'master' of https://e.coding.net/tjwater/tjwatercloud/TJWaterServer 2022-08-29 21:29:40 +08:00
DingZQ 5256243c76 Add code to get link nodes 2022-08-29 21:29:34 +08:00
wqy 96c5857653 Add script to install psycopg 2022-08-27 12:08:42 +08:00
wqy d7922351c0 Restore startserver.bat 2022-08-27 11:58:37 +08:00
wqy 4022823f1d Add gitignore 2022-08-27 11:57:11 +08:00
wqy 690e924dfd Clean repo 2022-08-27 11:54:33 +08:00
wqy ad60cf262e Add admin script to create/delete template 2022-08-27 11:51:09 +08:00
wqy b77349cfe6 Add epanet input schema 2022-08-27 11:49:43 +08:00
DingZQ cbb88986c2 Add addNodeWithCoord 2022-08-27 10:01:27 +08:00
DingZQ 633b7867f4 Update main.py 2022-08-27 09:48:04 +08:00
DingZQ b707664ffc Get node coord 2022-08-21 22:30:55 +08:00
DingZQ 482e7729b7 Refine 2022-08-21 21:52:44 +08:00
DingZQ f5ad0fde3d Add method to get nodes and links 2022-08-21 21:44:57 +08:00
DingZQ f8010a3f0d Add link 2022-08-21 21:39:53 +08:00
DingZQ 7ca9cb5d28 Initial commit 2022-08-21 11:13:13 +08:00
421 changed files with 10667300 additions and 44297 deletions
-10
View File
@@ -1,10 +0,0 @@
node_modules
.next
out
build
.git
.env*.local
README.md
docker-compose.yml
Dockerfile
.dockerignore
+10 -14
View File
@@ -1,15 +1,11 @@
KEYCLOAK_CLIENT_ID="tjwater"
KEYCLOAK_CLIENT_SECRET="83h0n413hau9bldzWdEaq6xRfASv24s5"
KEYCLOAK_ISSUER="https://keycloak.waternetwork.cn/realms/tjwater"
NEXTAUTH_SECRET="eyJhbGciOiJIUzUxMiIsInR5cCIgOiAiS"
NEXTAUTH_URL="https://demo.waternetwork.cn/"
DB_NAME=szh
DB_HOST=127.0.0.1
DB_PORT=5433
DB_USER=tjwater
DB_PASSWORD=Tjwater@123456
# 为前端暴露的变量添加 NEXT_PUBLIC_ 前缀
NEXT_PUBLIC_BACKEND_URL="https://server.waternetwork.cn"
NEXT_PUBLIC_MAP_URL="https://geoserver.waternetwork.cn/geoserver"
NEXT_PUBLIC_MAP_WORKSPACE="szh"
NEXT_PUBLIC_MAP_EXTENT="13490131, 3630016, 13525879, 3666968.25"
# NEXT_PUBLIC_MAP_AVAILABLE_LAYERS="junctions, pipes, reservoirs, scada"
NEXT_PUBLIC_NETWORK_NAME="szh"
NEXT_PUBLIC_MAPBOX_TOKEN="pk.eyJ1IjoiemhpZnUiLCJhIjoiY205azNyNGY1MGkyZDJxcTJleDUwaHV1ZCJ9.wOmSdOnDDdre-mB1Lpy6Fg"
NEXT_PUBLIC_TIANDITU_TOKEN="e3e8ad95ee911741fa71ed7bff2717ec"
TIMESCALEDB_DB_NAME=szh
TIMESCALEDB_DB_HOST=127.0.0.1
TIMESCALEDB_DB_PORT=5435
TIMESCALEDB_DB_USER=tjwater
TIMESCALEDB_DB_PASSWORD=Tjwater@123456
-3
View File
@@ -1,3 +0,0 @@
{
"extends": "next/core-web-vitals"
}
-174
View File
@@ -1,174 +0,0 @@
# Copilot Instructions for TJWater Frontend
## Project Overview
A Next.js 16 + TypeScript water network management system built with Refine framework, featuring real-time hydraulic simulation, SCADA data management, and GIS visualization using OpenLayers and Deck.gl.
## Build, Test, and Lint Commands
```bash
# Development
npm run dev # Start dev server (uses 4GB memory allocation)
# Production
npm run build # Build for production (standalone output)
npm run start # Start production server
# Testing
npm run test # Run all tests
npm run test:watch # Run tests in watch mode
npm run test:coverage # Generate coverage report
# Linting
npm run lint # Run ESLint
```
**Run single test file:**
```bash
npm test -- path/to/test-file.test.ts
```
## Architecture
### Framework Stack
- **Next.js 16** with App Router (not Pages Router)
- **Refine** framework for admin/CRUD operations
- **NextAuth.js** with Keycloak for SSO authentication
- **Material-UI (MUI) v6** for UI components
- **OpenLayers** + **Deck.gl** for map visualization
### Route Structure
- `src/app/layout.tsx` - Root layout with RefineContext
- `src/app/(main)/` - Protected routes with shared layout
- `/network-simulation` - Real-time network simulation
- `/scada-data-cleaning` - SCADA data management
- `/monitoring-place-optimization` - Sensor placement optimization
- `/health-risk-analysis` - Health risk assessment
- `/risk-analysis-location` - Risk location analysis
- `/network-partition-optimization` - Network partitioning
- `src/app/OlMap/` - Standalone map route with custom controls
- `src/app/login/` - Public authentication pages
### Key Directories
- `src/app/_refine_context.tsx` - Refine configuration with resources, auth provider, and data provider
- `src/providers/data-provider/` - REST API data provider (currently mock, update API_URL for production)
- `src/contexts/color-mode/` - Theme switching (light/dark mode persisted in cookies)
- `src/components/` - Reusable UI components (header, loading, olmap, title)
- `src/utils/` - Map utilities (layers.ts, mapQueryService.ts, color parsing)
- `src/config/config.ts` - Environment-based configuration with fallback defaults
### Path Aliases (TypeScript)
```typescript
@app/* -> src/app/*
@assets/* -> src/assets/*
@components/* -> src/components/*
@config/* -> src/config/*
@contexts/* -> src/contexts/*
@interfaces/* -> src/interfaces/*
@libs/* -> src/libs/*
@providers/* -> src/providers/*
@utils/* -> src/utils/*
@/* -> src/*
```
### Map Architecture
The map system uses a hybrid approach:
- **OpenLayers** as the base map engine (vector tiles from GeoServer)
- **Deck.gl** overlays for advanced visualizations (trips, contours, text labels)
- **DeckLayer** custom class bridges OL and Deck.gl (`@utils/layers`)
- Map data sourced from GeoServer MVT tiles (configured in `@config/config.ts`)
- Layers: junctions, pipes, valves, reservoirs, pumps, tanks, scada
### Client vs Server Components
- Most interactive components use `"use client"` directive (~35 files)
- Map components are always client-side (OpenLayers requires browser APIs)
- Layout and page files without interactivity can be server components
## Key Conventions
### Authentication Flow
- Keycloak SSO via NextAuth.js (`src/app/api/auth/[...nextauth]/`)
- Session managed with `SessionProvider` wrapper
- Auth check redirects to `/login` if unauthenticated
- Use `useSession()` hook for current user data
### Environment Variables
- All frontend-accessible variables must have `NEXT_PUBLIC_` prefix
- Backend URL: `NEXT_PUBLIC_BACKEND_URL` (defaults to http://192.168.1.42:8000)
- GeoServer URL: `NEXT_PUBLIC_MAP_URL` (defaults to http://127.0.0.1:8080/geoserver)
- Map layers: `NEXT_PUBLIC_MAP_AVAILABLE_LAYERS` (comma-separated)
- Keycloak config in `.env.local` (not committed)
### Refine Resources
Resources defined in `_refine_context.tsx` use Chinese labels and route to pages in `(main)/`:
- Each resource has: name (Chinese), list (route path), meta (icon + label)
- Icons from `react-icons` library
- No CRUD operations defined (list-only pages)
### Map Styling
- Default styles in `config.MAP_DEFAULT_STYLE` (stroke, circle, colors)
- Circle radius uses zoom-based interpolation (1px at z12, 8px at z24)
- WebGL rendering for vector tiles
- Style legends generated dynamically in map controls
### TypeScript Configuration
- Strict mode enabled
- Path aliases match jest.config.js mappings
- Target ES5 for broader compatibility
- Incremental builds enabled
### Next.js Configuration
- **Standalone output** for Docker deployment
- SVG files handled by `@svgr/webpack` (imported as React components)
- No custom server or middleware
### Testing Setup
- Jest with React Testing Library
- jsdom environment for component testing
- Path aliases configured to match tsconfig.json
- Setup file at `jest.setup.js`
## Common Patterns
### Adding a New Route
1. Create directory in `src/app/(main)/your-route/`
2. Add `page.tsx` and optional `loading.tsx`
3. Register resource in `src/app/_refine_context.tsx` resources array
4. Import icon from `react-icons`
### Working with Maps
- Use `MapComponent` from `src/app/OlMap/MapComponent.tsx`
- Access map context via `useMapData()` hook
- Vector tile layers auto-load from GeoServer workspace
- Custom overlays use Deck.gl layers (TextLayer, TripsLayer, ContourLayer)
### API Calls
- Update `dataProvider` in `src/providers/data-provider/index.ts` for real backend
- Currently points to `https://api.fake-rest.refine.dev`
- Use Refine hooks (`useList`, `useOne`, etc.) for data fetching
### Theme Management
- Theme stored in cookies (not localStorage)
- Toggle via `ColorModeContext` from `@contexts/color-mode`
- Supports light/dark modes only
- Default mode read from cookie in root layout (server-side)
+24 -29
View File
@@ -1,36 +1,31 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# vscode
.vscode/
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# python cache
__pycache__/
# testing
/coverage
# pytest
.pytest_cache/
# next.js
/.next/
/out/
# dev
dev_demo.py
build_db.py
# production
/build
# db inp
*.db.inp
# misc
.DS_Store
*.pem
# calculation
*.rpt
*.opt
*.opt.json
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# todo
TODO.md
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# build pyd
build
*.c
*.pyd
temp/
source_db_structure.dump
source_db.dump
-1
View File
@@ -1 +0,0 @@
legacy-peer-deps=true
+79974
View File
File diff suppressed because it is too large Load Diff
+9
View File
@@ -0,0 +1,9 @@
@echo off
:loop
echo 正在执行 git pull...
git pull
echo 等待10分钟...
timeout /t 600 /nobreak >nul
goto loop
-53
View File
@@ -1,53 +0,0 @@
FROM refinedev/node:22 AS base
FROM base AS deps
RUN apk add --no-cache libc6-compat
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
FROM base AS builder
# 只定义 ARG 接收来自构建命令或 docker-compose.yaml 的参数
# Next.js 在 build 时会自动读取同名的 ARG 作为环境变量
ARG NEXT_PUBLIC_BACKEND_URL
ARG NEXT_PUBLIC_MAP_URL
ARG NEXT_PUBLIC_MAP_WORKSPACE
ARG NEXT_PUBLIC_MAP_EXTENT
ARG NEXT_PUBLIC_NETWORK_NAME
ARG NEXT_PUBLIC_MAPBOX_TOKEN
ARG NEXT_PUBLIC_TIANDITU_TOKEN
COPY --from=deps /app/refine/node_modules ./node_modules
COPY . .
RUN npm run build
FROM base AS runner
ENV NODE_ENV=production
COPY --from=builder /app/refine/public ./public
RUN mkdir .next
RUN chown refine:nodejs .next
COPY --from=builder --chown=refine:nodejs /app/refine/.next/standalone ./
COPY --from=builder --chown=refine:nodejs /app/refine/.next/static ./.next/static
USER refine
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
+14
View File
@@ -0,0 +1,14 @@
UserName tjwater
Password Tjwater@123456
Organizatio TJWATEORG
Bucket TJWATERBUCKET
API Token : xGDM5RZqRJAuzAGS-otXUdC2NFdY75qJAjRLqAB4p5WcIIAlIUpOpT8_yA16AOHmJWerwQ_08gwb84sy42jnZQ==
influx config create --config-name onboarding `
--host-url "http://localhost:8086" `
--org "TJWATERORG" `
--token "p4Hq6DQ4xI6yA2tZQgo-VGzjWObylyWd4B45vMoiae0XJeNUlL87FdEUU5cJ63O87W7-nAhhGWl_0FGJiL801w==" `
--active
-48
View File
@@ -1,48 +0,0 @@
# my-refine-app
<div align="center" style="margin: 30px;">
<a href="https://refine.dev">
<img alt="refine logo" src="https://refine.ams3.cdn.digitaloceanspaces.com/readme/refine-readme-banner.png">
</a>
</div>
<br/>
This [Refine](https://github.com/refinedev/refine) project was generated with [create refine-app](https://github.com/refinedev/refine/tree/master/packages/create-refine-app).
## Getting Started
A React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility ✨
Refine's hooks and components simplifies the development process and eliminates the repetitive tasks by providing industry-standard solutions for crucial aspects of a project, including authentication, access control, routing, networking, state management, and i18n.
## Available Scripts
### Running the development server.
```bash
npm run dev
```
### Building for production.
```bash
npm run build
```
### Running the production server.
```bash
npm run start
```
## Learn More
To learn more about **Refine**, please check out the [Documentation](https://refine.dev/docs)
- **REST Data Provider** [Docs](https://refine.dev/docs/core/providers/data-provider/#overview)
- **Material UI** [Docs](https://refine.dev/docs/ui-frameworks/mui/tutorial/)
- **Custom Auth Provider** [Docs](https://refine.dev/docs/core/providers/auth-provider/)
## License
MIT
+1
View File
@@ -0,0 +1 @@
当前 适配 szh 项目的分支 是 dingsu/szh
+25
View File
@@ -0,0 +1,25 @@
import auto_realtime
import auto_store_non_realtime_SCADA_data
import asyncio
import influxdb_api
import influxdb_info
import project_info
# 为了让多个任务并发运行,我们可以用 asyncio.to_thread 分别启动它们
async def main():
task1 = asyncio.to_thread(auto_realtime.realtime_task)
task2 = asyncio.to_thread(auto_store_non_realtime_SCADA_data.store_non_realtime_SCADA_data_task)
await asyncio.gather(task1, task2)
if __name__ == "__main__":
url = influxdb_info.url
token = influxdb_info.token
org_name = influxdb_info.org
influxdb_api.query_pg_scada_info_realtime(project_info.name)
influxdb_api.query_pg_scada_info_non_realtime(project_info.name)
# 用 asyncio 并发启动两个任务
asyncio.run(main())
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+175
View File
@@ -0,0 +1,175 @@
from .project_backup import list_project, have_project, create_project, delete_project, clean_project
from .project_backup import is_project_open, open_project, close_project
from .project_backup import copy_project
#DingZQ, 2024-12-28, convert inp v3 to v2
from .inp_in import read_inp, import_inp, convert_inp_v3_to_v2
from .inp_out import dump_inp, export_inp
from .database import API_ADD, API_UPDATE, API_DELETE
from .database import ChangeSet
from .database import get_current_operation
from .database import execute_undo, execute_redo
from .database import list_snapshot
from .database import have_snapshot, have_snapshot_for_operation, have_snapshot_for_current_operation
from .database import take_snapshot_for_operation, take_snapshot_for_current_operation, take_snapshot
from .database import update_snapshot, update_snapshot_for_current_operation
from .database import delete_snapshot, delete_snapshot_by_operation
from .database import get_operation_by_snapshot, get_snapshot_by_operation
from .database import pick_snapshot
from .database import pick_operation, sync_with_server
from .database import get_restore_operation, set_restore_operation, set_restore_operation_to_current, restore
from .database import read, try_read, read_all, write
from .batch_exe import execute_batch_commands, execute_batch_command
from .extension_data import get_all_extension_data_keys, get_all_extension_data, get_extension_data, set_extension_data
from .s0_base import JUNCTION, RESERVOIR, TANK, PIPE, PUMP, VALVE, PATTERN, CURVE
from .s0_base import is_node, is_junction, is_reservoir, is_tank
from .s0_base import is_link, is_pipe, is_pump, is_valve
from .s0_base import is_curve
from .s0_base import is_pattern
from .s0_base import get_nodes, get_nodes_id_and_type, get_junctions, get_reservoirs, get_tanks, get_links, get_links_id_and_type, get_pipes, get_pumps, get_valves, get_curves, get_patterns
from .s0_base import get_node_type, get_link_type, get_element_type, get_element_type_value
from .s0_base import get_node_links, get_link_nodes
from .s0_base import get_major_nodes, get_major_pipes
from .s1_title import get_title_schema, get_title, set_title
from .s2_junctions import get_junction_schema, add_junction, get_junction, set_junction, get_all_junctions
from .batch_api import delete_junction_cascade
from .s3_reservoirs import get_reservoir_schema, add_reservoir, get_reservoir, set_reservoir, get_all_reservoirs
from .batch_api import delete_reservoir_cascade
from .s4_tanks import OVERFLOW_YES, OVERFLOW_NO
from .s4_tanks import get_tank_schema, add_tank, get_tank, set_tank, get_all_tanks
from .batch_api import delete_tank_cascade
from .s5_pipes import PIPE_STATUS_OPEN, PIPE_STATUS_CLOSED, PIPE_STATUS_CV
from .s5_pipes import get_pipe_schema, add_pipe, get_pipe, set_pipe, get_all_pipes
from .batch_api import delete_pipe_cascade
from .s6_pumps import get_pump_schema, add_pump, get_pump, set_pump, get_all_pumps
from .batch_api import delete_pump_cascade
from .s7_valves import VALVES_TYPE_PRV, VALVES_TYPE_PSV, VALVES_TYPE_PBV, VALVES_TYPE_FCV, VALVES_TYPE_TCV, VALVES_TYPE_GPV
from .s7_valves import get_valve_schema, add_valve, get_valve, set_valve, get_all_valves
from .batch_api import delete_valve_cascade
from .s8_tags import TAG_TYPE_NODE, TAG_TYPE_LINK
from .s8_tags import get_tag_schema, get_tags, get_tag, set_tag
from .s9_demands import get_demand_schema, get_demand, set_demand
from .s10_status import LINK_STATUS_OPEN, LINK_STATUS_CLOSED, LINK_STATUS_ACTIVE
from .s10_status import get_status_schema, get_status, set_status
from .s11_patterns import get_pattern_schema, get_pattern, set_pattern, add_pattern
from .batch_api import delete_pattern_cascade
from .s12_curves import CURVE_TYPE_PUMP, CURVE_TYPE_EFFICIENCY, CURVE_TYPE_VOLUME, CURVE_TYPE_HEADLOSS
from .s12_curves import get_curve_schema, get_curve, set_curve, add_curve
from .batch_api import delete_curve_cascade
from .s13_controls import get_control_schema, get_control, set_control
from .s14_rules import get_rule_schema, get_rule, set_rule
from .s15_energy import get_energy_schema, get_energy, set_energy
from .s15_energy import get_pump_energy_schema, get_pump_energy, set_pump_energy
from .s16_emitters import get_emitter_schema, get_emitter, set_emitter
from .s17_quality import get_quality_schema, get_quality, set_quality
from .s18_sources import SOURCE_TYPE_CONCEN, SOURCE_TYPE_MASS, SOURCE_TYPE_FLOWPACED, SOURCE_TYPE_SETPOINT
from .s18_sources import get_source_schema, get_source, set_source, add_source, delete_source
from .s19_reactions import get_reaction_schema, get_reaction, set_reaction
from .s19_reactions import get_pipe_reaction_schema, get_pipe_reaction, set_pipe_reaction
from .s19_reactions import get_tank_reaction_schema, get_tank_reaction, set_tank_reaction
from .s20_mixing import MIXING_MODEL_MIXED, MIXING_MODEL_2COMP, MIXING_MODEL_FIFO, MIXING_MODEL_LIFO
from .s20_mixing import get_mixing_schema, get_mixing, set_mixing, add_mixing, delete_mixing
from .s21_times import TIME_STATISTIC_NONE, TIME_STATISTIC_AVERAGED, TIME_STATISTIC_MINIMUM, TIME_STATISTIC_MAXIMUM, TIME_STATISTIC_RANGE
from .s21_times import get_time_schema, get_time, set_time
from .s23_options_util import OPTION_UNITS_CFS, OPTION_UNITS_GPM, OPTION_UNITS_MGD, OPTION_UNITS_IMGD, OPTION_UNITS_AFD, OPTION_UNITS_LPS, OPTION_UNITS_LPM, OPTION_UNITS_MLD, OPTION_UNITS_CMH, OPTION_UNITS_CMD
from .s23_options_util import OPTION_PRESSURE_PSI, OPTION_PRESSURE_KPA, OPTION_PRESSURE_METERS
from .s23_options_util import OPTION_HEADLOSS_HW, OPTION_HEADLOSS_DW, OPTION_HEADLOSS_CM
from .s23_options_util import OPTION_UNBALANCED_STOP, OPTION_UNBALANCED_CONTINUE
from .s23_options_util import OPTION_DEMAND_MODEL_DDA, OPTION_DEMAND_MODEL_PDA
from .s23_options_util import OPTION_QUALITY_NONE, OPTION_QUALITY_CHEMICAL, OPTION_QUALITY_AGE, OPTION_QUALITY_TRACE
from .s23_options_util import get_option_schema, get_option
from .batch_api import set_option_ex
from .s23_options_util import OPTION_V3_FLOW_UNITS_CFS, OPTION_V3_FLOW_UNITS_GPM, OPTION_V3_FLOW_UNITS_MGD, OPTION_V3_FLOW_UNITS_IMGD, OPTION_V3_FLOW_UNITS_AFD, OPTION_V3_FLOW_UNITS_LPS, OPTION_V3_FLOW_UNITS_LPM, OPTION_V3_FLOW_UNITS_MLD, OPTION_V3_FLOW_UNITS_CMH, OPTION_V3_FLOW_UNITS_CMD
from .s23_options_util import OPTION_V3_PRESSURE_UNITS_PSI, OPTION_V3_PRESSURE_UNITS_KPA, OPTION_V3_PRESSURE_UNITS_METERS
from .s23_options_util import OPTION_V3_HEADLOSS_MODEL_HW, OPTION_V3_HEADLOSS_MODEL_DW, OPTION_V3_HEADLOSS_MODEL_CM
from .s23_options_util import OPTION_V3_STEP_SIZING_FULL, OPTION_V3_STEP_SIZING_RELAXATION, OPTION_V3_STEP_SIZING_LINESEARCH
from .s23_options_util import OPTION_V3_IF_UNBALANCED_STOP, OPTION_V3_IF_UNBALANCED_CONTINUE
from .s23_options_util import OPTION_V3_DEMAND_MODEL_FIXED, OPTION_V3_DEMAND_MODEL_CONSTRAINED, OPTION_V3_DEMAND_MODEL_POWER, OPTION_V3_DEMAND_MODEL_LOGISTIC
from .s23_options_util import OPTION_V3_LEAKAGE_MODEL_NONE, OPTION_V3_LEAKAGE_MODEL_POWER, OPTION_V3_LEAKAGE_MODEL_FAVAD
from .s23_options_util import OPTION_V3_QUALITY_MODEL_NONE, OPTION_V3_QUALITY_MODEL_CHEMICAL, OPTION_V3_QUALITY_MODEL_AGE, OPTION_V3_QUALITY_MODEL_TRACE
from .s23_options_util import OPTION_V3_QUALITY_UNITS_HRS, OPTION_V3_QUALITY_UNITS_PCNT, OPTION_V3_QUALITY_UNITS_MGL, OPTION_V3_QUALITY_UNITS_UGL
from .s23_options_util import get_option_v3_schema, get_option_v3
from .batch_api import set_option_v3_ex
from .s24_coordinates import get_node_coord, get_nodes_in_extent, get_links_in_extent
from .s25_vertices import get_vertex_schema, get_vertex, set_vertex, add_vertex, delete_vertex
from .s25_vertices import get_all_vertex_links, get_all_vertices
from .s26_labels import get_label_schema, get_label, set_label, add_label, delete_label
from .s27_backdrop import get_backdrop_schema, get_backdrop, set_backdrop
from .s29_scada_device import SCADA_DEVICE_TYPE_PRESSURE, SCADA_DEVICE_TYPE_DEMAND, SCADA_DEVICE_TYPE_QUALITY, SCADA_DEVICE_TYPE_LEVEL, SCADA_DEVICE_TYPE_FLOW, SCADA_DEVICE_TYPE_UNKNOWN
from .s29_scada_device import get_scada_device_schema, get_scada_device, set_scada_device, add_scada_device, delete_scada_device
from .s29_scada_device import get_all_scada_device_ids, get_all_scada_devices
from .clean_api import clean_scada_device
from .s30_scada_device_data import get_scada_device_data_schema, get_scada_device_data, set_scada_device_data, add_scada_device_data, delete_scada_device_data
from .clean_api import clean_scada_device_data
from .s31_scada_element import SCADA_MODEL_TYPE_JUNCTION, SCADA_MODEL_TYPE_RESERVOIR, SCADA_MODEL_TYPE_TANK, SCADA_MODEL_TYPE_PIPE, SCADA_MODEL_TYPE_PUMP, SCADA_MODEL_TYPE_VALVE
from .s31_scada_element import SCADA_ELEMENT_STATUS_OFFLINE, SCADA_ELEMENT_STATUS_ONLINE
from .s31_scada_element import get_scada_element_schema, get_scada_element, set_scada_element, add_scada_element, delete_scada_element
from .s31_scada_element import get_all_scada_element_ids, get_all_scada_elements
from .clean_api import clean_scada_element
from .s32_region_util import get_nodes_in_boundary, get_nodes_in_region, get_links_on_region_boundary, calculate_convex_hull, calculate_boundary, inflate_boundary, inflate_region
from .s32_region import get_region_schema, get_region, set_region, add_region, delete_region
from .s33_dma_cal import PARTITION_TYPE_RB, PARTITION_TYPE_KWAY
from .s33_dma_cal import calculate_district_metering_area_for_nodes, calculate_district_metering_area_for_region, calculate_district_metering_area_for_network
from .s33_dma import get_district_metering_area_schema, get_district_metering_area, set_district_metering_area, add_district_metering_area, delete_district_metering_area
from .s33_dma import get_all_district_metering_area_ids, get_all_district_metering_areas
from .s33_dma_gen import generate_district_metering_area, generate_sub_district_metering_area
from .s34_sa_cal import calculate_service_area
from .s34_sa import get_service_area_schema, get_service_area, set_service_area, add_service_area, delete_service_area
from .s34_sa import get_all_service_area_ids, get_all_service_areas
from .s34_sa_gen import generate_service_area
from .s35_vd_cal import calculate_virtual_district
from .s35_vd import get_virtual_district_schema, get_virtual_district, set_virtual_district, add_virtual_district, delete_virtual_district
from .s35_vd import get_all_virtual_district_ids, get_all_virtual_districts
from .s35_vd_gen import generate_virtual_district
from .s36_wda_cal import calculate_demand_to_nodes, calculate_demand_to_region, calculate_demand_to_network
from .s38_scada_info import get_scada_info_schema, get_scada_info, get_all_scada_info
from .s39_user import get_user_schema, get_user, get_all_users
from .s40_schema import get_scheme_schema, get_scheme, get_all_schemes
from .s41_pipe_risk_probability import get_pipe_risk_probability_now, get_pipe_risk_probability, get_network_pipe_risk_probability_now, get_pipes_risk_probability, get_pipe_risk_probability_geometries
from .s42_sensor_placement import get_all_sensor_placements
from .s43_burst_locate_result import get_all_burst_locate_results
+53
View File
@@ -0,0 +1,53 @@
from .sections import *
from .database import ChangeSet, API_DELETE, API_UPDATE
from .batch_exe import execute_batch_command
def delete_junction_cascade(name: str, cs: ChangeSet) -> ChangeSet:
cs.operations[0] |= { 'operation' : API_DELETE, 'type' : s2_junction }
return execute_batch_command(name, cs)
def delete_reservoir_cascade(name: str, cs: ChangeSet) -> ChangeSet:
cs.operations[0] |= { 'operation' : API_DELETE, 'type' : s3_reservoir }
return execute_batch_command(name, cs)
def delete_tank_cascade(name: str, cs: ChangeSet) -> ChangeSet:
cs.operations[0] |= { 'operation' : API_DELETE, 'type' : s4_tank }
return execute_batch_command(name, cs)
def delete_pipe_cascade(name: str, cs: ChangeSet) -> ChangeSet:
cs.operations[0] |= { 'operation' : API_DELETE, 'type' : s5_pipe }
return execute_batch_command(name, cs)
def delete_pump_cascade(name: str, cs: ChangeSet) -> ChangeSet:
cs.operations[0] |= { 'operation' : API_DELETE, 'type' : s6_pump }
return execute_batch_command(name, cs)
def delete_valve_cascade(name: str, cs: ChangeSet) -> ChangeSet:
cs.operations[0] |= { 'operation' : API_DELETE, 'type' : s7_valve }
return execute_batch_command(name, cs)
def delete_pattern_cascade(name: str, cs: ChangeSet) -> ChangeSet:
cs.operations[0] |= { 'operation' : API_DELETE, 'type' : s11_pattern }
return execute_batch_command(name, cs)
def delete_curve_cascade(name: str, cs: ChangeSet) -> ChangeSet:
cs.operations[0] |= { 'operation' : API_DELETE, 'type' : s12_curve }
return execute_batch_command(name, cs)
def set_option_ex(name: str, cs: ChangeSet) -> ChangeSet:
cs.operations[0] |= { 'operation' : API_UPDATE, 'type' : s23_option }
return execute_batch_command(name, cs)
def set_option_v3_ex(name: str, cs: ChangeSet) -> ChangeSet:
cs.operations[0] |= { 'operation' : API_UPDATE, 'type' : s23_option_v3 }
return execute_batch_command(name, cs)
+238
View File
@@ -0,0 +1,238 @@
from .database import ChangeSet, g_delete_prefix, API_DELETE, API_UPDATE, try_read
from .sections import *
from .s0_base import *
from .s3_reservoirs import unset_reservoir_by_pattern
from .s4_tanks import unset_tank_by_curve
from .s6_pumps import unset_pump_by_curve, unset_pump_by_pattern
from .s8_tags import delete_tag_by_node, delete_tag_by_link
from .s9_demands import delete_demand_by_junction, unset_demand_by_pattern
from .s10_status import delete_status_by_link
from .s15_energy import delete_pump_energy_by_pump, unset_pump_energy_by_pattern, unset_pump_energy_by_curve
from .s16_emitters import delete_emitter_by_junction
from .s17_quality import delete_quality_by_node
from .s18_sources import delete_source_by_node, unset_source_by_pattern
from .s19_reactions import delete_pipe_reaction_by_pipe, delete_tank_reaction_by_tank
from .s20_mixing import delete_mixing_by_tank
from .s25_vertices import delete_vertex_by_link
from .s26_labels import unset_label_by_node
from .s23_options_util import generate_v2, generate_v3
def delete_junction_cascade_batch_cs(name: str, cs: ChangeSet) -> ChangeSet:
result = ChangeSet()
id = cs.operations[0]['id']
row = try_read(name, f"select * from junctions where id = '{id}'")
if row == None:
return result
links = get_node_links(name, id)
for link in links:
if is_pipe(name, link):
result.merge(delete_pipe_cascade_batch_cs(name, ChangeSet(g_delete_prefix | {'type': 'pipe', 'id': link})))
if is_pump(name, link):
result.merge(delete_pump_cascade_batch_cs(name, ChangeSet(g_delete_prefix | {'type': 'pump', 'id': link})))
if is_valve(name, link):
result.merge(delete_valve_cascade_batch_cs(name, ChangeSet(g_delete_prefix | {'type': 'valve', 'id': link})))
result.merge(delete_tag_by_node(name, id))
result.merge(delete_demand_by_junction(name, id))
result.merge(delete_emitter_by_junction(name, id))
result.merge(delete_quality_by_node(name, id))
result.merge(delete_source_by_node(name, id))
result.merge(unset_label_by_node(name, id))
result.merge(cs)
return result
def delete_reservoir_cascade_batch_cs(name: str, cs: ChangeSet) -> ChangeSet:
result = ChangeSet()
id = cs.operations[0]['id']
row = try_read(name, f"select * from reservoirs where id = '{id}'")
if row == None:
return result
links = get_node_links(name, id)
for link in links:
if is_pipe(name, link):
result.merge(delete_pipe_cascade_batch_cs(name, ChangeSet(g_delete_prefix | {'type': 'pipe', 'id': link})))
if is_pump(name, link):
result.merge(delete_pump_cascade_batch_cs(name, ChangeSet(g_delete_prefix | {'type': 'pump', 'id': link})))
if is_valve(name, link):
result.merge(delete_valve_cascade_batch_cs(name, ChangeSet(g_delete_prefix | {'type': 'valve', 'id': link})))
result.merge(delete_tag_by_node(name, id))
result.merge(delete_quality_by_node(name, id))
result.merge(delete_source_by_node(name, id))
result.merge(unset_label_by_node(name, id))
result.merge(cs)
return result
def delete_tank_cascade_batch_cs(name: str, cs: ChangeSet) -> ChangeSet:
result = ChangeSet()
id = cs.operations[0]['id']
row = try_read(name, f"select * from tanks where id = '{id}'")
if row == None:
return result
links = get_node_links(name, id)
for link in links:
if is_pipe(name, link):
result.merge(delete_pipe_cascade_batch_cs(name, ChangeSet(g_delete_prefix | {'type': 'pipe', 'id': link})))
if is_pump(name, link):
result.merge(delete_pump_cascade_batch_cs(name, ChangeSet(g_delete_prefix | {'type': 'pump', 'id': link})))
if is_valve(name, link):
result.merge(delete_valve_cascade_batch_cs(name, ChangeSet(g_delete_prefix | {'type': 'valve', 'id': link})))
result.merge(delete_tag_by_node(name, id))
result.merge(delete_quality_by_node(name, id))
result.merge(delete_source_by_node(name, id))
result.merge(delete_tank_reaction_by_tank(name, id))
result.merge(delete_mixing_by_tank(name, id))
result.merge(unset_label_by_node(name, id))
result.merge(cs)
return result
def delete_pipe_cascade_batch_cs(name: str, cs: ChangeSet) -> ChangeSet:
result = ChangeSet()
id = cs.operations[0]['id']
row = try_read(name, f"select * from pipes where id = '{id}'")
if row == None:
return result
result.merge(delete_tag_by_link(name, id))
result.merge(delete_status_by_link(name, id))
result.merge(delete_pipe_reaction_by_pipe(name, id))
result.merge(delete_vertex_by_link(name, id))
result.merge(cs)
return result
def delete_pump_cascade_batch_cs(name: str, cs: ChangeSet) -> ChangeSet:
result = ChangeSet()
id = cs.operations[0]['id']
row = try_read(name, f"select * from pumps where id = '{id}'")
if row == None:
return result
result.merge(delete_tag_by_link(name, id))
result.merge(delete_status_by_link(name, id))
result.merge(delete_pump_energy_by_pump(name, id))
result.merge(delete_vertex_by_link(name, id))
result.merge(cs)
return result
def delete_valve_cascade_batch_cs(name: str, cs: ChangeSet) -> ChangeSet:
result = ChangeSet()
id = cs.operations[0]['id']
row = try_read(name, f"select * from valves where id = '{id}'")
if row == None:
return result
result.merge(delete_tag_by_link(name, id))
result.merge(delete_status_by_link(name, id))
result.merge(delete_vertex_by_link(name, id))
result.merge(cs)
return result
def delete_pattern_cascade_batch_cs(name: str, cs: ChangeSet) -> ChangeSet:
result = ChangeSet()
id = cs.operations[0]['id']
row = try_read(name, f"select * from _pattern where id = '{id}'")
if row == None:
return result
result.merge(unset_reservoir_by_pattern(name, id))
result.merge(unset_pump_by_pattern(name, id))
result.merge(unset_demand_by_pattern(name, id))
result.merge(unset_pump_energy_by_pattern(name, id))
result.merge(unset_source_by_pattern(name, id))
result.merge(cs)
return result
def delete_curve_cascade_batch_cs(name: str, cs: ChangeSet) -> ChangeSet:
result = ChangeSet()
id = cs.operations[0]['id']
row = try_read(name, f"select * from _curve where id = '{id}'")
if row == None:
return result
result.merge(unset_tank_by_curve(name, id))
result.merge(unset_pump_by_curve(name, id))
result.merge(unset_pump_energy_by_curve(name, id))
result.merge(cs)
return result
def set_option_cs(cs: ChangeSet) -> ChangeSet:
cs.operations[0]['operation'] = API_UPDATE
cs.operations[0]['type'] = 'option'
new_cs = cs
new_cs.merge(generate_v3(cs))
return new_cs
def set_option_v3_cs(cs: ChangeSet) -> ChangeSet:
cs.operations[0]['operation'] = API_UPDATE
cs.operations[0]['type'] = 'option_v3'
new_cs = cs
new_cs.merge(generate_v2(cs))
return new_cs
def rewrite_batch_api(name: str, cs: ChangeSet) -> ChangeSet:
op = cs.operations[0]
api = op['operation']
type = op['type']
if api == API_DELETE:
if type == s2_junction:
return delete_junction_cascade_batch_cs(name, cs)
elif type == s3_reservoir:
return delete_reservoir_cascade_batch_cs(name, cs)
elif type == s4_tank:
return delete_tank_cascade_batch_cs(name, cs)
elif type == s5_pipe:
return delete_pipe_cascade_batch_cs(name, cs)
elif type == s6_pump:
return delete_pump_cascade_batch_cs(name, cs)
elif type == s7_valve:
return delete_valve_cascade_batch_cs(name, cs)
elif type == s11_pattern:
return delete_pattern_cascade_batch_cs(name, cs)
elif type == s12_curve:
return delete_curve_cascade_batch_cs(name, cs)
elif api == API_UPDATE:
if type == s23_option:
return set_option_cs(cs)
elif type == s23_option_v3:
return set_option_v3_cs(cs)
return cs
+380
View File
@@ -0,0 +1,380 @@
from typing import Any
from .sections import *
from .database import API_ADD, API_UPDATE, API_DELETE, ChangeSet, write, read, read_all, get_current_operation
from .extension_data import set_extension_data
from .s1_title import set_title
from .s2_junctions import set_junction, add_junction, delete_junction
from .s3_reservoirs import set_reservoir, add_reservoir, delete_reservoir
from .s4_tanks import set_tank, add_tank, delete_tank
from .s5_pipes import set_pipe, add_pipe, delete_pipe
from .s6_pumps import set_pump, add_pump, delete_pump
from .s7_valves import set_valve, add_valve, delete_valve
from .s8_tags import set_tag
from .s9_demands import set_demand
from .s10_status import set_status
from .s11_patterns import set_pattern, add_pattern, delete_pattern
from .s12_curves import set_curve, add_curve, delete_curve
from .s13_controls import set_control
from .s14_rules import set_rule
from .s15_energy import set_energy, set_pump_energy
from .s16_emitters import set_emitter
from .s17_quality import set_quality
from .s18_sources import set_source, add_source, delete_source
from .s19_reactions import set_reaction, set_pipe_reaction, set_tank_reaction
from .s20_mixing import set_mixing, add_mixing, delete_mixing
from .s21_times import set_time
from .s23_options_util import set_option, set_option_v3
from .s25_vertices import set_vertex, add_vertex, delete_vertex
from .s26_labels import set_label, add_label, delete_label
from .s27_backdrop import set_backdrop
from .s29_scada_device import set_scada_device, add_scada_device, delete_scada_device
from .s30_scada_device_data import set_scada_device_data, add_scada_device_data, delete_scada_device_data
from .s31_scada_element import set_scada_element, add_scada_element, delete_scada_element
from .s32_region import set_region, add_region, delete_region
from .s33_dma import set_district_metering_area, add_district_metering_area, delete_district_metering_area
from .s34_sa import set_service_area, add_service_area, delete_service_area
from .s35_vd import set_virtual_district, add_virtual_district, delete_virtual_district
from .batch_api_cs import rewrite_batch_api
def _execute_add_command(name: str, cs: ChangeSet) -> ChangeSet:
type = cs.operations[0]['type']
if type == s1_title:
return ChangeSet()
if type == s2_junction:
return add_junction(name, cs)
elif type == s3_reservoir:
return add_reservoir(name, cs)
elif type == s4_tank:
return add_tank(name, cs)
elif type == s5_pipe:
return add_pipe(name, cs)
elif type == s6_pump:
return add_pump(name, cs)
elif type == s7_valve:
return add_valve(name, cs)
elif type == s8_tag:
return ChangeSet()
elif type == s9_demand:
return ChangeSet()
elif type == s10_status:
return ChangeSet()
elif type == s11_pattern:
return add_pattern(name, cs)
elif type == s12_curve:
return add_curve(name, cs)
elif type == s13_control:
return ChangeSet()
elif type == s14_rule:
return ChangeSet()
elif type == s15_energy:
return ChangeSet()
elif type == s15_pump_energy:
return ChangeSet()
elif type == s16_emitter:
return ChangeSet()
elif type == s17_quality:
return ChangeSet()
elif type == s18_source:
return add_source(name, cs)
elif type == s19_reaction:
return ChangeSet()
elif type == s19_pipe_reaction:
return ChangeSet()
elif type == s19_tank_reaction:
return ChangeSet()
elif type == s20_mixing:
return add_mixing(name, cs)
elif type == s21_time:
return ChangeSet()
elif type == s22_report:
return ChangeSet()
elif type == s23_option:
return ChangeSet()
elif type == s23_option_v3:
return ChangeSet()
elif type == s24_coordinate:
return ChangeSet()
elif type == s25_vertex:
return add_vertex(name, cs)
elif type == s26_label:
return add_label(name, cs)
elif type == s27_backdrop:
return ChangeSet()
elif type == s28_end:
return ChangeSet()
elif type == s29_scada_device:
return add_scada_device(name, cs)
elif type == s30_scada_device_data:
return add_scada_device_data(name, cs)
elif type == s31_scada_element:
return add_scada_element(name, cs)
elif type == s32_region:
return add_region(name, cs)
elif type == s33_dma:
return add_district_metering_area(name, cs)
elif type == s34_sa:
return add_service_area(name, cs)
elif type == s35_vd:
return add_virtual_district(name, cs)
return ChangeSet()
def _execute_update_command(name: str, cs: ChangeSet) -> ChangeSet:
type = cs.operations[0]['type']
if type == 'extension_data':
return set_extension_data(name, cs)
if type == s1_title:
return set_title(name, cs)
if type == s2_junction:
return set_junction(name, cs)
elif type == s3_reservoir:
return set_reservoir(name, cs)
elif type == s4_tank:
return set_tank(name, cs)
elif type == s5_pipe:
return set_pipe(name, cs)
elif type == s6_pump:
return set_pump(name, cs)
elif type == s7_valve:
return set_valve(name, cs)
elif type == s8_tag:
return set_tag(name, cs)
elif type == s9_demand:
return set_demand(name, cs)
elif type == s10_status:
return set_status(name, cs)
elif type == s11_pattern:
return set_pattern(name, cs)
elif type == s12_curve:
return set_curve(name, cs)
elif type == s13_control:
return set_control(name, cs)
elif type == s14_rule:
return set_rule(name, cs)
elif type == s15_energy:
return set_energy(name, cs)
elif type == s15_pump_energy:
return set_pump_energy(name, cs)
elif type == s16_emitter:
return set_emitter(name, cs)
elif type == s17_quality:
return set_quality(name, cs)
elif type == s18_source:
return set_source(name, cs)
elif type == s19_reaction:
return set_reaction(name, cs)
elif type == s19_pipe_reaction:
return set_pipe_reaction(name, cs)
elif type == s19_tank_reaction:
return set_tank_reaction(name, cs)
elif type == s20_mixing:
return set_mixing(name, cs)
elif type == s21_time:
return set_time(name, cs)
elif type == s22_report: # no api now
return ChangeSet()
elif type == s23_option:
return set_option(name, cs)
elif type == s23_option_v3:
return set_option_v3(name, cs)
elif type == s24_coordinate: # do not support update here
return ChangeSet()
elif type == s25_vertex:
return set_vertex(name, cs)
elif type == s26_label:
return set_label(name, cs)
elif type == s27_backdrop:
return set_backdrop(name, cs)
elif type == s28_end: # end
return ChangeSet()
elif type == s29_scada_device:
return set_scada_device(name, cs)
elif type == s30_scada_device_data:
return set_scada_device_data(name, cs)
elif type == s31_scada_element:
return set_scada_element(name, cs)
elif type == s32_region:
return set_region(name, cs)
elif type == s33_dma:
return set_district_metering_area(name, cs)
elif type == s34_sa:
return set_service_area(name, cs)
elif type == s35_vd:
return set_virtual_district(name, cs)
return ChangeSet()
def _execute_delete_command(name: str, cs: ChangeSet) -> ChangeSet:
type = cs.operations[0]['type']
if type == s1_title:
return ChangeSet()
if type == s2_junction:
return delete_junction(name, cs)
elif type == s3_reservoir:
return delete_reservoir(name, cs)
elif type == s4_tank:
return delete_tank(name, cs)
elif type == s5_pipe:
return delete_pipe(name, cs)
elif type == s6_pump:
return delete_pump(name, cs)
elif type == s7_valve:
return delete_valve(name, cs)
elif type == s8_tag:
return ChangeSet()
elif type == s9_demand:
return ChangeSet()
elif type == s10_status:
return ChangeSet()
elif type == s11_pattern:
return delete_pattern(name, cs)
elif type == s12_curve:
return delete_curve(name, cs)
elif type == s13_control:
return ChangeSet()
elif type == s14_rule:
return ChangeSet()
elif type == s15_energy:
return ChangeSet()
elif type == s15_pump_energy:
return ChangeSet()
elif type == s16_emitter:
return ChangeSet()
elif type == s17_quality:
return ChangeSet()
elif type == s18_source:
return delete_source(name, cs)
elif type == s19_reaction:
return ChangeSet()
elif type == s19_pipe_reaction:
return ChangeSet()
elif type == s19_tank_reaction:
return ChangeSet()
elif type == s20_mixing:
return delete_mixing(name, cs)
elif type == s21_time:
return ChangeSet()
elif type == s22_report:
return ChangeSet()
elif type == s23_option:
return ChangeSet()
elif type == s23_option_v3:
return ChangeSet()
elif type == s24_coordinate:
return ChangeSet()
elif type == s25_vertex:
return delete_vertex(name, cs)
elif type == s26_label:
return delete_label(name, cs)
elif type == s27_backdrop:
return ChangeSet()
elif type == s28_end:
return ChangeSet()
elif type == s29_scada_device:
return delete_scada_device(name, cs)
elif type == s30_scada_device_data:
return delete_scada_device_data(name, cs)
elif type == s31_scada_element:
return delete_scada_element(name, cs)
elif type == s32_region:
return delete_region(name, cs)
elif type == s33_dma:
return delete_district_metering_area(name, cs)
elif type == s34_sa:
return delete_service_area(name, cs)
elif type == s35_vd:
return delete_virtual_district(name, cs)
return ChangeSet()
def execute_batch_commands(name: str, cs: ChangeSet) -> ChangeSet:
new_cs = ChangeSet()
for op in cs.operations:
new_cs.merge(rewrite_batch_api(name, ChangeSet(op)))
result = ChangeSet()
todo = {}
try:
for op in new_cs.operations:
todo = op
operation = op['operation']
if operation == API_ADD:
result.merge(_execute_add_command(name, ChangeSet(op)))
elif operation == API_UPDATE:
result.merge(_execute_update_command(name, ChangeSet(op)))
elif operation == API_DELETE:
result.merge(_execute_delete_command(name, ChangeSet(op)))
except:
print(f'ERROR: Fail to execute {todo}')
return result
def execute_batch_command(name: str, cs: ChangeSet) -> ChangeSet:
write(name, 'delete from batch_operation where id > 0')
write(name, "update operation_table set option = 'batch_operation' where option = 'operation'")
new_cs = ChangeSet()
for op in cs.operations:
new_cs.merge(rewrite_batch_api(name, ChangeSet(op)))
result = ChangeSet()
todo = {}
try:
for op in new_cs.operations:
todo = op
operation = op['operation']
if operation == API_ADD:
result.merge(_execute_add_command(name, ChangeSet(op)))
elif operation == API_UPDATE:
result.merge(_execute_update_command(name, ChangeSet(op)))
elif operation == API_DELETE:
result.merge(_execute_delete_command(name, ChangeSet(op)))
except:
print(f'ERROR: Fail to execute {todo}')
count = read(name, 'select count(*) as count from batch_operation')['count']
if count == 1:
write(name, 'delete from batch_operation where id > 0')
write(name, "update operation_table set option = 'operation' where option = 'batch_operation'")
return ChangeSet()
redo_list: list[str] = []
redo_cs_list: list[dict[str, Any]] = []
redo_rows = read_all(name, 'select redo, redo_cs from batch_operation where id > 0 order by id asc')
for row in redo_rows:
redo_list.append(row['redo'])
redo_cs_list += eval(row['redo_cs'])
undo_list: list[str] = []
undo_cs_list: list[dict[str, Any]] = []
undo_rows = read_all(name, 'select undo, undo_cs from batch_operation where id > 0 order by id desc')
for row in undo_rows:
undo_list.append(row['undo'])
undo_cs_list += eval(row['undo_cs'])
redo = '\n'.join(redo_list).replace("'", "''")
redo_cs = str(redo_cs_list).replace("'", "''")
undo = '\n'.join(undo_list).replace("'", "''")
undo_cs = str(undo_cs_list).replace("'", "''")
parent = get_current_operation(name)
write(name, f"insert into operation (id, redo, undo, parent, redo_cs, undo_cs) values (default, '{redo}', '{undo}', {parent}, '{redo_cs}', '{undo_cs}')")
current = read(name, 'select max(id) as id from operation')['id']
write(name, f"update current_operation set id = {current}")
write(name, 'delete from batch_operation where id > 0')
write(name, "update operation_table set option = 'operation' where option = 'batch_operation'")
return result
+45
View File
@@ -0,0 +1,45 @@
from .database import ChangeSet, read_all
from .batch_exe import execute_batch_command
# TODO: merge to batch_api
def clean_scada_device_cs(name: str) -> ChangeSet:
cs = ChangeSet()
rows = read_all(name, 'select id from scada_device acs')
for row in rows:
cs.delete({ 'type': 'scada_device', 'id': row['id'] })
return cs
def clean_scada_device_data_cs(name: str) -> ChangeSet:
cs = ChangeSet()
rows = read_all(name, 'select distinct device_id from scada_device_data acs')
for row in rows:
cs.update({ 'type': 'scada_device_data', 'device_id': row['device_id'], 'data': [] })
return cs
def clean_scada_element_cs(name: str) -> ChangeSet:
cs = ChangeSet()
rows = read_all(name, 'select id from scada_element acs')
for row in rows:
cs.delete({ 'type': 'scada_element', 'id': row['id'] })
return cs
def clean_scada_device(name: str) -> ChangeSet:
return execute_batch_command(name, clean_scada_device_cs(name))
def clean_scada_device_data(name: str) -> ChangeSet:
return execute_batch_command(name, clean_scada_device_data_cs(name))
def clean_scada_element(name: str) -> ChangeSet:
return execute_batch_command(name, clean_scada_element_cs(name))
+3
View File
@@ -0,0 +1,3 @@
import psycopg as pg
g_conn_dict : dict[str, pg.Connection] = {}
+349
View File
@@ -0,0 +1,349 @@
from typing import Any
from psycopg.rows import dict_row, Row
from .connection import g_conn_dict as conn
API_ADD = 'add'
API_UPDATE = 'update'
API_DELETE = 'delete'
g_add_prefix = { 'operation': API_ADD }
g_update_prefix = { 'operation': API_UPDATE }
g_delete_prefix = { 'operation': API_DELETE }
class ChangeSet:
def __init__(self, ps: dict[str, Any] | None = None):
self.operations : list[dict[str, Any]] = []
if ps != None:
self.append(ps)
@staticmethod
def from_list(ps: list[dict[str, Any]]):
cs = ChangeSet()
for _cs in ps:
cs.append(_cs)
return cs
def add(self, ps: dict[str, Any]):
self.operations.append(g_add_prefix | ps)
return self
def update(self, ps: dict[str, Any]):
self.operations.append(g_update_prefix | ps)
return self
def delete(self, ps: dict[str, Any]):
self.operations.append(g_delete_prefix | ps)
return self
def append(self, ps: dict[str, Any]):
self.operations.append(ps)
return self
def merge(self, cs):
if len(cs.operations) > 0:
self.operations += cs.operations
return self
def dump(self):
for op in self.operations:
print(op)
def compress(self):
return self
class DbChangeSet:
def __init__(self, redo_sql: str, undo_sql: str, redo_cs: list[dict[str, Any]], undo_cs: list[dict[str, Any]]) -> None:
self.redo_sql = redo_sql
self.undo_sql = undo_sql
self.redo_cs = redo_cs
self.undo_cs = undo_cs
@staticmethod
def from_list(css):
redo_sql_s : list[str] = []
undo_sql_s : list[str] = []
redo_cs_s : list[dict[str, Any]] = []
undo_cs_s : list[dict[str, Any]] = []
for r in css:
redo_sql_s.append(r.redo_sql)
undo_sql_s.append(r.undo_sql)
redo_cs_s += r.redo_cs
r.undo_cs.reverse() # reverse again...
undo_cs_s += r.undo_cs
redo_sql = '\n'.join(redo_sql_s)
undo_sql_s.reverse()
undo_sql = '\n'.join(undo_sql_s)
undo_cs_s.reverse()
return DbChangeSet(redo_sql, undo_sql, redo_cs_s, undo_cs_s)
def read(name: str, sql: str) -> Row:
with conn[name].cursor(row_factory=dict_row) as cur:
cur.execute(sql)
row = cur.fetchone()
if row == None:
raise Exception(sql)
return row
def read_all(name: str, sql: str) -> list[Row]:
with conn[name].cursor(row_factory=dict_row) as cur:
cur.execute(sql)
return cur.fetchall()
def try_read(name: str, sql: str) -> Row | None:
with conn[name].cursor(row_factory=dict_row) as cur:
cur.execute(sql)
return cur.fetchone()
def write(name: str, sql: str) -> None:
with conn[name].cursor() as cur:
cur.execute(sql)
def get_current_operation(name: str) -> int:
return int(read(name, 'select id from current_operation')['id'])
def execute_command(name: str, command: DbChangeSet, undo_redo: bool = True) -> ChangeSet:
write(name, command.redo_sql)
if undo_redo:
op_table = read(name, "select * from operation_table")['option']
parent = get_current_operation(name)
redo_sql = command.redo_sql.replace("'", "''")
undo_sql = command.undo_sql.replace("'", "''")
redo_cs_str = str(command.redo_cs).replace("'", "''")
undo_cs_str = str(command.undo_cs).replace("'", "''")
write(name, f"insert into {op_table} (id, redo, undo, parent, redo_cs, undo_cs) values (default, '{redo_sql}', '{undo_sql}', {parent}, '{redo_cs_str}', '{undo_cs_str}')")
if op_table == 'operation':
current = read(name, 'select max(id) as id from operation')['id']
write(name, f"update current_operation set id = {current}")
return ChangeSet.from_list(command.redo_cs)
def execute_undo(name: str, discard: bool = False) -> ChangeSet:
row = read(name, f'select * from operation where id = {get_current_operation(name)}')
write(name, row['undo'])
# update foreign key
write(name, f"update current_operation set id = {row['parent']} where id = {row['id']}")
if discard:
# update foreign key
write(name, f"update operation set redo_child = null where id = {row['parent']}")
# on delete cascade => child & snapshot
write(name, f"delete from operation where id = {row['id']}")
else:
write(name, f"update operation set redo_child = {row['id']} where id = {row['parent']}")
e = eval(row['undo_cs'])
return ChangeSet.from_list(e)
def execute_redo(name: str) -> ChangeSet:
row = read(name, f'select * from operation where id = {get_current_operation(name)}')
if row['redo_child'] == None:
return ChangeSet()
row = read(name, f"select * from operation where id = {row['redo_child']}")
write(name, row['redo'])
write(name, f"update current_operation set id = {row['id']} where id = {row['parent']}")
e = eval(row['redo_cs'])
return ChangeSet.from_list(e)
def list_snapshot(name: str) -> list[tuple[int, str]]:
rows = read_all(name, f'select * from snapshot_operation order by id')
result = []
for row in rows:
result.append((int(row['id']), str(row['tag'])))
return result
def have_snapshot(name: str, tag: str) -> bool:
return try_read(name, f"select id from snapshot_operation where tag = '{tag}'") != None
def have_snapshot_for_operation(name: str, operation: int) -> bool:
return try_read(name, f"select id from snapshot_operation where id = {operation}") != None
def have_snapshot_for_current_operation(name: str) -> bool:
return have_snapshot_for_operation(name, get_current_operation(name))
def take_snapshot_for_operation(name: str, operation: int, tag: str) -> None:
if tag == None or tag == '':
return None
write(name, f"insert into snapshot_operation (id, tag) values ({operation}, '{tag}')")
def take_snapshot_for_current_operation(name: str, tag: str) -> None:
take_snapshot_for_operation(name, get_current_operation(name), tag)
# deprecated ! use take_snapshot_for_current_operation instead
def take_snapshot(name: str, tag: str) -> None:
take_snapshot_for_current_operation(name, tag)
def update_snapshot(name: str, operation: int, tag: str) -> None:
if tag == None or tag == '':
return None
if have_snapshot_for_operation(name, operation):
write(name, f"update snapshot_operation set tag = '{tag}' where id = {operation}")
else:
take_snapshot_for_operation(name, operation, tag)
def update_snapshot_for_current_operation(name: str, tag: str) -> None:
return update_snapshot(name, get_current_operation(name), tag)
def delete_snapshot(name: str, tag: str) -> None:
write(name, f"delete from snapshot_operation where tag = '{tag}'")
def delete_snapshot_by_operation(name: str, operation: int) -> None:
write(name, f"delete from snapshot_operation where id = {operation}")
def get_operation_by_snapshot(name: str, tag: str) -> int | None:
row = try_read(name, f"select id from snapshot_operation where tag = '{tag}'")
return int(row['id']) if row != None else None
def get_snapshot_by_operation(name: str, operation: int) -> str | None:
row = try_read(name, f"select tag from snapshot_operation where id = {operation}")
return str(row['tag']) if row != None else None
def _get_parents(name: str, id: int) -> list[int]:
ids = [id]
while ids[-1] != 0:
row = read(name, f'select parent from operation where id = {ids[-1]}')
ids.append(int(row['parent']))
return ids
def pick_operation(name: str, operation: int, discard: bool) -> ChangeSet:
target = operation
curr = get_current_operation(name)
curr_parents = _get_parents(name, curr)
target_parents = _get_parents(name, target)
change = ChangeSet()
if target in curr_parents:
for _ in range(curr_parents.index(target)):
change.merge(execute_undo(name, discard))
elif curr in target_parents:
target_parents.reverse()
curr_index = target_parents.index(curr)
for i in range(curr_index, len(target_parents) - 1):
write(name, f"update operation set redo_child = '{target_parents[i + 1]}' where id = '{target_parents[i]}'")
change.merge(execute_redo(name))
else:
ancestor_index = -1
while curr_parents[ancestor_index] == target_parents[ancestor_index]:
ancestor_index -= 1
ancestor = curr_parents[ancestor_index + 1]
for _ in range(curr_parents.index(ancestor)):
change.merge(execute_undo(name, discard))
target_parents.reverse()
curr_index = target_parents.index(ancestor)
for i in range(curr_index, len(target_parents) - 1):
write(name, f"update operation set redo_child = '{target_parents[i + 1]}' where id = '{target_parents[i]}'")
change.merge(execute_redo(name))
return change.compress()
def pick_snapshot(name: str, tag: str, discard: bool) -> ChangeSet:
if not have_snapshot(name, tag):
return ChangeSet()
target = int(read(name, f"select id from snapshot_operation where tag = '{tag}'")['id'])
return pick_operation(name, target, discard)
def _get_change_set(name: str, operation: int, undo: bool) -> ChangeSet:
row = read(name, f'select * from operation where id = {operation}')
field= 'undo_cs' if undo else 'redo_cs'
return ChangeSet.from_list(eval(row[field]))
def sync_with_server(name: str, operation: int) -> ChangeSet:
fr = operation
to = get_current_operation(name)
fr_parents = _get_parents(name, fr)
to_parents = _get_parents(name, to)
change = ChangeSet()
if fr in to_parents:
index = to_parents.index(fr) - 1
while index >= 0:
change.merge(_get_change_set(name, to_parents[index], False)) #redo
index -= 1
elif to in fr_parents:
index = 0
while index <= fr_parents.index(to) - 1:
change.merge(_get_change_set(name, fr_parents[index], True))
index += 1
else:
ancestor_index = -1
while fr_parents[ancestor_index] == to_parents[ancestor_index]:
ancestor_index -= 1
ancestor = fr_parents[ancestor_index + 1]
index = 0
while index <= fr_parents.index(ancestor) - 1:
change.merge(_get_change_set(name, fr_parents[index], True))
index += 1
index = to_parents.index(ancestor) - 1
while index >= 0:
change.merge(_get_change_set(name, to_parents[index], False))
index -= 1
return change.compress()
def get_restore_operation(name: str) -> int:
return read(name, f'select * from restore_operation')['id']
def set_restore_operation(name: str, operation: int) -> None:
write(name, f'update restore_operation set id = {operation}')
def set_restore_operation_to_current(name: str) -> None:
return set_restore_operation(name, get_current_operation(name))
def restore(name: str, discard: bool) -> ChangeSet:
op = get_restore_operation(name)
return pick_operation(name, op, discard)
Binary file not shown.
BIN
View File
Binary file not shown.
+62
View File
@@ -0,0 +1,62 @@
from .database import *
def get_all_extension_data_keys(name: str) -> list[str]:
result: list[str] = []
for row in read_all(name, 'select key from extension_data'):
result.append(row['key'])
return result
def get_all_extension_data(name: str) -> dict[str, Any]:
result: dict[str, Any] = {}
for row in read_all(name, 'select key, value from extension_data'):
result[row['key']] = row['value']
return result
def get_extension_data(name: str, key: str) -> str | None:
if key == None or key == '':
return None
row = try_read(name, f"select value from extension_data where key = '{key}'")
if row == None:
return None
return row['value']
def _set_extension_data(name: str, cs: ChangeSet) -> DbChangeSet:
op = cs.operations[0]
key, new_val = op['key'], op['value']
f_new_val = f"'{new_val}'" if new_val != None else 'null'
old_val = get_extension_data(name, key)
f_old_val = f"'{old_val}'" if old_val != None else 'null'
redo_sql = f"delete from extension_data where key = '{key}';"
if new_val != None:
redo_sql += f"insert into extension_data (key, value) values ('{key}', {f_new_val});"
undo_sql = f"delete from extension_data where key = '{key}';"
if old_val != None:
undo_sql += f"insert into extension_data (key, value) values ('{key}', {f_old_val});"
redo_cs = g_update_prefix | { 'type': 'extension_data', 'key': key, 'value': new_val }
undo_cs = g_update_prefix | { 'type': 'extension_data', 'key': key, 'value': old_val }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_extension_data(name: str, cs: ChangeSet) -> ChangeSet:
if len(cs.operations) != 1:
return ChangeSet()
op = cs.operations[0]
if 'key' not in op or 'value' not in op:
return ChangeSet()
key = op['key']
if key == None or key == '':
return ChangeSet()
return execute_command(name, _set_extension_data(name, cs))
+428
View File
@@ -0,0 +1,428 @@
import datetime
import os
from .project_backup import *
from .database import ChangeSet, write
from .sections import *
from .s0_base import get_region_type
from .s1_title import inp_in_title
from .s2_junctions import inp_in_junction
from .s3_reservoirs import inp_in_reservoir
from .s4_tanks import inp_in_tank
from .s5_pipes import inp_in_pipe
from .s6_pumps import inp_in_pump
from .s7_valves import inp_in_valve
from .s8_tags import inp_in_tag
from .s9_demands import inp_in_demand
from .s10_status import inp_in_status
from .s11_patterns import pattern_v3_types, inp_in_pattern
from .s12_curves import curve_types, inp_in_curve
from .s13_controls import inp_in_control
from .s14_rules import inp_in_rule
from .s15_energy import inp_in_energy
from .s16_emitters import inp_in_emitter
from .s17_quality import inp_in_quality
from .s18_sources import inp_in_source
from .s19_reactions import inp_in_reaction
from .s20_mixing import inp_in_mixing
from .s21_times import inp_in_time
from .s22_report import inp_in_report
from .s23_options import inp_in_option
from .s23_options_v3 import inp_in_option_v3
from .s24_coordinates import inp_in_coord
from .s25_vertices import inp_in_vertex
from .s26_labels import inp_in_label
from .s27_backdrop import inp_in_backdrop
from .s32_region import inp_in_region,inp_in_bound,inp_in_regionnodes
from .s32_region_util import from_postgis_polygon,to_postgis_polygon
#DingZQ, 2024-12-28, export inp
from .inp_out import export_inp
_S = 'S'
_L = 'L'
def _inp_in_option(section: list[str], version: str = '3') -> str:
return inp_in_option_v3(section) if version == '3' else inp_in_option(section)
_handler = {
TITLE : (_S, inp_in_title),
JUNCTIONS : (_L, inp_in_junction), # line, demand_outside
RESERVOIRS : (_L, inp_in_reservoir),
TANKS : (_L, inp_in_tank),
PIPES : (_L, inp_in_pipe),
PUMPS : (_L, inp_in_pump),
VALVES : (_L, inp_in_valve),
TAGS : (_L, inp_in_tag),
DEMANDS : (_L, inp_in_demand),
STATUS : (_L, inp_in_status),
PATTERNS : (_L, inp_in_pattern), # line, fixed
CURVES : (_L, inp_in_curve),
CONTROLS : (_L, inp_in_control),
RULES : (_L, inp_in_rule),
ENERGY : (_L, inp_in_energy),
EMITTERS : (_L, inp_in_emitter),
QUALITY : (_L, inp_in_quality),
SOURCES : (_L, inp_in_source),
REACTIONS : (_L, inp_in_reaction),
MIXING : (_L, inp_in_mixing),
TIMES : (_S, inp_in_time),
REPORT : (_S, inp_in_report),
OPTIONS : (_S, _inp_in_option), # line, version
COORDINATES : (_L, inp_in_coord),
VERTICES : (_L, inp_in_vertex),
REGION : (_L, inp_in_region),
BOUND : (_L, inp_in_bound),
REGION_NODES : (_L, inp_in_regionnodes),
LABELS : (_L, inp_in_label),
BACKDROP : (_S, inp_in_backdrop),
#END : 'END',
}
_level_1 = [
TITLE,
PATTERNS,
CURVES,
CONTROLS,
RULES,
TIMES,
REPORT,
OPTIONS,
BACKDROP,
]
_level_2 = [
JUNCTIONS,
RESERVOIRS,
TANKS,
]
_level_3 = [
PIPES,
PUMPS,
VALVES,
DEMANDS,
EMITTERS,
QUALITY,
SOURCES,
MIXING,
COORDINATES,
LABELS,
]
_level_4 = [
TAGS,
STATUS,
ENERGY,
REACTIONS,
VERTICES,
REGION,
BOUND,
REGION_NODES,
]
map_regiontype={
# map the region types from desktop to server
'DISTRIBUTION':'WDA',
'DMA':'DMA',
'PMA':'PMA',
'VD':'VD',
'SA':'SA',
}
class SQLBatch:
def __init__(self, project: str, count: int = 100) -> None:
self.batch: list[str] = []
self.project = project
self.count = count
def add(self, sql: str) -> None:
self.batch.append(sql)
if len(self.batch) == self.count:
self.flush()
def flush(self) -> None:
write(self.project, ''.join(self.batch))
self.batch.clear()
def _print_time(desc: str) -> datetime.datetime:
now = datetime.datetime.now()
time = now.strftime('%Y-%m-%d %H:%M:%S')
print(f"{time}: {desc}")
return now
def _get_file_offset(inp: str) -> tuple[dict[str, list[int]], bool]:
offset: dict[str, list[int]] = {}
current = ''
demand_outside = False
with open(inp) as f:
while True:
line = f.readline()
if not line:
break
line = line.strip()
if line.startswith('['):
for s in section_name:
if line.startswith(f'[{s}'):
if s not in offset:
offset[s] = []
offset[s].append(f.tell())
current = s
break
elif line != '' and line.startswith(';') == False:
if current == DEMANDS:
demand_outside = True
return (offset, demand_outside)
def parse_file(project: str, inp: str, version: str = '3') -> None:
start = _print_time(f'Start reading file "{inp}"...')
_print_time("First scan...")
offset, demand_outside = _get_file_offset(inp)
levels = _level_1 + _level_2 + _level_3 + _level_4
# parse the whole section rather than line
sections : dict[str, list[str]]= {}
for [s, t] in _handler.items():
if t[0] == _S:
sections[s] = []
variable_patterns = []
current_pattern = None
current_curve = None
curve_type_desc_line = None
current_region =None
current_bound=[]
current_bound.clear()
region_list={}
current_region_nodes=[]
current_region_nodes.clear()
sql_batch = SQLBatch(project)
_print_time("Second scan...")
with open(inp) as f:
for s in levels:
if s not in offset:
continue
if s == DEMANDS and demand_outside == False:
continue
_print_time(f"[{s}]")
is_s = _handler[s][0] == _S
handler = _handler[s][1]
for ptr in offset[s]:
f.seek(ptr)
while True:
line = f.readline()
if not line:
break
line = line.strip()
if line.startswith('['):
break
elif line == '':
continue
if is_s:
sections[s].append(line)
else:
if line.startswith(';'):
if version != '3': #v2
line = line.removeprefix(';')
if s == PATTERNS: # ;desc
pass
elif s == CURVES: # ;type: desc
curve_type_desc_line = line
continue
if s == PATTERNS:
tokens = line.split()
if tokens[1].upper() in pattern_v3_types: #v3
sql_batch.add(f"insert into _pattern (id) values ('{tokens[0]}');")
current_pattern = tokens[0]
if tokens[1].upper() == 'VARIABLE':
variable_patterns.append(tokens[0])
continue
if current_pattern != tokens[0]:
sql_batch.add(f"insert into _pattern (id) values ('{tokens[0]}');")
current_pattern = tokens[0]
elif s == CURVES:
tokens = line.split()
if tokens[1].upper() in curve_types: #v3
sql_batch.add(f"insert into _curve (id, type) values ('{tokens[0]}', '{tokens[1].upper()}');")
current_curve = tokens[0]
continue
if current_curve != tokens[0]:
type = curve_types[0]
if curve_type_desc_line != None:
type = curve_type_desc_line.split(':')[0].strip()
sql_batch.add(f"insert into _curve (id, type) values ('{tokens[0]}', '{type}');")
current_curve = tokens[0]
curve_type_desc_line = None
elif s== REGION:
tokens = line.split()
region_list[tokens[0]]=tokens[1]
elif s == BOUND:
tokens = line.split()
if(tokens[0]!=current_region and len(current_bound)>0):
#insert the previous region after get all the vertex of the attatched geometry
current_bound.append(current_bound[0])
current_geometry=to_postgis_polygon(current_bound)
region_type=map_regiontype[region_list[tokens[0]]]
sql_batch.add(f"insert into region(id, boundary,r_type) values ('{current_region}', '{current_geometry}','{region_type}');")
#start the new region
current_bound.clear()
vertex_point=(float(tokens[1]),float(tokens[2]))
current_bound.append(vertex_point)
current_region=tokens[0]
elif s==REGION_NODES:
tokens = line.split()
if(tokens[0]!=current_region and len(current_region_nodes)>0):
#insert the previous region after get all the vertex of the attatched geometry
sql_batch.add(get_insert_into_region_sql(current_region,current_region_nodes))
#start the new region
current_region_nodes.clear()
current_region_nodes.append(tokens[1])
current_region=tokens[0]
if s == JUNCTIONS:
sql_batch.add(handler(line, demand_outside))
elif s == PATTERNS:
sql_batch.add(handler(line, current_pattern not in variable_patterns))
elif s==BOUND or s==REGION_NODES:
continue
else:
sql_batch.add(handler(line))
f.seek(0)
if is_s:
if s == OPTIONS:
sql_batch.add(handler(sections[s], version))
else:
sql_batch.add(handler(sections[s]))
#need to insert the last region into database
if len(current_bound)>0:
current_bound.append(current_bound[0])
current_geometry=to_postgis_polygon(current_bound)
region_type=map_regiontype[region_list[current_region]]
sql_batch.add(f"insert into region(id, boundary,r_type) values ('{current_region}', '{current_geometry}','{region_type}');")
#reset the current region to none for the [REGION_NODES] session reading
#current_region=None
#need to insert the last region_nodes into database
if len(current_region_nodes)>0:
sql_batch.add(get_insert_into_region_sql(current_region,current_region_nodes))
#current_region=None
sql_batch.flush()
end = _print_time(f'End reading file "{inp}"')
print(f"Total (in second): {(end-start).seconds}(s)")
def get_insert_into_region_sql(region:str,nodes:list[str])->str:
str_sql=''
str_nodes = str(nodes).replace("'", "''")
r_type=region[0:region.index('_')]
if r_type == 'DMA' or r_type == 'SA' or r_type == 'VD':
table = ''
if r_type == 'DMA':
table = 'region_dma'
elif r_type == 'SA':
table = 'region_sa'
source=region[region.index('_')+1:]
str_sql=f"insert into region_sa(id,time_index,source,nodes) values ('{region}', 0,'{source}','{str_nodes}');"
elif r_type == 'VD':
table = 'region_vd'
return str_sql
def read_inp(project: str, inp: str, version: str = '3') -> bool:
if version != '3' and version != '2':
version = '2'
if is_project_open(project):
close_project(project)
if have_project(project):
delete_project(project)
create_project(project)
open_project(project)
parse_file(project, inp, version)
'''try:
parse_file(project, inp, version)
except:
close_project(project)
delete_project(project)
return False'''
close_project(project)
return True
#DingZQ, 2024-12-28, convert v3 to v2
def convert_inp_v3_to_v2(inp: str) -> ChangeSet:
project = 'v3Tov2'
if is_project_open(project):
close_project(project)
if have_project(project):
delete_project(project)
create_project(project)
open_project(project)
filename = f'inp/{project}_temp.inp'
if os.path.exists(filename):
os.remove(filename)
with open(filename, 'w') as f:
f.write(inp)
parse_file(project, filename, '3')
'''try:
parse_file(project, inp, version)
except:
close_project(project)
delete_project(project)
return False'''
return export_inp(project, '2')
def import_inp(project: str, cs: ChangeSet, version: str = '3') -> bool:
if version != '3' and version != '2':
version = '2'
if 'inp' not in cs.operations[0]:
return False
filename = f'inp/{project}_temp.inp'
if os.path.exists(filename):
os.remove(filename)
_print_time(f'Start writing temp file "{filename}"...')
with open(filename, 'w',encoding="GBK") as f:
f.write(str(cs.operations[0]['inp']))
_print_time(f'End writing temp file "{filename}"...')
result = read_inp(project, filename, version)
#os.remove(filename)
return result
+287
View File
@@ -0,0 +1,287 @@
import os
from .project_backup import *
from .database import ChangeSet
from .sections import *
from .s1_title import inp_out_title
from .s2_junctions import inp_out_junction
from .s3_reservoirs import inp_out_reservoir
from .s4_tanks import inp_out_tank
from .s5_pipes import inp_out_pipe
from .s6_pumps import inp_out_pump
from .s7_valves import inp_out_valve
from .s8_tags import inp_out_tag
from .s9_demands import inp_out_demand
from .s10_status import inp_out_status
from .s11_patterns import inp_out_pattern, inp_out_pattern_v3
from .s12_curves import inp_out_curve, inp_out_curve_v3
from .s13_controls import inp_out_control
from .s14_rules import inp_out_rule
from .s15_energy import inp_out_energy
from .s16_emitters import inp_out_emitter
from .s17_quality import inp_out_quality
from .s18_sources import inp_out_source
from .s19_reactions import inp_out_reaction
from .s20_mixing import inp_out_mixing
from .s21_times import inp_out_time
from .s22_report import inp_out_report
from .s23_options import inp_out_option
from .s23_options_v3 import inp_out_option_v3
from .s24_coordinates import inp_out_coord
from .s25_vertices import inp_out_vertex
from .s26_labels import inp_out_label
from .s27_backdrop import inp_out_backdrop
#from .s28_end import *
def dump_inp(project: str, inp: str, version: str = '3'):
if version != '3' and version != '2':
version = '2'
if not have_project(project):
return
project_open = is_project_open(project)
if not project_open:
open_project(project)
dir = os.getcwd()
path = os.path.join(dir, inp)
if os.path.exists(path):
os.remove(path)
file = open(path, mode='w',encoding="UTF-8")
# REGION, BOUND, REGION_NODES 在 epanet v2 中没有,是我们自己定制的
# v2 需要去掉我们自己定制的 section
sections = section_names_for_epanetv2
if version == '3':
sections = section_name
for name in sections:
if name == TITLE:
file.write(f'[{name}]\n')
else:
file.write(f'\n[{name}]\n')
if name == TITLE:
file.write('\n'.join(inp_out_title(project)))
elif name == JUNCTIONS: # + coords
file.write('\n'.join(inp_out_junction(project)))
elif name == RESERVOIRS: # + coords
file.write('\n'.join(inp_out_reservoir(project)))
elif name == TANKS: # + coords
file.write('\n'.join(inp_out_tank(project)))
elif name == PIPES:
file.write('\n'.join(inp_out_pipe(project)))
elif name == PUMPS:
file.write('\n'.join(inp_out_pump(project)))
elif name == VALVES:
file.write('\n'.join(inp_out_valve(project)))
elif name == TAGS:
file.write('\n'.join(inp_out_tag(project)))
elif name == DEMANDS:
file.write('\n'.join(inp_out_demand(project)))
elif name == STATUS:
file.write('\n'.join(inp_out_status(project)))
elif name == PATTERNS:
if version == '3':
file.write('\n'.join(inp_out_pattern_v3(project)))
else:
file.write('\n'.join(inp_out_pattern(project)))
elif name == CURVES:
if version == '3':
file.write('\n'.join(inp_out_curve_v3(project)))
else:
file.write('\n'.join(inp_out_curve(project)))
elif name == CONTROLS:
file.write('\n'.join(inp_out_control(project)))
elif name == RULES:
file.write('\n'.join(inp_out_rule(project)))
elif name == ENERGY:
file.write('\n'.join(inp_out_energy(project)))
elif name == EMITTERS:
file.write('\n'.join(inp_out_emitter(project)))
elif name == QUALITY:
file.write('\n'.join(inp_out_quality(project)))
elif name == SOURCES:
file.write('\n'.join(inp_out_source(project)))
elif name == REACTIONS:
file.write('\n'.join(inp_out_reaction(project)))
elif name == MIXING:
file.write('\n'.join(inp_out_mixing(project)))
elif name == TIMES:
file.write('\n'.join(inp_out_time(project)))
elif name == REPORT:
file.write('\n'.join(inp_out_report(project)))
elif name == OPTIONS:
if version == '3':
file.write('\n'.join(inp_out_option_v3(project)))
else:
file.write('\n'.join(inp_out_option(project)))
elif name == COORDINATES:
file.write('\n'.join(inp_out_coord(project)))
elif name == VERTICES:
file.write('\n'.join(inp_out_vertex(project)))
elif name == LABELS:
file.write('\n'.join(inp_out_label(project)))
elif name == BACKDROP:
file.write('\n'.join(inp_out_backdrop(project)))
elif name == END:
pass # :)
file.write('\n')
file.close()
if not project_open:
close_project(project)
def export_inp(project: str, version: str = '3') -> ChangeSet:
if version != '3' and version != '2':
version = '2'
if not have_project(project):
return ChangeSet()
project_open = is_project_open(project)
if not project_open:
open_project(project)
inp = ''
for name in section_name:
if name == TITLE:
inp += f'[{name}]\n'
else:
inp += f'\n[{name}]\n'
if name == TITLE:
inp += '\n'.join(inp_out_title(project))
elif name == JUNCTIONS: # + coords
inp += '\n'.join(inp_out_junction(project))
elif name == RESERVOIRS: # + coords
inp += '\n'.join(inp_out_reservoir(project))
elif name == TANKS: # + coords
inp += '\n'.join(inp_out_tank(project))
elif name == PIPES:
inp += '\n'.join(inp_out_pipe(project))
elif name == PUMPS:
inp += '\n'.join(inp_out_pump(project))
elif name == VALVES:
inp += '\n'.join(inp_out_valve(project))
elif name == TAGS:
inp += '\n'.join(inp_out_tag(project))
elif name == DEMANDS:
inp += '\n'.join(inp_out_demand(project))
elif name == STATUS:
inp += '\n'.join(inp_out_status(project))
elif name == PATTERNS:
if version == '3':
inp += '\n'.join(inp_out_pattern_v3(project))
else:
inp += '\n'.join(inp_out_pattern(project))
elif name == CURVES:
if version == '3':
inp += '\n'.join(inp_out_curve_v3(project))
else:
inp += '\n'.join(inp_out_curve(project))
elif name == CONTROLS:
inp += '\n'.join(inp_out_control(project))
elif name == RULES:
inp += '\n'.join(inp_out_rule(project))
elif name == ENERGY:
inp += '\n'.join(inp_out_energy(project))
elif name == EMITTERS:
inp += '\n'.join(inp_out_emitter(project))
elif name == QUALITY:
inp += '\n'.join(inp_out_quality(project))
elif name == SOURCES:
inp += '\n'.join(inp_out_source(project))
elif name == REACTIONS:
inp += '\n'.join(inp_out_reaction(project))
elif name == MIXING:
inp += '\n'.join(inp_out_mixing(project))
elif name == TIMES:
inp += '\n'.join(inp_out_time(project))
elif name == REPORT:
inp += '\n'.join(inp_out_report(project))
elif name == OPTIONS:
if version == '3':
inp += '\n'.join(inp_out_option_v3(project))
else:
inp += '\n'.join(inp_out_option(project))
elif name == COORDINATES:
inp += '\n'.join(inp_out_coord(project))
elif name == VERTICES:
inp += '\n'.join(inp_out_vertex(project))
elif name == LABELS:
inp += '\n'.join(inp_out_label(project))
elif name == BACKDROP:
inp += '\n'.join(inp_out_backdrop(project))
elif name == END:
pass # :)
inp += '\n'
if not project_open:
close_project(project)
return ChangeSet({'operation': 'export', 'inp': inp})
+36
View File
@@ -0,0 +1,36 @@
from dotenv import load_dotenv
import os
load_dotenv()
pg_name = os.getenv("DB_NAME")
pg_host = os.getenv("DB_HOST")
pg_port = os.getenv("DB_PORT")
pg_user = os.getenv("DB_USER")
pg_password = os.getenv("DB_PASSWORD")
def get_pgconn_string(
db_name=pg_name,
db_host=pg_host,
db_port=pg_port,
db_user=pg_user,
db_password=pg_password,
):
"""返回 PostgreSQL 连接字符串"""
return f"dbname={db_name} host={db_host} port={db_port} user={db_user} password={db_password}"
def get_pg_config():
"""返回 PostgreSQL 配置变量的字典"""
return {
"name": pg_name,
"host": pg_host,
"port": pg_port,
"user": pg_user,
}
def get_pg_password():
"""返回密码(谨慎使用)"""
return pg_password
+165
View File
@@ -0,0 +1,165 @@
import os
import psycopg as pg
from psycopg.rows import dict_row
from .connection import g_conn_dict as conn
from .postgresql_info import get_pgconn_string, get_pg_config, get_pg_password
# no undo/redo
_server_databases = ["template0", "template1", "postgres", "project"]
def list_project() -> list[str]:
ps = []
with pg.connect(conninfo=get_pgconn_string(), autocommit=True) as conn:
with conn.cursor(row_factory=dict_row) as cur:
for p in cur.execute(
f"select datname from pg_database where datname <> 'postgres' and datname <> 'template0' and datname <> 'template1' and datname <> 'project'"
):
ps.append(p["datname"])
return ps
def have_project(name: str) -> bool:
with pg.connect(conninfo=get_pgconn_string(), autocommit=True) as conn:
with conn.cursor() as cur:
cur.execute(f"select * from pg_database where datname = '{name}'")
return cur.rowcount > 0
def copy_project(source: str, new: str) -> None:
with pg.connect(conninfo=get_pgconn_string(), autocommit=True) as conn:
with conn.cursor() as cur:
cur.execute(f'create database "{new}" with template = {source}')
# 2025-02-07, WMH
# copyproject会把pg中operation这个表的全部内容也加进去,我们实际项目运行一周后operation这个表会变得特别大,导致CopyProject花费的时间很长,CopyProjectEx把operation的在复制时没有一块复制过去,节省时间
class CopyProjectEx:
@staticmethod
def create_database(connection, new_db):
with connection.cursor() as cursor:
cursor.execute(f'create database "{new_db}"')
connection.commit()
@staticmethod
def execute_pg_dump(source_db, exclude_table_list):
os.environ["PGPASSWORD"] = get_pg_password() # 设置密码环境变量
pg_config = get_pg_config()
host = pg_config["host"]
port = pg_config["port"]
user = pg_config["user"]
dump_command_structure = f"pg_dump -h {host} -p {port} -U {user} -F c -s -f source_db_structure.dump {source_db}"
os.system(dump_command_structure)
if exclude_table_list is not None:
exclude_table = " ".join(["-T {}".format(i) for i in exclude_table_list])
dump_command_db = f"pg_dump -h {host} -p {port} -U {user} -F c -a {exclude_table} -f source_db.dump {source_db}"
else:
dump_command_db = f"pg_dump -h {host} -p {port} -U {user} -F c -a -f source_db.dump {source_db}"
os.system(dump_command_db)
@staticmethod
def execute_pg_restore(new_db):
os.environ["PGPASSWORD"] = get_pg_password() # 设置密码环境变量
pg_config = get_pg_config()
host = pg_config["host"]
port = pg_config["port"]
user = pg_config["user"]
restore_command_structure = f"pg_restore -h {host} -p {port} -U {user} -d {new_db} source_db_structure.dump"
os.system(restore_command_structure)
restore_command_db = (
f"pg_restore -h {host} -p {port} -U {user} -d {new_db} source_db.dump"
)
os.system(restore_command_db)
@staticmethod
def init_operation_table(connection, excluded_table):
with connection.cursor() as cursor:
if "operation" in excluded_table:
insert_query = "insert into operation (id, redo, undo, redo_cs, undo_cs) values (0, '', '', '', '')"
cursor.execute(insert_query)
if "current_operation" in excluded_table:
insert_query = "insert into current_operation (id) values (0)"
cursor.execute(insert_query)
if "restore_operation" in excluded_table:
insert_query = "insert into restore_operation (id) values (0)"
cursor.execute(insert_query)
if "batch_operation" in excluded_table:
insert_query = "insert into batch_operation (id, redo, undo, redo_cs, undo_cs) values (0, '', '', '', '')"
cursor.execute(insert_query)
if "operation_table" in excluded_table:
insert_query = (
"insert into operation_table (option) values ('operation')"
)
cursor.execute(insert_query)
connection.commit()
def __call__(self, source: str, new_db: str, excluded_tables: [str] = None) -> None:
source_connection = pg.connect(conninfo=get_pgconn_string(), autocommit=True)
self.create_database(source_connection, new_db)
self.execute_pg_dump(source, excluded_tables)
self.execute_pg_restore(new_db)
source_connection.close()
new_db_connection = pg.connect(
conninfo=get_pgconn_string(db_name=new_db), autocommit=True
)
self.init_operation_table(new_db_connection, excluded_tables)
new_db_connection.close()
def create_project(name: str) -> None:
return copy_project("project", name)
def delete_project(name: str) -> None:
with pg.connect(conninfo=get_pgconn_string(), autocommit=True) as conn:
with conn.cursor() as cur:
cur.execute(
f"select pg_terminate_backend(pid) from pg_stat_activity where datname = '{name}'"
)
cur.execute(f'drop database "{name}"')
def clean_project(excluded: list[str] = []) -> None:
projects = list_project()
with pg.connect(conninfo=get_pgconn_string(), autocommit=True) as conn:
with conn.cursor(row_factory=dict_row) as cur:
row = cur.execute(f"select current_database()").fetchone()
if row != None:
current_db = row["current_database"]
if current_db in projects:
projects.remove(current_db)
for project in projects:
if project in _server_databases or project in excluded:
continue
cur.execute(
f"select pg_terminate_backend(pid) from pg_stat_activity where datname = '{project}'"
)
cur.execute(f'drop database "{project}"')
def open_project(name: str) -> None:
if name not in conn:
conn[name] = pg.connect(
conninfo=get_pgconn_string(db_name=name), autocommit=True
)
def is_project_open(name: str) -> bool:
return name in conn
def close_project(name: str) -> None:
if name in conn:
conn[name].close()
del conn[name]
+152
View File
@@ -0,0 +1,152 @@
import os
import psycopg as pg
from psycopg.rows import dict_row
from .connection import g_conn_dict as conn
from .postgresql_info import get_pgconn_string
# no undo/redo
_server_databases = ['template0', 'template1', 'postgres', 'project']
def list_project() -> list[str]:
ps = []
with pg.connect(conninfo=get_pgconn_string(), autocommit=True) as conn:
with conn.cursor(row_factory=dict_row) as cur:
for p in cur.execute(f"select datname from pg_database where datname <> 'postgres' and datname <> 'template0' and datname <> 'template1' and datname <> 'project'"):
ps.append(p['datname'])
return ps
def have_project(name: str) -> bool:
with pg.connect(conninfo=get_pgconn_string(db_name=name), autocommit=True) as conn:
with conn.cursor() as cur:
cur.execute(f"select * from pg_database where datname = '{name}'")
return cur.rowcount > 0
def copy_project(source: str, new: str) -> None:
with pg.connect(conninfo=get_pgconn_string(), autocommit=True) as conn:
with conn.cursor() as cur:
cur.execute(f'create database "{new}" with template = {source}')
# 2025-02-07, WMH
# copyproject会把pg中operation这个表的全部内容也加进去,我们实际项目运行一周后operation这个表会变得特别大,导致CopyProject花费的时间很长,CopyProjectEx把operation的在复制时没有一块复制过去,节省时间
class CopyProjectEx:
@ staticmethod
def create_database(connection, new_db):
with connection.cursor() as cursor:
cursor.execute(f'create database "{new_db}"')
connection.commit()
@staticmethod
def execute_pg_dump(hostname, source_db, exclude_table_list):
dump_command_structure = (
f'pg_dump -h {hostname} -F c -s -f source_db_structure.dump {source_db}'
)
os.system(dump_command_structure)
if exclude_table_list is not None:
exclude_table = ' '.join(['-T {}'.format(i) for i in exclude_table_list])
dump_command_db = (
f'pg_dump -h {hostname} -F c -a {exclude_table} -f source_db.dump {source_db}'
)
else:
dump_command_db = (
f'pg_dump -h {hostname} -F c -a -f source_db.dump {source_db}'
)
os.system(dump_command_db)
@staticmethod
def execute_pg_restore(hostname, new_db):
restore_command_structure = (
f'pg_restore -h {hostname} -d {new_db} source_db_structure.dump'
)
os.system(restore_command_structure)
restore_command_db = (
f'pg_restore -h {hostname} -d {new_db} source_db.dump'
)
os.system(restore_command_db)
@staticmethod
def init_operation_table(connection, excluded_table):
with connection.cursor() as cursor:
if 'operation' in excluded_table:
insert_query \
= "insert into operation (id, redo, undo, redo_cs, undo_cs) values (0, '', '', '', '')"
cursor.execute(insert_query)
if 'current_operation' in excluded_table:
insert_query \
= "insert into current_operation (id) values (0)"
cursor.execute(insert_query)
if 'restore_operation' in excluded_table:
insert_query \
= "insert into restore_operation (id) values (0)"
cursor.execute(insert_query)
if 'batch_operation' in excluded_table:
insert_query \
= "insert into batch_operation (id, redo, undo, redo_cs, undo_cs) values (0, '', '', '', '')"
cursor.execute(insert_query)
if 'operation_table' in excluded_table:
insert_query \
= "insert into operation_table (option) values ('operation')"
cursor.execute(insert_query)
connection.commit()
def __call__(self, source: str, new: str, excluded_table: [str] = None) -> None:
connection = pg.connect(conninfo=get_pgconn_string(), autocommit=True)
self.create_database(connection, new)
self.execute_pg_dump('127.0.0.1', source, excluded_table)
self.execute_pg_restore('127.0.0.1', new)
connection = pg.connect(conninfo=get_pgconn_string(db_name=new), autocommit=True)
self.init_operation_table(connection, excluded_table)
def create_project(name: str) -> None:
return copy_project('project', name)
def delete_project(name: str) -> None:
with pg.connect(conninfo=get_pgconn_string(), autocommit=True) as conn:
with conn.cursor() as cur:
cur.execute(f"select pg_terminate_backend(pid) from pg_stat_activity where datname = '{name}'")
cur.execute(f'drop database "{name}"')
def clean_project(excluded: list[str] = []) -> None:
projects = list_project()
with pg.connect(conninfo=get_pgconn_string(), autocommit=True) as conn:
with conn.cursor(row_factory=dict_row) as cur:
row = cur.execute(f"select current_database()").fetchone()
if row != None:
current_db = row['current_database']
if current_db in projects:
projects.remove(current_db)
for project in projects:
if project in _server_databases or project in excluded:
continue
cur.execute(f"select pg_terminate_backend(pid) from pg_stat_activity where datname = '{project}'")
cur.execute(f'drop database "{project}"')
def open_project(name: str) -> None:
if name not in conn:
conn[name] = pg.connect(conninfo=get_pgconn_string(db_name=name), autocommit=True)
def is_project_open(name: str) -> bool:
return name in conn
def close_project(name: str) -> None:
if name in conn:
conn[name].close()
del conn[name]
+262
View File
@@ -0,0 +1,262 @@
from psycopg.rows import dict_row, Row
from .connection import g_conn_dict as conn
from .database import read
from typing import Any
_NODE = '_node'
_LINK = '_link'
_CURVE = '_curve'
_PATTERN = '_pattern'
_REGION = '_region'
JUNCTION = 'junction'
RESERVOIR = 'reservoir'
TANK = 'tank'
PIPE = 'pipe'
PUMP = 'pump'
VALVE = 'valve'
PATTERN = 'pattern'
CURVE = 'curve'
REGION = 'region'
# DingZQ, 2025-02-05
'''
C++ 代码里已经定义了这些 enum 值
{
kNothing = -1,
//Node
kReservoir = 0,
kTank,
kJunction,
//Link
kPipe,
kPump,
kValve,
'''
ELEMENT_TYPES : dict[str, int] = {
RESERVOIR : 0,
TANK : 1,
JUNCTION : 2,
PIPE : 3,
PUMP : 4,
VALVE : 5,
}
def _get_from(name: str, id: str, base_type: str) -> Row | None:
with conn[name].cursor(row_factory=dict_row) as cur:
cur.execute(f"select * from {base_type} where id = '{id}'")
return cur.fetchone()
def is_node(name: str, id: str) -> bool:
return _get_from(name, id, _NODE) != None
def is_junction(name: str, id: str) -> bool:
row = _get_from(name, id, _NODE)
return row != None and row['type'] == JUNCTION
def is_reservoir(name: str, id: str) -> bool:
row = _get_from(name, id, _NODE)
return row != None and row['type'] == RESERVOIR
def is_tank(name: str, id: str) -> bool:
row = _get_from(name, id, _NODE)
return row != None and row['type'] == TANK
def is_link(name: str, id: str) -> bool:
return _get_from(name, id, _LINK) != None
def is_pipe(name: str, id: str) -> bool:
row = _get_from(name, id, _LINK)
return row != None and row['type'] == PIPE
def is_pump(name: str, id: str) -> bool:
row = _get_from(name, id, _LINK)
return row != None and row['type'] == PUMP
def is_valve(name: str, id: str) -> bool:
row = _get_from(name, id, _LINK)
return row != None and row['type'] == VALVE
# DingZQ, 2025-02-05
def get_node_type(name: str, node_id: str) -> str:
row = _get_from(name, node_id, _NODE)
return row['type']
def get_link_type(name: str, link_id: str) -> str:
row = _get_from(name, link_id, _LINK)
return row['type']
def get_element_type(name: str, element_id: str) -> str:
if is_node(name, element_id):
return get_node_type(name, element_id)
elif is_link(name, element_id):
return get_link_type(name, element_id)
else:
return None
def get_element_type_value(name: str, element_id: str) -> int:
return ELEMENT_TYPES[get_element_type(name, element_id)]
def is_curve(name: str, id: str) -> bool:
return _get_from(name, id, _CURVE) != None
def is_pattern(name: str, id: str) -> bool:
return _get_from(name, id, _PATTERN) != None
def is_region(name: str, id: str) -> bool:
return _get_from(name, id, _REGION) != None
def _get_all(name: str, base_type: str) -> list[str]:
ids : list[str] = []
with conn[name].cursor(row_factory=dict_row) as cur:
cur.execute(f"select id from {base_type} order by id")
for record in cur:
ids.append(record['id'])
return ids
def get_nodes(name: str) -> list[str]:
return _get_all(name, _NODE)
# DingZQ
def _get_nodes_by_type(name: str, type: str) -> list[str]:
ids : list[str] = []
with conn[name].cursor(row_factory=dict_row) as cur:
cur.execute(f"select id from {_NODE} where type = '{type}' order by id")
for record in cur:
ids.append(record['id'])
return ids
# DingZQ
def get_nodes_id_and_type(name: str) -> dict[str, str]:
nodes_id_and_type: dict[str, str] = {}
with conn[name].cursor(row_factory=dict_row) as cur:
cur.execute(f"select id, type from {_NODE} order by id")
for record in cur:
nodes_id_and_type[record['id']] = record['type']
return nodes_id_and_type
# DingZQ 2024-12-31
def get_major_nodes(name: str, diameter: int) -> list[str]:
major_nodes_set = set()
with conn[name].cursor(row_factory=dict_row) as cur:
cur.execute(f"select node1, node2 from pipes where diameter > {diameter}")
for record in cur:
major_nodes_set.add(record['node1'])
major_nodes_set.add(record['node2'])
return list(major_nodes_set)
# DingZQs
def get_junctions(name: str) -> list[str]:
return _get_nodes_by_type(name, JUNCTION)
# DingZQ
def get_reservoirs(name: str) -> list[str]:
return _get_nodes_by_type(name, RESERVOIR)
# DingZQ
def get_tanks(name: str) -> list[str]:
return _get_nodes_by_type(name, TANK)
# DingZQ
def get_links(name: str) -> list[str]:
return _get_all(name, _LINK)
# DingZQ
def _get_links_by_type(name: str, type: str) -> list[str]:
ids : list[str] = []
with conn[name].cursor(row_factory=dict_row) as cur:
cur.execute(f"select id from {_LINK} where type = '{type}' order by id")
for record in cur:
ids.append(record['id'])
return ids
# DingZQ
def get_links_id_and_type(name: str) -> dict[str, str]:
links_id_and_type: dict[str, str] = {}
with conn[name].cursor(row_factory=dict_row) as cur:
cur.execute(f"select id, type from {_LINK} order by id")
for record in cur:
links_id_and_type[record['id']] = record['type']
return links_id_and_type
# DingZQ 2024-12-31
# 获取直径大于800的管道
def get_major_pipes(name: str, diameter: int) -> list[str]:
major_pipe_ids: list[str] = []
with conn[name].cursor(row_factory=dict_row) as cur:
cur.execute(f"select id from pipes where diameter > {diameter} order by id")
for record in cur:
major_pipe_ids.append(record['id'])
return major_pipe_ids
# DingZQ
def get_pipes(name: str) -> list[str]:
return _get_links_by_type(name, PIPE)
# DingZQ
def get_pumps(name: str) -> list[str]:
return _get_links_by_type(name, PUMP)
# DingZQ
def get_valves(name: str) -> list[str]:
return _get_links_by_type(name, VALVE)
def get_curves(name: str) -> list[str]:
return _get_all(name, _CURVE)
def get_patterns(name: str) -> list[str]:
return _get_all(name, _PATTERN)
def get_regions(name: str) -> list[str]:
return _get_all(name, _REGION)
def get_node_links(name: str, id: str) -> list[str]:
with conn[name].cursor(row_factory=dict_row) as cur:
links: list[str] = []
for p in cur.execute(f"select id from pipes where node1 = '{id}' or node2 = '{id}'").fetchall():
links.append(p['id'])
for p in cur.execute(f"select id from pumps where node1 = '{id}' or node2 = '{id}'").fetchall():
links.append(p['id'])
for p in cur.execute(f"select id from valves where node1 = '{id}' or node2 = '{id}'").fetchall():
links.append(p['id'])
return links
def get_link_nodes(name: str, id: str) -> list[str]:
row = {}
if is_pipe(name, id):
row = read(name, f"select node1, node2 from pipes where id = '{id}'")
elif is_pump(name, id):
row = read(name, f"select node1, node2 from pumps where id = '{id}'")
elif is_valve(name, id):
row = read(name, f"select node1, node2 from valves where id = '{id}'")
return [str(row['node1']), str(row['node2'])]
def get_region_type(name: str, id: str)->str:
if(is_region(name,id)):
type = read(name, f"select type from _region where id = '{id}'")
return type
+110
View File
@@ -0,0 +1,110 @@
from .database import *
LINK_STATUS_OPEN = 'OPEN'
LINK_STATUS_CLOSED = 'CLOSED'
LINK_STATUS_ACTIVE = 'ACTIVE'
def get_status_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'link' : {'type': 'str' , 'optional': False , 'readonly': True },
'status' : {'type': 'str' , 'optional': True , 'readonly': False},
'setting' : {'type': 'float' , 'optional': True , 'readonly': False} }
def get_status(name: str, link: str) -> dict[str, Any]:
s = try_read(name, f"select * from status where link = '{link}'")
if s == None:
return { 'link': link, 'status': None, 'setting': None }
d = {}
d['link'] = str(s['link'])
d['status'] = str(s['status']) if s['status'] != None else None
d['setting'] = float(s['setting']) if s['setting'] != None else None
return d
class Status(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'status'
self.link = str(input['link'])
self.status = str(input['status']) if 'status' in input and input['status'] != None else None
self.setting = float(input['setting']) if 'setting' in input and input['setting'] != None else None
self.f_type = f"'{self.type}'"
self.f_link = f"'{self.link}'"
self.f_status = f"'{self.status}'" if self.status != None else 'null'
self.f_setting = self.setting if self.setting != None else 'null'
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'link': self.link, 'status': self.status, 'setting': self.setting }
def _set_status(name: str, cs: ChangeSet) -> DbChangeSet:
old = Status(get_status(name, cs.operations[0]['link']))
raw_new = get_status(name, cs.operations[0]['link'])
new_dict = cs.operations[0]
schema = get_status_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = Status(raw_new)
redo_sql = f"delete from status where link = {new.f_link};"
if new.status != None or new.setting != None:
redo_sql += f"\ninsert into status (link, status, setting) values ({new.f_link}, {new.f_status}, {new.f_setting});"
undo_sql = f"delete from status where link = {old.f_link};"
if old.status != None or old.setting != None:
undo_sql += f"\ninsert into status (link, status, setting) values ({old.f_link}, {old.f_status}, {old.f_setting});"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_status(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_status(name, cs))
#--------------------------------------------------------------
# [EPA2][EPA3][IN][OUT]
# link value
#--------------------------------------------------------------
def inp_in_status(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
link = str(tokens[0])
value = tokens[1].upper()
if value == LINK_STATUS_OPEN or value == LINK_STATUS_CLOSED or value == LINK_STATUS_ACTIVE:
return str(f"insert into status (link, status, setting) values ('{link}', '{value}', null);")
else:
return str(f"insert into status (link, status, setting) values ('{link}', null, {float(value)});")
def inp_out_status(name: str) -> list[str]:
lines = []
objs = read_all(name, 'select * from status')
for obj in objs:
link = obj['link']
status = obj['status'] if obj['status'] != None else ''
setting = obj['setting'] if obj['setting'] != None else ''
if status != '':
lines.append(f'{link} {status}')
if setting != '':
lines.append(f'{link} {setting}')
return lines
def delete_status_by_link(name: str, link: str) -> ChangeSet:
row = try_read(name, f"select * from status where link = '{link}'")
if row == None:
return ChangeSet()
return ChangeSet(g_update_prefix | {'type': 'status', 'link': link, 'status': None, 'setting': None})
+163
View File
@@ -0,0 +1,163 @@
from .database import *
PATTERN_V3_TYPE_FIXED = 'FIXED'
PATTERN_V3_TYPE_VARIABLE = 'VARIABLE'
pattern_v3_types = [PATTERN_V3_TYPE_FIXED, PATTERN_V3_TYPE_VARIABLE]
def get_pattern_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'factors' : {'type': 'float_list' , 'optional': False , 'readonly': False } }
def get_pattern(name: str, id: str) -> dict[str, Any]:
p_one = try_read(name, f"select * from _pattern where id = '{id}'")
if p_one == None:
return {}
pas = read_all(name, f"select * from patterns where id = '{id}' order by _order")
ps = []
for r in pas:
ps.append(float(r['factor']))
return { 'id': id, 'factors': ps }
def _set_pattern(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
f_id = f"'{id}'"
old = get_pattern(name, id)
new = { 'id': id }
if 'factors' in cs.operations[0]:
new['factors'] = cs.operations[0]['factors']
else:
new['factors'] = old['factors']
# TODO: transaction ?
redo_sql = f"delete from patterns where id = {f_id};"
for f_factor in new['factors']:
redo_sql += f"\ninsert into patterns (id, factor) values ({f_id}, {f_factor});"
undo_sql = f"delete from patterns where id = {f_id};"
for f_factor in old['factors']:
undo_sql += f"\ninsert into patterns (id, factor) values ({f_id}, {f_factor});"
redo_cs = g_update_prefix | { 'type': 'pattern' } | new
undo_cs = g_update_prefix | { 'type': 'pattern' } | old
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_pattern(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_pattern(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _set_pattern(name, cs))
def _add_pattern(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
f_id = f"'{id}'"
new = { 'id': id, 'factors': cs.operations[0]['factors'] }
# TODO: transaction ?
redo_sql = f"insert into _pattern (id) values ({f_id});"
for f_factor in new['factors']:
redo_sql += f"\ninsert into patterns (id, factor) values ({f_id}, {f_factor});"
undo_sql = f"delete from patterns where id = {f_id};"
undo_sql += f"\ndelete from _pattern where id = {f_id};"
redo_cs = g_add_prefix | { 'type': 'pattern' } | new
undo_cs = g_delete_prefix | { 'type': 'pattern' } | { 'id': id }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_pattern(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_pattern(name, cs.operations[0]['id']) != {}:
return ChangeSet()
return execute_command(name, _add_pattern(name, cs))
def _delete_pattern(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
f_id = f"'{id}'"
old = get_pattern(name, id)
redo_sql = f"delete from patterns where id = {f_id};"
redo_sql += f"\ndelete from _pattern where id = {f_id};"
# TODO: transaction ?
undo_sql = f"insert into _pattern (id) values ({f_id});"
for f_factor in old['factors']:
undo_sql += f"\ninsert into patterns (id, factor) values ({f_id}, {f_factor});"
redo_cs = g_delete_prefix | { 'type': 'pattern' } | { 'id': id }
undo_cs = g_add_prefix | { 'type': 'pattern' } | old
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_pattern(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_pattern(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _delete_pattern(name, cs))
#--------------------------------------------------------------
# [EPA2][IN][OUT]
# ;desc
# id mult1 mult2 .....
#--------------------------------------------------------------
#--------------------------------------------------------------
# [EPA3][IN][OUT]
# id FIXED (interval)
# id factor1 factor2 ...
# id VARIABLE
# id time1 factor1 time2 factor2 ...
#--------------------------------------------------------------
def inp_in_pattern(line: str, fixed: bool = True) -> str:
tokens = line.split()
sql = ''
if fixed:
for token in tokens[1:]:
sql += f"insert into patterns (id, factor) values ('{tokens[0]}', {float(token)});"
else:
for token in tokens[1::2]:
sql += f"insert into patterns (id, factor) values ('{tokens[0]}', {float(token)});"
return sql
def inp_out_pattern(name: str) -> list[str]:
lines = []
objs = read_all(name, f"select * from patterns order by _order")
for obj in objs:
id = obj['id']
factor = obj['factor']
lines.append(f'{id} {factor}')
return lines
def inp_out_pattern_v3(name: str) -> list[str]:
lines = []
objs = read_all(name, f"select * from patterns order by _order")
ids = []
for obj in objs:
id = obj['id']
if id not in ids:
# for EPA3, ignore time of variable pattern...
lines.append(f'{id} FIXED')
ids.append(id)
factor = obj['factor']
lines.append(f'{id} {factor}')
return lines
+186
View File
@@ -0,0 +1,186 @@
from .database import *
CURVE_TYPE_PUMP = 'PUMP'
CURVE_TYPE_EFFICIENCY = 'EFFICIENCY'
CURVE_TYPE_VOLUME = 'VOLUME'
CURVE_TYPE_HEADLOSS = 'HEADLOSS'
curve_types = [CURVE_TYPE_PUMP, CURVE_TYPE_EFFICIENCY, CURVE_TYPE_VOLUME, CURVE_TYPE_HEADLOSS]
def get_curve_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'c_type' : {'type': 'str' , 'optional': False , 'readonly': False},
'coords' : {'type': 'list' , 'optional': False , 'readonly': False,
'element': { 'x' : {'type': 'float' , 'optional': False , 'readonly': False },
'y' : {'type': 'float' , 'optional': False , 'readonly': False } }}}
def get_curve(name: str, id: str) -> dict[str, Any]:
c_one = try_read(name, f"select * from _curve where id = '{id}'")
if c_one == None:
return {}
cus = read_all(name, f"select * from curves where id = '{id}' order by _order")
cs = []
for r in cus:
cs.append({ 'x': float(r['x']), 'y': float(r['y']) })
d = {}
d['id'] = id
d['c_type'] = c_one['type']
d['coords'] = cs
return d
def _set_curve(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
f_id = f"'{id}'"
old = get_curve(name, id)
old_f_type = f"'{old['c_type']}'"
new = { 'id': id }
if 'coords' in cs.operations[0]:
new['coords'] = cs.operations[0]['coords']
else:
new['coords'] = old['coords']
if 'c_type' in cs.operations[0]:
new['c_type'] = cs.operations[0]['c_type']
else:
new['c_type'] = old['c_type']
new_f_type = f"'{new['c_type']}'"
# TODO: transaction ?
redo_sql = f"delete from curves where id = {f_id};"
redo_sql += f"\nupdate _curve set type = {new_f_type} where id = {f_id};"
for xy in new['coords']:
f_x, f_y = xy['x'], xy['y']
redo_sql += f"\ninsert into curves (id, x, y) values ({f_id}, {f_x}, {f_y});"
undo_sql = f"delete from curves where id = {f_id};"
undo_sql += f"\nupdate _curve set type = {old_f_type} where id = {f_id};"
for xy in old['coords']:
f_x, f_y = xy['x'], xy['y']
undo_sql += f"\ninsert into curves (id, x, y) values ({f_id}, {f_x}, {f_y});"
redo_cs = g_update_prefix | { 'type': 'curve' } | new
undo_cs = g_update_prefix | { 'type': 'curve' } | old
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_curve(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_curve(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _set_curve(name, cs))
def _add_curve(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
f_id = f"'{id}'"
new = { 'id': id, 'c_type': cs.operations[0]['c_type'], 'coords': [] }
new_f_type = f"'{new['c_type']}'"
# TODO: transaction ?
redo_sql = f"insert into _curve (id, type) values ({f_id}, {new_f_type});"
for xy in cs.operations[0]['coords']:
x, y = float(xy['x']), float(xy['y'])
f_x, f_y = x, y
redo_sql += f"\ninsert into curves (id, x, y) values ({f_id}, {f_x}, {f_y});"
new['coords'].append({ 'x': x, 'y': y })
undo_sql = f"delete from curves where id = {f_id};"
undo_sql += f"\ndelete from _curve where id = {f_id};"
redo_cs = g_add_prefix | { 'type': 'curve' } | new
undo_cs = g_delete_prefix | { 'type': 'curve' } | { 'id' : id }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_curve(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_curve(name, cs.operations[0]['id']) != {}:
return ChangeSet()
return execute_command(name, _add_curve(name, cs))
def _delete_curve(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
f_id = f"'{id}'"
old = get_curve(name, id)
old_f_type = f"'{old['c_type']}'"
redo_sql = f"delete from curves where id = {f_id};"
redo_sql += f"\ndelete from _curve where id = {f_id};"
# TODO: transaction ?
undo_sql = f"insert into _curve (id, type) values ({f_id}, {old_f_type});"
for xy in old['coords']:
f_x, f_y = xy['x'], xy['y']
undo_sql += f"\ninsert into curves (id, x, y) values ({f_id}, {f_x}, {f_y});"
redo_cs = g_delete_prefix | { 'type': 'curve' } | { 'id' : id }
undo_cs = g_add_prefix | { 'type': 'curve' } | old
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_curve(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_curve(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _delete_curve(name, cs))
#--------------------------------------------------------------
# [EPA2][IN][OUT]
# ;type: desc
# id x y
#--------------------------------------------------------------
#--------------------------------------------------------------
# [EPA3][IN][OUT]
# id type
# id x y
#--------------------------------------------------------------
def inp_in_curve(line: str) -> str:
tokens = line.split()
return str(f"insert into curves (id, x, y) values ('{tokens[0]}', {float(tokens[1])}, {float(tokens[2])});")
def inp_out_curve(name: str) -> list[str]:
lines = []
types = read_all(name, f"select * from _curve")
for type in types:
id = type['id']
# ;type: desc
lines.append(f";{type['type']}:")
objs = read_all(name, f"select * from curves where id = '{id}' order by _order")
for obj in objs:
id = obj['id']
x = obj['x']
y = obj['y']
lines.append(f'{id} {x} {y}')
return lines
def inp_out_curve_v3(name: str) -> list[str]:
lines = []
types = read_all(name, f"select * from _curve")
for type in types:
id = type['id']
# id type
lines.append(f"{id} {type['type']}")
objs = read_all(name, f"select * from curves where id = '{id}' order by _order")
for obj in objs:
id = obj['id']
x = obj['x']
y = obj['y']
lines.append(f'{id} {x} {y}')
return lines
+52
View File
@@ -0,0 +1,52 @@
from .database import *
def get_control_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'controls' : {'type': 'str_list' , 'optional': False , 'readonly': False} }
def get_control(name: str) -> dict[str, Any]:
cs = read_all(name, f"select * from controls")
ds = []
for c in cs:
ds.append(c['line'])
return { 'controls': ds }
def _set_control(name: str, cs: ChangeSet) -> DbChangeSet:
old = get_control(name)
redo_sql = 'delete from controls;'
for line in cs.operations[0]['controls']:
redo_sql += f"\ninsert into controls (line) values ('{line}');"
undo_sql = 'delete from controls;'
for line in old['controls']:
undo_sql += f"\ninsert into controls (line) values ('{line}');"
redo_cs = g_update_prefix | { 'type': 'control', 'controls': cs.operations[0]['controls'] }
undo_cs = g_update_prefix | { 'type': 'control', 'controls': old['controls'] }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_control(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_control(name, cs))
#--------------------------------------------------------------
# [EPA2][EPA3]
# LINK linkID setting IF NODE nodeID {BELOW/ABOVE} level
# LINK linkID setting AT TIME value (units)
# LINK linkID setting AT CLOCKTIME value (units)
# (0) (1) (2) (3) (4) (5) (6) (7)
# todo...
#--------------------------------------------------------------
def inp_in_control(line: str) -> str:
return str(f"insert into controls (line) values ('{line}');")
def inp_out_control(name: str) -> list[str]:
return get_control(name)['controls']
+48
View File
@@ -0,0 +1,48 @@
from .database import *
def get_rule_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'rules' : {'type': 'str_list' , 'optional': False , 'readonly': False} }
def get_rule(name: str) -> dict[str, Any]:
cs = read_all(name, f"select * from rules")
ds = []
for c in cs:
ds.append(c['line'])
return { 'rules': ds }
def _set_rule(name: str, cs: ChangeSet) -> DbChangeSet:
old = get_rule(name)
redo_sql = 'delete from rules;'
for line in cs.operations[0]['rules']:
redo_sql += f"\ninsert into rules (line) values ('{line}');"
undo_sql = 'delete from rules;'
for line in old['rules']:
undo_sql += f"\ninsert into rules (line) values ('{line}');"
redo_cs = g_update_prefix | { 'type': 'rule', 'rules': cs.operations[0]['rules'] }
undo_cs = g_update_prefix | { 'type': 'rule', 'rules': old['rules'] }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_rule(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_rule(name, cs))
#--------------------------------------------------------------
# [EPA2][EPA3]
# TODO...
#--------------------------------------------------------------
def inp_in_rule(line: str) -> str:
return str(f"insert into rules (line) values ('{line}');")
def inp_out_rule(name: str) -> list[str]:
return get_rule(name)['rules']
+240
View File
@@ -0,0 +1,240 @@
from .database import *
element_schema = {'type': 'str' , 'optional': True , 'readonly': False}
def get_energy_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'GLOBAL PRICE' : element_schema,
'GLOBAL PATTERN' : element_schema,
'GLOBAL EFFIC' : element_schema,
'DEMAND CHARGE' : element_schema }
def get_energy(name: str) -> dict[str, Any]:
ts = read_all(name, f"select * from energy")
d = {}
for e in ts:
d[e['key']] = str(e['value'])
return d
def _set_energy(name: str, cs: ChangeSet) -> DbChangeSet:
raw_old = get_energy(name)
old = {}
new = {}
new_dict = cs.operations[0]
schema = get_energy_schema(name)
for key in schema.keys():
if key in new_dict:
old[key] = str(raw_old[key])
new[key] = str(new_dict[key])
redo_cs = g_update_prefix | { 'type' : 'energy' }
redo_sql = ''
for key, value in new.items():
if redo_sql != '':
redo_sql += '\n'
redo_sql += f"update energy set value = '{value}' where key = '{key}';"
redo_cs |= { key: value }
undo_cs = g_update_prefix | { 'type' : 'energy' }
undo_sql = ''
for key, value in old.items():
if undo_sql != '':
undo_sql += '\n'
undo_sql += f"update energy set value = '{value}' where key = '{key}';"
undo_cs |= { key: value }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_energy(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_energy(name, cs))
def get_pump_energy_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'pump' : {'type': 'str' , 'optional': False , 'readonly': True },
'price' : {'type': 'float' , 'optional': True , 'readonly': False},
'pattern' : {'type': 'str' , 'optional': True , 'readonly': False},
'effic' : {'type': 'str' , 'optional': True , 'readonly': False} }
def get_pump_energy(name: str, pump: str) -> dict[str, Any]:
d = {}
d['pump'] = pump
pe = try_read(name, f"select * from energy_pump_price where pump = '{pump}'")
d['price'] = float(pe['price']) if pe != None else None
pe = try_read(name, f"select * from energy_pump_pattern where pump = '{pump}'")
d['pattern'] = str(pe['pattern']) if pe != None else None
pe = try_read(name, f"select * from energy_pump_effic where pump = '{pump}'")
d['effic'] = str(pe['effic']) if pe != None else None
return d
class PumpEnergy(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'pump_energy'
self.pump = str(input['pump'])
self.price = float(input['price']) if 'price' in input and input['price'] != None else None
self.pattern = str(input['pattern']) if 'pattern' in input and input['pattern'] != None else None
self.effic = str(input['effic']) if 'effic' in input and input['effic'] != None else None
self.f_type = f"'{self.type}'"
self.f_pump = f"'{self.pump}'"
self.f_price = self.price if self.price != None else 'null'
self.f_pattern = f"'{self.pattern}'" if self.pattern != None else 'null'
self.f_effic = f"'{self.effic}'" if self.effic != None else 'null'
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'pump': self.pump, 'price': self.price, 'pattern': self.pattern, 'effic': self.effic }
def _set_pump_energy(name: str, cs: ChangeSet) -> DbChangeSet:
old = PumpEnergy(get_pump_energy(name, cs.operations[0]['pump']))
raw_new = get_pump_energy(name, cs.operations[0]['pump'])
new_dict = cs.operations[0]
schema = get_pump_energy_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = PumpEnergy(raw_new)
redo_sql = f"delete from energy_pump_price where pump = {new.f_pump};\ndelete from energy_pump_pattern where pump = {new.f_pump};\ndelete from energy_pump_effic where pump = {new.f_pump};"
if new.price != None:
redo_sql += f"\ninsert into energy_pump_price (pump, price) values ({new.f_pump}, {new.f_price});"
if new.pattern != None:
redo_sql += f"\ninsert into energy_pump_pattern (pump, pattern) values ({new.f_pump}, {new.f_pattern});"
if new.effic != None:
redo_sql += f"\ninsert into energy_pump_effic (pump, effic) values ({new.f_pump}, {new.f_effic});"
undo_sql = f"delete from energy_pump_price where pump = {old.f_pump};\ndelete from energy_pump_pattern where pump = {old.f_pump};\ndelete from energy_pump_effic where pump = {old.f_pump};"
if old.price != None:
undo_sql += f"\ninsert into energy_pump_price (pump, price) values ({old.f_pump}, {old.f_price});"
if old.pattern != None:
undo_sql += f"\ninsert into energy_pump_pattern (pump, pattern) values ({old.f_pump}, {old.f_pattern});"
if old.effic != None:
undo_sql += f"\ninsert into energy_pump_effic (pump, effic) values ({old.f_pump}, {old.f_effic});"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_pump_energy(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_pump_energy(name, cs))
#--------------------------------------------------------------
# [EPA2][EPA3][IN][OUT]
# GLOBAL {PRICE/PATTERN/EFFIC} value
# PUMP id {PRICE/PATTERN/EFFIC} value
# DEMAND CHARGE value
#--------------------------------------------------------------
def inp_in_energy(line: str) -> str:
tokens = line.split()
if tokens[0].upper() == 'PUMP':
pump = tokens[1]
key = tokens[2].lower()
value = tokens[3]
if key == 'price':
value = float(value)
else:
value = f"'{value}'"
if key == 'efficiency':
key = 'effic'
return str(f"insert into energy_pump_{key} (pump, {key}) values ('{pump}', {value});")
else:
line = line.upper().strip()
for key in get_energy_schema('').keys():
if line.startswith(key):
value = line.removeprefix(key).strip()
# exception here
if line.startswith('GLOBAL EFFICIENCY'):
value = line.removeprefix('GLOBAL EFFICIENCY').strip()
return str(f"update energy set value = '{value}' where key = '{key}';")
return str('')
def inp_out_energy(name: str) -> list[str]:
lines = []
objs = read_all(name, f"select * from energy")
for obj in objs:
key = obj['key']
value = obj['value']
if value.strip() != '':
lines.append(f'{key} {value}')
objs = read_all(name, f"select * from energy_pump_price")
for obj in objs:
pump = obj['pump']
value = obj['price']
lines.append(f'PUMP {pump} PRICE {value}')
objs = read_all(name, f"select * from energy_pump_pattern")
for obj in objs:
pump = obj['pump']
value = obj['pattern']
lines.append(f'PUMP {pump} PATTERN {value}')
objs = read_all(name, f"select * from energy_pump_effic")
for obj in objs:
pump = obj['pump']
value = obj['effic']
lines.append(f'PUMP {pump} EFFIC {value}')
return lines
def delete_pump_energy_by_pump(name: str, pump: str) -> ChangeSet:
row1 = try_read(name, f"select * from energy_pump_price where pump = '{pump}'")
row2 = try_read(name, f"select * from energy_pump_pattern where pump = '{pump}'")
row3 = try_read(name, f"select * from energy_pump_effic where pump = '{pump}'")
if row1 == None and row2 == None and row3 == None:
return ChangeSet()
return ChangeSet(g_update_prefix | {'type': 'pump_energy', 'pump' : pump, 'price': None, 'pattern': None, 'effic': None})
def unset_pump_energy_by_pattern(name: str, pattern: str) -> ChangeSet:
cs = ChangeSet()
rows = read_all(name, f"select * from energy_pump_pattern where pattern = '{pattern}'")
for row in rows:
pump = row['pump']
row1 = try_read(name, f"select * from energy_pump_price where pump = '{pump}'")
price = float(row1['price']) if row1 != None else None
row2 = try_read(name, f"select * from energy_pump_effic where pump = '{pump}'")
effic = str(row2['effic']) if row2 != None else None
cs.append(g_update_prefix | {'type': 'pump_energy', 'pump' : pump, 'price': price, 'pattern': None, 'effic': effic})
return cs
def unset_pump_energy_by_curve(name: str, curve: str) -> ChangeSet:
cs = ChangeSet()
rows = read_all(name, f"select * from energy_pump_effic where effic = '{curve}'")
for row in rows:
pump = row['pump']
row1 = try_read(name, f"select * from energy_pump_price where pump = '{pump}'")
price = float(row1['price']) if row1 != None else None
row2 = try_read(name, f"select * from energy_pump_pattern where pump = '{pump}'")
pattern = str(row2['pattern']) if row2 != None else None
cs.append(g_update_prefix | {'type': 'pump_energy', 'pump' : pump, 'price': price, 'pattern': pattern, 'effic': None})
return cs
+98
View File
@@ -0,0 +1,98 @@
from .database import *
def get_emitter_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'junction' : {'type': 'str' , 'optional': False , 'readonly': True },
'coefficient' : {'type': 'float' , 'optional': True , 'readonly': False} }
def get_emitter(name: str, junction: str) -> dict[str, Any]:
e = try_read(name, f"select * from emitters where junction = '{junction}'")
if e == None:
return { 'junction': junction, 'coefficient': None }
d = {}
d['junction'] = str(e['junction'])
d['coefficient'] = float(e['coefficient']) if e['coefficient'] != None else None
return d
class Emitter(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'emitter'
self.junction = str(input['junction'])
self.coefficient = float(input['coefficient']) if 'coefficient' in input and input['coefficient'] != None else None
self.f_type = f"'{self.type}'"
self.f_junction = f"'{self.junction}'"
self.f_coefficient = self.coefficient if self.coefficient != None else 'null'
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'junction': self.junction, 'coefficient': self.coefficient }
def _set_emitter(name: str, cs: ChangeSet) -> DbChangeSet:
old = Emitter(get_emitter(name, cs.operations[0]['junction']))
raw_new = get_emitter(name, cs.operations[0]['junction'])
new_dict = cs.operations[0]
schema = get_emitter_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = Emitter(raw_new)
redo_sql = f"delete from emitters where junction = {new.f_junction};"
if new.coefficient != None:
redo_sql += f"\ninsert into emitters (junction, coefficient) values ({new.f_junction}, {new.f_coefficient});"
undo_sql = f"delete from emitters where junction = {old.f_junction};"
if old.coefficient != None:
undo_sql += f"\ninsert into emitters (junction, coefficient) values ({old.f_junction}, {old.f_coefficient});"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_emitter(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_emitter(name, cs))
#--------------------------------------------------------------
# [EPA2][IN][OUT]
# node Ke
#--------------------------------------------------------------
# [EPA3][IN][OUT]
# node Ke (exponent pattern)
#--------------------------------------------------------------
def inp_in_emitter(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
junction = str(tokens[0])
coefficient = float(tokens[1])
return str(f"insert into emitters (junction, coefficient) values ('{junction}', {coefficient});")
def inp_out_emitter(name: str) -> list[str]:
lines = []
objs = read_all(name, 'select * from emitters')
for obj in objs:
junction = obj['junction']
coefficient = obj['coefficient']
lines.append(f'{junction} {coefficient}')
return lines
def delete_emitter_by_junction(name: str, junction: str) -> ChangeSet:
row = try_read(name, f"select * from emitters where junction = '{junction}'")
if row == None:
return ChangeSet()
return ChangeSet(g_update_prefix | {'type' : 'emitter', 'junction': junction, 'coefficient': None})
+95
View File
@@ -0,0 +1,95 @@
from .database import *
def get_quality_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'node' : {'type': 'str' , 'optional': False , 'readonly': True },
'quality' : {'type': 'float' , 'optional': True , 'readonly': False} }
def get_quality(name: str, node: str) -> dict[str, Any]:
e = try_read(name, f"select * from quality where node = '{node}'")
if e == None:
return { 'node': node, 'quality': None }
d = {}
d['node'] = str(e['node'])
d['quality'] = float(e['quality']) if e['quality'] != None else None
return d
class Quality(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'quality'
self.node = str(input['node'])
self.quality = float(input['quality']) if 'quality' in input and input['quality'] != None else None
self.f_type = f"'{self.type}'"
self.f_node = f"'{self.node}'"
self.f_quality = self.quality if self.quality != None else 'null'
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'node': self.node, 'quality': self.quality }
def _set_quality(name: str, cs: ChangeSet) -> DbChangeSet:
old = Quality(get_quality(name, cs.operations[0]['node']))
raw_new = get_quality(name, cs.operations[0]['node'])
new_dict = cs.operations[0]
schema = get_quality_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = Quality(raw_new)
redo_sql = f"delete from quality where node = {new.f_node};"
if new.quality != None:
redo_sql += f"\ninsert into quality (node, quality) values ({new.f_node}, {new.f_quality});"
undo_sql = f"delete from quality where node = {old.f_node};"
if old.quality != None:
undo_sql += f"\ninsert into quality (node, quality) values ({old.f_node}, {old.f_quality});"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_quality(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_quality(name, cs))
#--------------------------------------------------------------
# [EPA2][EPA3][IN][OUT]
# node initqual
#--------------------------------------------------------------
def inp_in_quality(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
node = str(tokens[0])
quality = float(tokens[1])
return str(f"insert into quality (node, quality) values ('{node}', {quality});")
def inp_out_quality(name: str) -> list[str]:
lines = []
objs = read_all(name, 'select * from quality')
for obj in objs:
node = obj['node']
quality = obj['quality']
lines.append(f'{node} {quality}')
return lines
def delete_quality_by_node(name: str, node: str) -> ChangeSet:
row = try_read(name, f"select * from quality where node = '{node}'")
if row == None:
return ChangeSet()
return ChangeSet(g_update_prefix | {'type' : 'quality', 'node': node, 'quality': None})
+153
View File
@@ -0,0 +1,153 @@
from .database import *
from .s0_base import *
SOURCE_TYPE_CONCEN = 'CONCEN'
SOURCE_TYPE_MASS = 'MASS'
SOURCE_TYPE_FLOWPACED = 'FLOWPACED'
SOURCE_TYPE_SETPOINT = 'SETPOINT'
def get_source_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'node' : {'type': 'str' , 'optional': False , 'readonly': True },
's_type' : {'type': 'str' , 'optional': False , 'readonly': False},
'strength' : {'type': 'float' , 'optional': False , 'readonly': False},
'pattern' : {'type': 'str' , 'optional': True , 'readonly': False} }
def get_source(name: str, node: str) -> dict[str, Any]:
s = try_read(name, f"select * from sources where node = '{node}'")
if s == None:
return {}
d = {}
d['node'] = str(s['node'])
d['s_type'] = str(s['s_type'])
d['strength'] = float(s['strength'])
d['pattern'] = str(s['pattern']) if s['pattern'] != None else None
return d
class Source(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'source'
self.node = str(input['node'])
self.s_type = str(input['s_type'])
self.strength = float(input['strength'])
self.pattern = str(input['pattern']) if 'pattern' in input and input['pattern'] != None else None
self.f_type = f"'{self.type}'"
self.f_node = f"'{self.node}'"
self.f_s_type = f"'{self.s_type}'"
self.f_strength = self.strength
self.f_pattern = f"'{self.pattern}'" if self.pattern != None else 'null'
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'node': self.node, 's_type': self.s_type, 'strength': self.strength, 'pattern': self.pattern }
def as_id_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'node': self.node }
def _set_source(name: str, cs: ChangeSet) -> DbChangeSet:
old = Source(get_source(name, cs.operations[0]['node']))
raw_new = get_source(name, cs.operations[0]['node'])
new_dict = cs.operations[0]
schema = get_source_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = Source(raw_new)
redo_sql = f"update sources set s_type = {new.f_s_type}, strength = {new.f_strength}, pattern = {new.f_pattern} where node = {new.f_node};"
undo_sql = f"update sources set s_type = {old.f_s_type}, strength = {old.f_strength}, pattern = {old.f_pattern} where node = {old.f_node};"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_source(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_source(name, cs))
def _add_source(name: str, cs: ChangeSet) -> DbChangeSet:
new = Source(cs.operations[0])
redo_sql = f"insert into sources (node, s_type, strength, pattern) values ({new.f_node}, {new.f_s_type}, {new.f_strength}, {new.f_pattern});"
undo_sql = f"delete from sources where node = {new.f_node};"
redo_cs = g_add_prefix | new.as_dict()
undo_cs = g_delete_prefix | new.as_id_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_source(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _add_source(name, cs))
def _delete_source(name: str, cs: ChangeSet) -> DbChangeSet:
old = Source(get_source(name, cs.operations[0]['node']))
redo_sql = f"delete from sources where node = {old.f_node};"
undo_sql = f"insert into sources (node, s_type, strength, pattern) values ({old.f_node}, {old.f_s_type}, {old.f_strength}, {old.f_pattern});"
redo_cs = g_delete_prefix | old.as_id_dict()
undo_cs = g_add_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_source(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _delete_source(name, cs))
#--------------------------------------------------------------
# [EPA2][EPA3][IN][OUT]
# node sourcetype quality (pattern)
#--------------------------------------------------------------
def inp_in_source(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
node = str(tokens[0])
s_type = str(tokens[1].upper())
strength = float(tokens[2])
pattern = str(tokens[3]) if num_without_desc >= 4 else None
pattern = f"'{pattern}'" if pattern != None else 'null'
return str(f"insert into sources (node, s_type, strength, pattern) values ('{node}', '{s_type}', {strength}, {pattern});")
def inp_out_source(name: str) -> list[str]:
lines = []
objs = read_all(name, 'select * from sources')
for obj in objs:
node = obj['node']
s_type = obj['s_type']
strength = obj['strength']
pattern = obj['pattern'] if obj['pattern'] != None else ''
lines.append(f'{node} {s_type} {strength} {pattern}')
return lines
def delete_source_by_node(name: str, node: str) -> ChangeSet:
row = try_read(name, f"select * from sources where node = '{node}'")
if row == None:
return ChangeSet()
return ChangeSet(g_delete_prefix | {'type' : 'source', 'node': node})
def unset_source_by_pattern(name: str, pattern: str) -> ChangeSet:
cs = ChangeSet()
rows = read_all(name, f"select node from sources where pattern = '{pattern}'")
for row in rows:
cs.append(g_update_prefix | {'type': 'source', 'node': row['node'], 'pattern': None})
return cs
+263
View File
@@ -0,0 +1,263 @@
from .database import *
element_schema = {'type': 'str' , 'optional': True , 'readonly': False}
def get_reaction_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'ORDER BULK' : element_schema,
'ORDER WALL' : element_schema,
'ORDER TANK' : element_schema,
'GLOBAL BULK' : element_schema,
'GLOBAL WALL' : element_schema,
'LIMITING POTENTIAL' : element_schema,
'ROUGHNESS CORRELATION' : element_schema }
def get_reaction(name: str) -> dict[str, Any]:
ts = read_all(name, f"select * from reactions")
d = {}
for e in ts:
d[e['key']] = str(e['value'])
return d
def _set_reaction(name: str, cs: ChangeSet) -> DbChangeSet:
raw_old = get_reaction(name)
old = {}
new = {}
new_dict = cs.operations[0]
schema = get_reaction_schema(name)
for key in schema.keys():
if key in new_dict:
old[key] = str(raw_old[key])
new[key] = str(new_dict[key])
redo_cs = g_update_prefix | { 'type' : 'reaction' }
redo_sql = ''
for key, value in new.items():
if redo_sql != '':
redo_sql += '\n'
redo_sql += f"update reactions set value = '{value}' where key = '{key}';"
redo_cs |= { key: value }
undo_cs = g_update_prefix | { 'type' : 'reaction' }
undo_sql = ''
for key, value in old.items():
if undo_sql != '':
undo_sql += '\n'
undo_sql += f"update reactions set value = '{value}' where key = '{key}';"
undo_cs |= { key: value }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_reaction(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_reaction(name, cs))
def get_pipe_reaction_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'pipe' : {'type': 'str' , 'optional': False , 'readonly': True },
'bulk' : {'type': 'float' , 'optional': True , 'readonly': False},
'wall' : {'type': 'float' , 'optional': True , 'readonly': False} }
def get_pipe_reaction(name: str, pipe: str) -> dict[str, Any]:
d = {}
d['pipe'] = pipe
pr = try_read(name, f"select * from reactions_pipe_bulk where pipe = '{pipe}'")
d['bulk'] = float(pr['value']) if pr != None else None
pr = try_read(name, f"select * from reactions_pipe_wall where pipe = '{pipe}'")
d['wall'] = float(pr['value']) if pr != None else None
return d
class PipeReaction(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'pipe_reaction'
self.pipe = str(input['pipe'])
self.bulk = float(input['bulk']) if 'bulk' in input and input['bulk'] != None else None
self.wall = float(input['wall']) if 'wall' in input and input['wall'] != None else None
self.f_type = f"'{self.type}'"
self.f_pipe = f"'{self.pipe}'"
self.f_bulk = self.bulk if self.bulk != None else 'null'
self.f_wall = self.wall if self.wall != None else 'null'
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'pipe': self.pipe, 'bulk': self.bulk, 'wall': self.wall }
def _set_pipe_reaction(name: str, cs: ChangeSet) -> DbChangeSet:
old = PipeReaction(get_pipe_reaction(name, cs.operations[0]['pipe']))
raw_new = get_pipe_reaction(name, cs.operations[0]['pipe'])
new_dict = cs.operations[0]
schema = get_pipe_reaction_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = PipeReaction(raw_new)
redo_sql = f"delete from reactions_pipe_bulk where pipe = {new.f_pipe};\ndelete from reactions_pipe_wall where pipe = {new.f_pipe};"
if new.bulk != None:
redo_sql += f"\ninsert into reactions_pipe_bulk (pipe, value) values ({new.f_pipe}, {new.f_bulk});"
if new.wall != None:
redo_sql += f"\ninsert into reactions_pipe_wall (pipe, value) values ({new.f_pipe}, {new.f_wall});"
undo_sql = f"delete from reactions_pipe_bulk where pipe = {old.f_pipe};\ndelete from reactions_pipe_wall where pipe = {old.f_pipe};"
if old.bulk != None:
undo_sql += f"\ninsert into reactions_pipe_bulk (pipe, value) values ({old.f_pipe}, {old.f_bulk});"
if old.wall != None:
undo_sql += f"\ninsert into reactions_pipe_wall (pipe, value) values ({old.f_pipe}, {old.f_wall});"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_pipe_reaction(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_pipe_reaction(name, cs))
def get_tank_reaction_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'tank' : {'type': 'str' , 'optional': False , 'readonly': True },
'value' : {'type': 'float' , 'optional': True , 'readonly': False} }
def get_tank_reaction(name: str, tank: str) -> dict[str, Any]:
d = {}
d['tank'] = tank
pr = try_read(name, f"select * from reactions_tank where tank = '{tank}'")
d['value'] = float(pr['value']) if pr != None else None
return d
class TankReaction(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'tank_reaction'
self.tank = str(input['tank'])
self.value = float(input['value']) if 'value' in input and input['value'] != None else None
self.f_type = f"'{self.type}'"
self.f_tank = f"'{self.tank}'"
self.f_value = self.value if self.value != None else 'null'
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'tank': self.tank, 'value': self.value }
def _set_tank_reaction(name: str, cs: ChangeSet) -> DbChangeSet:
old = TankReaction(get_tank_reaction(name, cs.operations[0]['tank']))
raw_new = get_tank_reaction(name, cs.operations[0]['tank'])
new_dict = cs.operations[0]
schema = get_tank_reaction_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = TankReaction(raw_new)
redo_sql = f"delete from reactions_tank where tank = {new.f_tank};"
if new.value != None:
redo_sql += f"\ninsert into reactions_tank (tank, value) values ({new.f_tank}, {new.f_value});"
undo_sql = f"delete from reactions_tank where tank = {old.f_tank};"
if old.value != None:
undo_sql += f"\ninsert into reactions_tank (tank, value) values ({old.f_tank}, {old.f_value});"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_tank_reaction(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_tank_reaction(name, cs))
#--------------------------------------------------------------
# [EPA2][EPA3][IN][OUT]
# ORDER {BULK/WALL/TANK} value
# GLOBAL BULK coeff
# GLOBAL WALL coeff
# BULK link1 (link2) coeff
# WALL link1 (link2) coeff
# TANK node1 (node2) coeff
# LIMITING POTENTIAL value
# ROUGHNESS CORRELATION value
#--------------------------------------------------------------
def inp_in_reaction(line: str) -> str:
tokens = line.split()
token0 = tokens[0].upper()
if token0 == 'BULK' or token0 == 'WALL':
pipe = tokens[1]
key = token0.lower()
value = tokens[2]
return str(f"insert into reactions_pipe_{key} (pipe, value) values ('{pipe}', {value});")
elif token0 == 'TANK':
tank = tokens[1]
value = tokens[2]
return str(f"insert into reactions_tank (tank, value) values ('{tank}', {value});")
else:
line = line.upper().strip()
for key in get_reaction_schema('').keys():
if line.startswith(key):
value = line.removeprefix(key).strip()
return str(f"update reactions set value = '{value}' where key = '{key}';")
return str('')
def inp_out_reaction(name: str) -> list[str]:
lines = []
objs = read_all(name, f"select * from reactions")
for obj in objs:
key = obj['key']
value = obj['value']
lines.append(f'{key} {value}')
objs = read_all(name, f"select * from reactions_pipe_bulk")
for obj in objs:
pipe = obj['pipe']
value = obj['value']
lines.append(f'BULK {pipe} {value}')
objs = read_all(name, f"select * from reactions_pipe_wall")
for obj in objs:
pipe = obj['pipe']
value = obj['value']
lines.append(f'WALL {pipe} {value}')
objs = read_all(name, f"select * from reactions_tank")
for obj in objs:
tank = obj['tank']
value = obj['value']
lines.append(f'TANK {tank} {value}')
return lines
def delete_pipe_reaction_by_pipe(name: str, pipe: str) -> ChangeSet:
row1 = try_read(name, f"select * from reactions_pipe_bulk where pipe = '{pipe}'")
row2 = try_read(name, f"select * from reactions_pipe_wall where pipe = '{pipe}'")
if row1 == None and row2 == None:
return ChangeSet()
return ChangeSet(g_update_prefix | {'type': 'pipe_reaction', 'pipe': pipe, 'bulk': None, 'wall': None})
def delete_tank_reaction_by_tank(name: str, tank: str) -> ChangeSet:
row = try_read(name, f"select * from reactions_tank where tank = '{tank}'")
if row == None:
return ChangeSet()
return ChangeSet(g_update_prefix | {'type': 'tank_reaction', 'tank': tank, 'value': None})
+40
View File
@@ -0,0 +1,40 @@
from .database import *
def get_title_schema(name: str) -> dict[str, dict[str, Any]]:
return {'value': {'type': 'float', 'optional': False, 'readonly': False}}
def get_title(name: str) -> dict[str, Any]:
title = read(name, 'select * from title')
return { 'value': title['value'] }
def _set_title(name: str, cs: ChangeSet) -> DbChangeSet:
new = cs.operations[0]['value']
old = get_title(name)['value']
redo_sql = f"update title set value = '{new}';"
undo_sql = f"update title set value = '{old}';"
redo_cs = g_update_prefix | { 'type': 'title', 'value': new }
undo_cs = g_update_prefix | { 'type': 'title', 'value': old }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_title(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_title(name ,cs))
def inp_in_title(section: list[str]) -> str:
if section == []:
return str('')
title = '\n'.join(section)
return str(f"update title set value = '{title}';")
def inp_out_title(name: str) -> list[str]:
obj = str(get_title(name)['value'])
return obj.split('\n')
+150
View File
@@ -0,0 +1,150 @@
from .database import *
from .s0_base import *
MIXING_MODEL_MIXED = 'MIXED'
MIXING_MODEL_2COMP = '2COMP'
MIXING_MODEL_FIFO = 'FIFO'
MIXING_MODEL_LIFO = 'LIFO'
def get_mixing_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'tank' : {'type': 'str' , 'optional': False , 'readonly': True },
'model' : {'type': 'str' , 'optional': False , 'readonly': False},
'value' : {'type': 'float' , 'optional': True , 'readonly': False} }
def get_mixing(name: str, tank: str) -> dict[str, Any]:
m = try_read(name, f"select * from mixing where tank = '{tank}'")
if m == None:
return {}
d = {}
d['tank'] = str(m['tank'])
d['model'] = str(m['model'])
d['value'] = float(m['value']) if m['value'] != None else None
return d
class Mixing(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'mixing'
self.tank = str(input['tank'])
self.model = str(input['model'])
self.value = float(input['value']) if 'value' in input and input['value'] != None else None
self.f_type = f"'{self.type}'"
self.f_tank = f"'{self.tank}'"
self.f_model = f"'{self.model}'"
self.f_value = self.value if self.value != None else 'null'
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'tank': self.tank, 'model': self.model, 'value': self.value }
def as_id_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'tank': self.tank }
def _set_mixing(name: str, cs: ChangeSet) -> DbChangeSet:
old = Mixing(get_mixing(name, cs.operations[0]['tank']))
raw_new = get_mixing(name, cs.operations[0]['tank'])
new_dict = cs.operations[0]
schema = get_mixing_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = Mixing(raw_new)
redo_sql = f"update mixing set model = {new.f_model}, value = {new.f_value} where tank = {new.f_tank};"
undo_sql = f"update mixing set model = {old.f_model}, value = {old.f_value} where tank = {old.f_tank};"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_mixing(name: str, cs: ChangeSet) -> ChangeSet:
if 'tank' not in cs.operations[0]:
return ChangeSet()
if get_mixing(name, cs.operations[0]['tank']) == {}:
return ChangeSet()
return execute_command(name, _set_mixing(name, cs))
def _add_mixing(name: str, cs: ChangeSet) -> DbChangeSet:
new = Mixing(cs.operations[0])
redo_sql = f"insert into mixing (tank, model, value) values ({new.f_tank}, {new.f_model}, {new.f_value});"
undo_sql = f"delete from mixing where tank = {new.f_tank};"
redo_cs = g_add_prefix | new.as_dict()
undo_cs = g_delete_prefix | new.as_id_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_mixing(name: str, cs: ChangeSet) -> ChangeSet:
if 'tank' not in cs.operations[0]:
return ChangeSet()
if get_mixing(name, cs.operations[0]['tank']) != {}:
return ChangeSet()
return execute_command(name, _add_mixing(name, cs))
def _delete_mixing(name: str, cs: ChangeSet) -> DbChangeSet:
old = Mixing(get_mixing(name, cs.operations[0]['tank']))
redo_sql = f"delete from mixing where tank = {old.f_tank};"
undo_sql = f"insert into mixing (tank, model, value) values ({old.f_tank}, {old.f_model}, {old.f_value});"
redo_cs = g_delete_prefix | old.as_id_dict()
undo_cs = g_add_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_mixing(name: str, cs: ChangeSet) -> ChangeSet:
if 'tank' not in cs.operations[0]:
return ChangeSet()
if get_mixing(name, cs.operations[0]['tank']) == {}:
return ChangeSet()
return execute_command(name, _delete_mixing(name, cs))
#--------------------------------------------------------------
# [EPA2][EPA3][IN][OUT]
# TankID MixModel FractVolume
# FractVolume if type == MIX2
#--------------------------------------------------------------
def inp_in_mixing(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
tank = str(tokens[0])
model = str(tokens[1].upper())
value = float(tokens[3]) if num_without_desc >= 4 else None
value = value if value != None else 'null'
return str(f"insert into mixing (tank, model, value) values ('{tank}', '{model}', {value});")
def inp_out_mixing(name: str) -> list[str]:
lines = []
objs = read_all(name, 'select * from mixing')
for obj in objs:
tank = obj['tank']
model = obj['model']
value = obj['value'] if obj['value'] != None else ''
lines.append(f'{tank} {model} {value}')
return lines
def delete_mixing_by_tank(name: str, tank: str) -> ChangeSet:
row = try_read(name, f"select * from mixing where tank = '{tank}'")
if row == None:
return ChangeSet()
return ChangeSet(g_delete_prefix | {'type' : 'mixing', 'tank': tank})
+112
View File
@@ -0,0 +1,112 @@
from .database import *
TIME_STATISTIC_NONE = 'NONE'
TIME_STATISTIC_AVERAGED = 'AVERAGED'
TIME_STATISTIC_MINIMUM = 'MINIMUM'
TIME_STATISTIC_MAXIMUM = 'MAXIMUM'
TIME_STATISTIC_RANGE = 'RANGE'
element_schema = {'type': 'str' , 'optional': True , 'readonly': False}
def get_time_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'DURATION' : element_schema,
'HYDRAULIC TIMESTEP' : element_schema,
'QUALITY TIMESTEP' : element_schema,
'RULE TIMESTEP' : element_schema,
'PATTERN TIMESTEP' : element_schema,
'PATTERN START' : element_schema,
'REPORT TIMESTEP' : element_schema,
'REPORT START' : element_schema,
'START CLOCKTIME' : element_schema,
'STATISTIC' : element_schema}
def get_time(name: str) -> dict[str, Any]:
ts = read_all(name, f"select * from times")
d = {}
for e in ts:
d[e['key']] = str(e['value'])
return d
def _set_time(name: str, cs: ChangeSet) -> DbChangeSet:
raw_old = get_time(name)
old = {}
new = {}
new_dict = cs.operations[0]
schema = get_time_schema(name)
for key in schema.keys():
if key in new_dict:
old[key] = str(raw_old[key])
new[key] = str(new_dict[key])
redo_cs = g_update_prefix | { 'type' : 'time' }
redo_sql = ''
for key, value in new.items():
if redo_sql != '':
redo_sql += '\n'
redo_sql += f"update times set value = '{value}' where key = '{key}';"
redo_cs |= { key: value }
undo_cs = g_update_prefix | { 'type' : 'time' }
undo_sql = ''
for key, value in old.items():
if undo_sql != '':
undo_sql += '\n'
undo_sql += f"update times set value = '{value}' where key = '{key}';"
undo_cs |= { key: value }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_time(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_time(name, cs))
#--------------------------------------------------------------
# [EPA2][EPA3]
# STATISTIC {NONE/AVERAGE/MIN/MAX/RANGE}
# DURATION value (units)
# HYDRAULIC TIMESTEP value (units)
# QUALITY TIMESTEP value (units)
# RULE TIMESTEP value (units)
# PATTERN TIMESTEP value (units)
# PATTERN START value (units)
# REPORT TIMESTEP value (units)
# REPORT START value (units)
# START CLOCKTIME value (AM PM)
# [EPA3] supports [EPA2] keyword
#--------------------------------------------------------------
def inp_in_time(section: list[str]) -> str:
sql = ''
for s in section:
if s.startswith(';'):
continue
line = s.upper().strip()
# TOTAL DURATION => DURATION
if line.startswith('TOTAL DURATION'):
line = line.replace('TOTAL DURATION', 'DURATION')
for key in get_time_schema('').keys():
if line.startswith(key):
value = line.removeprefix(key).strip()
sql += f"update times set value = '{value}' where key = '{key}';"
return sql
def inp_out_time(name: str) -> list[str]:
lines = []
objs = read_all(name, f"select * from times")
for obj in objs:
key = obj['key']
value = obj['value']
lines.append(f'{key} {value}')
return lines
+34
View File
@@ -0,0 +1,34 @@
from .database import *
#--------------------------------------------------------------
# [EPA2]
# PAGE linesperpage
# STATUS {NONE/YES/FULL}
# SUMMARY {YES/NO}
# MESSAGES {YES/NO}
# ENERGY {NO/YES}
# NODES {NONE/ALL}
# NODES node1 node2 ...
# LINKS {NONE/ALL}
# LINKS link1 link2 ...
# FILE filename
# variable {YES/NO}
# variable {BELOW/ABOVE/PRECISION} value
# [EPA3][NOT SUPPORT]
# TRIALS {YES/NO}
#--------------------------------------------------------------
def inp_in_report(section: list[str]) -> str:
return ''
def inp_out_report(name: str) -> list[str]:
lines = []
objs = read_all(name, f"select * from report")
for obj in objs:
key = obj['key']
value = obj['value']
lines.append(f'{key} {value}')
return lines
+81
View File
@@ -0,0 +1,81 @@
from .database import *
from .s23_options_util import get_option_schema, generate_v3
def _inp_in_option(section: list[str]) -> ChangeSet:
if len(section) <= 0:
return ChangeSet()
cs = g_update_prefix | { 'type' : 'option' }
for s in section:
if s.startswith(';'):
continue
tokens = s.strip().split()
if tokens[0].upper() == 'PATTERN': # can not upper id
value = tokens[1] if len(tokens) > 1 else ''
cs |= { 'PATTERN' : value }
elif tokens[0].upper() == 'QUALITY': # can not upper trace node
value = tokens[1] if len(tokens) > 1 else ''
if len(tokens) > 2:
value += f' {tokens[2]}'
cs |= { 'QUALITY' : value }
else:
line = s.upper().strip()
for key in get_option_schema('').keys():
if line.startswith(key):
value = line.removeprefix(key).strip()
cs |= { key : value }
result = ChangeSet(cs)
result.merge(generate_v3(result))
return result
def inp_in_option(section: list[str]) -> str:
sql = ''
result = _inp_in_option(section)
for op in result.operations:
for key in op.keys():
if key == 'operation' or key == 'type':
continue
if op['type'] == 'option':
sql += f"update options set value = '{op[key]}' where key = '{key}';"
else:
sql += f"update options_v3 set value = '{op[key]}' where key = '{key}';"
return sql
def inp_out_option(name: str) -> list[str]:
lines = []
objs = read_all(name, f"select * from options")
is_dda = False
for obj in objs:
if obj['key'] == 'DEMAND MODEL':
is_dda = obj['value'] == 'DDA'
dda_ignore = [
'HEADERROR', # TODO: default is 0 which is conflict with PDA
'FLOWCHANGE', # TODO: default is 0 which is conflict with PDA
'MINIMUM PRESSURE',
'REQUIRED PRESSURE',
'PRESSURE EXPONENT'
]
for obj in objs:
key = obj['key']
# why write this ?
if key == 'PRESSURE':
continue
# release version does not support new keys and has error message
if key == 'HTOL' or key == 'QTOL' or key == 'RQTOL':
continue
# ignore some weird settings for DDA
if is_dda and key in dda_ignore:
continue
value = obj['value']
if str(value).strip() != '':
lines.append(f'{key} {value}')
return lines
+401
View File
@@ -0,0 +1,401 @@
from .database import *
#--------------------------------------------------------------
# [EPANET2][IN][OUT]
# UNITS CFS/GPM/MGD/IMGD/AFD/LPS/LPM/MLD/CMH/CMD/SI
# PRESSURE PSI/KPA/M
# HEADLOSS H-W/D-W/C-M
# QUALITY NONE/AGE/TRACE/CHEMICAL (TraceNode)
# UNBALANCED STOP/CONTINUE {Niter}
# PATTERN id
# DEMAND MODEL DDA/PDA
# DEMAND MULTIPLIER value
# EMITTER EXPONENT value
# VISCOSITY value
# DIFFUSIVITY value
# SPECIFIC GRAVITY value
# TRIALS value
# ACCURACY value#
# HEADERROR value
# FLOWCHANGE value
# MINIMUM PRESSURE value
# REQUIRED PRESSURE value
# PRESSURE EXPONENT value#
# TOLERANCE value
# HTOL value
# QTOL value
# RQTOL value
# CHECKFREQ value
# MAXCHECK value
# DAMPLIMIT value
# ---- Unsupported Options -----
# HYDRAULICS USE/SAVE filename
# MAP filename
#--------------------------------------------------------------
element_schema = {'type': 'str' , 'optional': True , 'readonly': False}
OPTION_UNITS_CFS = 'CFS'
OPTION_UNITS_GPM = 'GPM'
OPTION_UNITS_MGD = 'MGD'
OPTION_UNITS_IMGD = 'IMGD'
OPTION_UNITS_AFD = 'AFD'
OPTION_UNITS_LPS = 'LPS'
OPTION_UNITS_LPM = 'LPM'
OPTION_UNITS_MLD = 'MLD'
OPTION_UNITS_CMH = 'CMH'
OPTION_UNITS_CMD = 'CMD'
OPTION_PRESSURE_PSI = 'PSI'
OPTION_PRESSURE_KPA = 'KPA'
OPTION_PRESSURE_METERS = 'METERS'
OPTION_HEADLOSS_HW = 'H-W'
OPTION_HEADLOSS_DW = 'D-W'
OPTION_HEADLOSS_CM = 'C-M'
OPTION_UNBALANCED_STOP = 'STOP'
OPTION_UNBALANCED_CONTINUE = 'CONTINUE'
OPTION_DEMAND_MODEL_DDA = 'DDA'
OPTION_DEMAND_MODEL_PDA = 'PDA'
OPTION_QUALITY_NONE = 'NONE'
OPTION_QUALITY_CHEMICAL = 'CHEMICAL'
OPTION_QUALITY_AGE = 'AGE'
OPTION_QUALITY_TRACE = 'TRACE'
def get_option_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'UNITS' : element_schema,
'PRESSURE' : element_schema,
'HEADLOSS' : element_schema,
'QUALITY' : element_schema,
'UNBALANCED' : element_schema,
'PATTERN' : element_schema,
'DEMAND MODEL' : element_schema,
'DEMAND MULTIPLIER' : element_schema,
'EMITTER EXPONENT' : element_schema,
'VISCOSITY' : element_schema,
'DIFFUSIVITY' : element_schema,
'SPECIFIC GRAVITY' : element_schema,
'TRIALS' : element_schema,
'ACCURACY' : element_schema,
'HEADERROR' : element_schema,
'FLOWCHANGE' : element_schema,
'MINIMUM PRESSURE' : element_schema,
'REQUIRED PRESSURE' : element_schema,
'PRESSURE EXPONENT' : element_schema,
'TOLERANCE' : element_schema,
'HTOL' : element_schema,
'QTOL' : element_schema,
'RQTOL' : element_schema,
'CHECKFREQ' : element_schema,
'MAXCHECK' : element_schema,
'DAMPLIMIT' : element_schema }
def get_option(name: str) -> dict[str, Any]:
ts = read_all(name, f"select * from options")
d = {}
for e in ts:
d[e['key']] = str(e['value'])
return d
def _set_option(name: str, cs: ChangeSet) -> DbChangeSet:
raw_old = get_option(name)
old = {}
new = {}
new_dict = cs.operations[0]
schema = get_option_schema(name)
for key in schema.keys():
if key in new_dict:
old[key] = str(raw_old[key])
new[key] = str(new_dict[key])
redo_cs = g_update_prefix | { 'type' : 'option' }
redo_sql = ''
for key, value in new.items():
if redo_sql != '':
redo_sql += '\n'
redo_sql += f"update options set value = '{value}' where key = '{key}';"
redo_cs |= { key: value }
undo_cs = g_update_prefix | { 'type' : 'option' }
undo_sql = ''
for key, value in old.items():
if undo_sql != '':
undo_sql += '\n'
undo_sql += f"update options set value = '{value}' where key = '{key}';"
undo_cs |= { key: value }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_option(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_option(name, cs))
OPTION_V3_FLOW_UNITS_CFS = OPTION_UNITS_CFS
OPTION_V3_FLOW_UNITS_GPM = OPTION_UNITS_GPM
OPTION_V3_FLOW_UNITS_MGD = OPTION_UNITS_MGD
OPTION_V3_FLOW_UNITS_IMGD = OPTION_UNITS_IMGD
OPTION_V3_FLOW_UNITS_AFD = OPTION_UNITS_AFD
OPTION_V3_FLOW_UNITS_LPS = OPTION_UNITS_LPS
OPTION_V3_FLOW_UNITS_LPM = OPTION_UNITS_LPM
OPTION_V3_FLOW_UNITS_MLD = OPTION_UNITS_MLD
OPTION_V3_FLOW_UNITS_CMH = OPTION_UNITS_CMH
OPTION_V3_FLOW_UNITS_CMD = OPTION_UNITS_CMD
OPTION_V3_PRESSURE_UNITS_PSI = OPTION_PRESSURE_PSI
OPTION_V3_PRESSURE_UNITS_KPA = OPTION_PRESSURE_KPA
OPTION_V3_PRESSURE_UNITS_METERS = OPTION_PRESSURE_METERS
OPTION_V3_HEADLOSS_MODEL_HW = OPTION_HEADLOSS_HW
OPTION_V3_HEADLOSS_MODEL_DW = OPTION_HEADLOSS_DW
OPTION_V3_HEADLOSS_MODEL_CM = OPTION_HEADLOSS_CM
OPTION_V3_STEP_SIZING_FULL = 'FULL'
OPTION_V3_STEP_SIZING_RELAXATION = 'RELAXATION'
OPTION_V3_STEP_SIZING_LINESEARCH = 'LINESEARCH'
OPTION_V3_IF_UNBALANCED_STOP = OPTION_UNBALANCED_STOP
OPTION_V3_IF_UNBALANCED_CONTINUE = OPTION_UNBALANCED_CONTINUE
OPTION_V3_DEMAND_MODEL_FIXED = 'FIXED'
OPTION_V3_DEMAND_MODEL_CONSTRAINED = 'CONSTRAINED'
OPTION_V3_DEMAND_MODEL_POWER = 'POWER'
OPTION_V3_DEMAND_MODEL_LOGISTIC = 'LOGISTIC'
OPTION_V3_LEAKAGE_MODEL_NONE = 'NONE'
OPTION_V3_LEAKAGE_MODEL_POWER = 'POWER'
OPTION_V3_LEAKAGE_MODEL_FAVAD = 'FAVAD'
OPTION_V3_QUALITY_MODEL_NONE = OPTION_QUALITY_NONE
OPTION_V3_QUALITY_MODEL_CHEMICAL = OPTION_QUALITY_CHEMICAL
OPTION_V3_QUALITY_MODEL_AGE = OPTION_QUALITY_AGE
OPTION_V3_QUALITY_MODEL_TRACE = OPTION_QUALITY_TRACE
OPTION_V3_QUALITY_UNITS_HRS = 'HRS'
OPTION_V3_QUALITY_UNITS_PCNT = 'PCNT'
OPTION_V3_QUALITY_UNITS_MGL = 'MG/L'
OPTION_V3_QUALITY_UNITS_UGL = 'UG/L'
def get_option_v3_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'FLOW_UNITS' : element_schema,
'PRESSURE_UNITS' : element_schema,
'HEADLOSS_MODEL' : element_schema,
'SPECIFIC_GRAVITY' : element_schema,
'SPECIFIC_VISCOSITY' : element_schema,
'MAXIMUM_TRIALS' : element_schema,
'HEAD_TOLERANCE' : element_schema,
'FLOW_TOLERANCE' : element_schema,
'FLOW_CHANGE_LIMIT' : element_schema,
'RELATIVE_ACCURACY' : element_schema,
'TIME_WEIGHT' : element_schema,
'STEP_SIZING' : element_schema,
'IF_UNBALANCED' : element_schema,
'DEMAND_MODEL' : element_schema,
'DEMAND_PATTERN' : element_schema,
'DEMAND_MULTIPLIER' : element_schema,
'MINIMUM_PRESSURE' : element_schema,
'SERVICE_PRESSURE' : element_schema,
'PRESSURE_EXPONENT' : element_schema,
'LEAKAGE_MODEL' : element_schema,
'LEAKAGE_COEFF1' : element_schema,
'LEAKAGE_COEFF2' : element_schema,
'EMITTER_EXPONENT' : element_schema,
'QUALITY_MODEL' : element_schema,
'QUALITY_NAME' : element_schema,
'QUALITY_UNITS' : element_schema,
'TRACE_NODE' : element_schema,
'SPECIFIC_DIFFUSIVITY' : element_schema,
'QUALITY_TOLERANCE' : element_schema }
def get_option_v3(name: str) -> dict[str, Any]:
ts = read_all(name, f"select * from options_v3")
d = {}
for e in ts:
d[e['key']] = str(e['value'])
return d
def _set_option_v3(name: str, cs: ChangeSet) -> DbChangeSet:
raw_old = get_option_v3(name)
old = {}
new = {}
new_dict = cs.operations[0]
schema = get_option_v3_schema(name)
for key in schema.keys():
if key in new_dict:
old[key] = str(raw_old[key])
new[key] = str(new_dict[key])
redo_cs = g_update_prefix | { 'type' : 'option_v3' }
redo_sql = ''
for key, value in new.items():
if redo_sql != '':
redo_sql += '\n'
redo_sql += f"update options_v3 set value = '{value}' where key = '{key}';"
redo_cs |= { key: value }
undo_cs = g_update_prefix | { 'type' : 'option_v3' }
undo_sql = ''
for key, value in old.items():
if undo_sql != '':
undo_sql += '\n'
undo_sql += f"update options_v3 set value = '{value}' where key = '{key}';"
undo_cs |= { key: value }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_option_v3(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_option_v3(name, cs))
_key_map_23 = {
'UNITS' : 'FLOW_UNITS',
'PRESSURE' : 'PRESSURE_UNITS',
'HEADLOSS' : 'HEADLOSS_MODEL',
'QUALITY' : 'QUALITY_MODEL',
'UNBALANCED' : 'IF_UNBALANCED',
'PATTERN' : 'DEMAND_PATTERN',
'DEMAND MODEL' : 'DEMAND_MODEL',
'DEMAND MULTIPLIER' : 'DEMAND_MULTIPLIER',
'EMITTER EXPONENT' : 'EMITTER_EXPONENT',
'VISCOSITY' : 'SPECIFIC_VISCOSITY',
'DIFFUSIVITY' : 'SPECIFIC_DIFFUSIVITY',
'SPECIFIC GRAVITY' : 'SPECIFIC_GRAVITY',
'TRIALS' : 'MAXIMUM_TRIALS',
'ACCURACY' : 'RELATIVE_ACCURACY',
#'HEADERROR' : '',
'FLOWCHANGE' : 'FLOW_CHANGE_LIMIT',
'MINIMUM PRESSURE' : 'MINIMUM_PRESSURE',
'REQUIRED PRESSURE' : 'SERVICE_PRESSURE',
'PRESSURE EXPONENT' : 'PRESSURE_EXPONENT',
'TOLERANCE' : 'QUALITY_TOLERANCE',
'HTOL' : 'HEAD_TOLERANCE',
'QTOL' : 'FLOW_TOLERANCE',
#'RQTOL' : '',
#'CHECKFREQ' : '',
#'MAXCHECK' : '',
#'DAMPLIMIT' : '',
}
_key_map_32 = {
'FLOW_UNITS' : 'UNITS',
'PRESSURE_UNITS' : 'PRESSURE',
'HEADLOSS_MODEL' : 'HEADLOSS',
'SPECIFIC_GRAVITY' : 'SPECIFIC GRAVITY',
'SPECIFIC_VISCOSITY' : 'VISCOSITY',
'MAXIMUM_TRIALS' : 'TRIALS',
'HEAD_TOLERANCE' : 'HTOL',
'FLOW_TOLERANCE' : 'QTOL',
'FLOW_CHANGE_LIMIT' : 'FLOWCHANGE',
'RELATIVE_ACCURACY' : 'ACCURACY',
#'TIME_WEIGHT' : '',
#'STEP_SIZING' : '',
'IF_UNBALANCED' : 'UNBALANCED',
'DEMAND_MODEL' : 'DEMAND MODEL',
'DEMAND_PATTERN' : 'PATTERN',
'DEMAND_MULTIPLIER' : 'DEMAND MULTIPLIER',
'MINIMUM_PRESSURE' : 'MINIMUM PRESSURE',
'SERVICE_PRESSURE' : 'REQUIRED PRESSURE',
'PRESSURE_EXPONENT' : 'PRESSURE EXPONENT',
#'LEAKAGE_MODEL' : '',
#'LEAKAGE_COEFF1' : '',
#'LEAKAGE_COEFF2' : '',
'EMITTER_EXPONENT' : 'EMITTER EXPONENT',
'QUALITY_MODEL' : 'QUALITY',
#'QUALITY_NAME' : '',
#'QUALITY_UNITS' : '',
#'TRACE_NODE' : '',
'SPECIFIC_DIFFUSIVITY' : 'DIFFUSIVITY',
'QUALITY_TOLERANCE' : 'TOLERANCE'
}
def generate_v2(cs: ChangeSet) -> ChangeSet:
op = cs.operations[0]
if op['type'] == 'option':
return cs
map = _key_map_32
cs_v2 = {}
for key in op:
if key == 'operation' or key == 'type':
continue
if key in map.keys():
if key != 'QUALITY_MODEL' and key != 'DEMAND_MODEL':
cs_v2 |= { map[key] : op[key] }
elif key == 'QUALITY_MODEL':
if str(op[key]).upper() == OPTION_QUALITY_TRACE and 'TRACE_NODE' in op.keys():
cs_v2 |= { map[key] : f"{OPTION_QUALITY_TRACE} {op['TRACE_NODE']}" }
else:
cs_v2 |= { map[key] : str(op[key]).upper() }
elif key == 'DEMAND_MODEL':
if op[key] == OPTION_V3_DEMAND_MODEL_FIXED:
cs_v2 |= { map[key] : OPTION_DEMAND_MODEL_DDA }
else:
cs_v2 |= { map[key] : OPTION_DEMAND_MODEL_PDA }
if len(cs_v2) > 0:
cs_v2 |= g_update_prefix | { 'type' : 'option' }
return ChangeSet(cs_v2)
return ChangeSet()
def generate_v3(cs: ChangeSet) -> ChangeSet:
op = cs.operations[0]
if op['type'] == 'option_v3':
return cs
map = _key_map_23
cs_v3 = {}
for key in op:
if key == 'operation' or key == 'type':
continue
if key in map.keys():
if key != 'QUALITY' and key != 'DEMAND MODEL':
cs_v3 |= { map[key] : op[key] }
elif key == 'QUALITY':
tokens = str(op[key]).split()
if len(tokens) >= 1:
cs_v3 |= { map[key] : tokens[0].upper() }
if tokens[0].upper() == OPTION_QUALITY_TRACE and len(tokens) >= 2:
cs_v3 |= { 'TRACE_NODE' : tokens[1] }
elif key == 'DEMAND MODEL':
if op[key] == OPTION_DEMAND_MODEL_DDA:
cs_v3 |= { map[key] : OPTION_V3_DEMAND_MODEL_FIXED }
else:
cs_v3 |= { map[key] : OPTION_V3_DEMAND_MODEL_POWER }
if len(cs_v3) > 0:
cs_v3 |= g_update_prefix | { 'type' : 'option_v3' }
return ChangeSet(cs_v3)
return ChangeSet()
+79
View File
@@ -0,0 +1,79 @@
from .database import *
from .s23_options_util import get_option_schema, get_option_v3_schema, generate_v2, generate_v3
def _parse_v2(v2_lines: list[str]) -> dict[str, str]:
cs_v2 = g_update_prefix | { 'type' : 'option' }
for s in v2_lines:
tokens = s.split()
if tokens[0].upper() == 'PATTERN': # can not upper id
value = tokens[1] if len(tokens) > 1 else ''
cs_v2 |= { 'PATTERN' : value }
elif tokens[0].upper() == 'QUALITY': # can not upper trace node
value = tokens[1]
if len(tokens) > 2:
value += f' {tokens[2]}'
cs_v2 |= { 'QUALITY' : value }
else:
line = s.upper().strip()
for key in get_option_schema('').keys():
if line.startswith(key):
value = line.removeprefix(key).strip()
cs_v2 |= { key : value }
return cs_v2
def _inp_in_option_v3(section: list[str]) -> ChangeSet:
if len(section) <= 0:
return ChangeSet()
cs_v3 = g_update_prefix | { 'type' : 'option_v3' }
v2_lines = []
for s in section:
if s.startswith(';'):
continue
tokens = s.strip().split()
key = tokens[0]
if key in get_option_v3_schema('').keys():
value = ''
if len(tokens) == 2:
value = tokens[1]
elif len(tokens) > 2:
value = ' '.join(tokens[1:])
cs_v3 |= { key : value }
else:
v2_lines.append(s.strip())
# unlikely...
cs_v2 = _parse_v2(v2_lines)
result = ChangeSet(cs_v3)
result.merge(generate_v3(ChangeSet(cs_v2)))
result.merge(generate_v2(result))
return result
def inp_in_option_v3(section: list[str]) -> str:
sql = ''
result = _inp_in_option_v3(section)
for op in result.operations:
for key in op.keys():
if key == 'operation' or key == 'type':
continue
if op['type'] == 'option_v3':
sql += f"update options_v3 set value = '{op[key]}' where key = '{key}';"
else:
sql += f"update options set value = '{op[key]}' where key = '{key}';"
return sql
def inp_out_option_v3(name: str) -> list[str]:
lines = []
objs = read_all(name, f"select * from options_v3")
for obj in objs:
key = obj['key']
value = obj['value']
if str(value).strip() != '':
lines.append(f'{key} {value}')
return lines
+92
View File
@@ -0,0 +1,92 @@
from .database import *
from .s0_base import get_link_nodes
def sql_update_coord(node: str, x: float, y: float) -> str:
coord = f"st_geomfromtext('point({x} {y})')"
return str(f"update coordinates set coord = {coord} where node = '{node}';")
def sql_insert_coord(node: str, x: float, y: float) -> str:
coord = f"st_geomfromtext('point({x} {y})')"
return str(f"insert into coordinates (node, coord) values ('{node}', {coord});")
def sql_delete_coord(node: str) -> str:
return str(f"delete from coordinates where node = '{node}';")
def from_postgis_point(coord: str) -> dict[str, float]:
xy = coord.lower().removeprefix('point(').removesuffix(')').split(' ')
return { 'x': float(xy[0]), 'y': float(xy[1]) }
def get_node_coord(name: str, node: str) -> dict[str, float]:
row = try_read(name, f"select st_astext(coord) as coord_geom from coordinates where node = '{node}'")
if row == None:
write(name, sql_insert_coord(node, 0.0, 0.0))
return {'x': 0.0, 'y': 0.0}
return from_postgis_point(row['coord_geom'])
# DingZQ 2025-01-03, get nodes in extent
# return node id list
# node_id:junction:x:y
def get_nodes_in_extent(name: str, x1: float, y1: float, x2: float, y2: float) -> list[str]:
nodes = []
objs = read_all(name, 'select node, st_astext(coord) as coord_geom from coordinates')
for obj in objs:
node_id = obj['node']
coord = from_postgis_point(obj['coord_geom'])
x = coord['x']
y = coord['y']
if x1 <= x <= x2 and y1 <= y <= y2:
nodes.append(f"{node_id}:junction:{x}:{y}")
return nodes
# DingZQ 2025-01-03, get links in extent
# return link id list
# link_id:pipe:node_id1:node_id2
def get_links_in_extent(name: str, x1: float, y1: float, x2: float, y2: float) -> list[str]:
node_ids = set([s.split(':')[0] for s in get_nodes_in_extent(name, x1, y1, x2, y2)])
all_link_ids = []
with conn[name].cursor(row_factory=dict_row) as cur:
cur.execute(f"select id from pipes")
for record in cur:
all_link_ids.append(record['id'])
links = []
for link_id in all_link_ids:
nodes = get_link_nodes(name, link_id)
if nodes[0] in node_ids and nodes[1] in node_ids:
links.append(f"{link_id}:pipe:{nodes[0]}:{nodes[1]}")
return links
def node_has_coord(name: str, node: str) -> bool:
return try_read(name, f"select node from coordinates where node = '{node}'") != None
#--------------------------------------------------------------
# [EPA2][EPA3][IN][OUT]
# id x y
#--------------------------------------------------------------
# exception ! need merge to node change set !
def inp_in_coord(line: str) -> str:
tokens = line.split()
node = tokens[0]
coord = f"st_geomfromtext('point({tokens[1]} {tokens[2]})')"
return str(f"insert into coordinates (node, coord) values ('{node}', {coord});")
def inp_out_coord(name: str) -> list[str]:
lines = []
objs = read_all(name, 'select node, st_astext(coord) as coord_geom from coordinates')
for obj in objs:
node = obj['node']
coord = from_postgis_point(obj['coord_geom'])
x = coord['x']
y = coord['y']
lines.append(f'{node} {x} {y}')
return lines
+120
View File
@@ -0,0 +1,120 @@
from .database import *
def get_vertex_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'link' : {'type': 'str' , 'optional': False , 'readonly': True },
'coords' : {'type': 'list' , 'optional': False , 'readonly': False,
'element': { 'x' : {'type': 'float' , 'optional': False , 'readonly': False },
'y' : {'type': 'float' , 'optional': False , 'readonly': False } }}}
def get_vertex(name: str, link: str) -> dict[str, Any]:
cus = read_all(name, f"select * from vertices where link = '{link}' order by _order")
cs = []
for r in cus:
cs.append({ 'x': float(r['x']), 'y': float(r['y']) })
return { 'link': link, 'coords': cs }
def _set_vertex(name: str, cs: ChangeSet) -> DbChangeSet:
link = cs.operations[0]['link']
old = get_vertex(name, link)
new = { 'link': link, 'coords': [] }
f_link = f"'{link}'"
# TODO: transaction ?
redo_sql = f"delete from vertices where link = {f_link};"
for xy in cs.operations[0]['coords']:
x, y = float(xy['x']), float(xy['y'])
f_x, f_y = x, y
redo_sql += f"\ninsert into vertices (link, x, y) values ({f_link}, {f_x}, {f_y});"
new['coords'].append({ 'x': x, 'y': y })
undo_sql = f"delete from vertices where link = {f_link};"
for xy in old['coords']:
f_x, f_y = xy['x'], xy['y']
undo_sql += f"\ninsert into vertices (link, x, y) values ({f_link}, {f_x}, {f_y});"
redo_cs = { 'type': 'vertex' } | new
undo_cs = { 'type': 'vertex' } | old
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_vertex(name: str, cs: ChangeSet) -> ChangeSet:
result = _set_vertex(name, cs)
result.redo_cs[0] |= g_update_prefix
result.undo_cs[0] |= g_update_prefix
return execute_command(name, result)
def _add_vertex(name: str, cs: ChangeSet) -> DbChangeSet:
result = _set_vertex(name, cs)
result.redo_cs[0] |= g_add_prefix
result.undo_cs[0] |= g_delete_prefix
return result
def _delete_vertex(name: str, cs: ChangeSet) -> DbChangeSet:
cs.operations[0]['coords'] = []
result = _set_vertex(name, cs)
result.redo_cs[0] |= g_delete_prefix
result.undo_cs[0] |= g_add_prefix
return result
def add_vertex(name: str, cs: ChangeSet) -> ChangeSet:
result = _add_vertex(name, cs)
return execute_command(name, result)
def delete_vertex(name: str, cs: ChangeSet) -> ChangeSet:
result = _delete_vertex(name, cs)
return execute_command(name, result)
def get_all_vertex_links(name: str) -> list[str]:
result : list[str] = []
rows = read_all(name, 'select link from vertices order by link')
for row in rows:
result.append(str(row['link']))
return result
def get_all_vertices(name: str) -> list[dict[str, Any]]:
return read_all(name, 'select * from vertices order by link')
#--------------------------------------------------------------
# [EPA2][IN][OUT]
# id x y
# [EPA3][NOT SUPPORT]
#--------------------------------------------------------------
def inp_in_vertex(line: str) -> str:
tokens = line.split()
link = tokens[0]
x = float(tokens[1])
y = float(tokens[2])
return str(f"insert into vertices (link, x, y) values ('{link}', {x}, {y});")
def inp_out_vertex(name: str) -> list[str]:
lines = []
objs = read_all(name, f"select * from vertices order by _order")
for obj in objs:
link = obj['link']
x = obj['x']
y = obj['y']
lines.append(f"{link} {x} {y}")
return lines
def delete_vertex_by_link(name: str, link: str) -> ChangeSet:
row = try_read(name, f"select * from vertices where link = '{link}'")
if row == None:
return ChangeSet()
return ChangeSet(g_delete_prefix | {'type': 'vertex', 'link' : link})
+137
View File
@@ -0,0 +1,137 @@
from .database import *
def get_label_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'x' : {'type': 'float' , 'optional': False , 'readonly': False},
'y' : {'type': 'float' , 'optional': False , 'readonly': False},
'label' : {'type': 'str' , 'optional': False , 'readonly': False},
'node' : {'type': 'str' , 'optional': True , 'readonly': False} }
def get_label(name: str, x: float, y: float) -> dict[str, Any]:
d = {}
d['x'] = x
d['y'] = y
l = try_read(name, f'select * from labels where x = {x} and y = {y}')
if l == None:
d['label'] = None
d['node'] = None
else:
d['label'] = str(l['label'])
d['node'] = str(l['node']) if l['node'] != None else None
return d
class Label(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'label'
self.x = float(input['x'])
self.y = float(input['y'])
self.label = str(input['label'])
self.node = str(input['node']) if 'node' in input and input['node'] != None else None
self.f_type = f"'{self.type}'"
self.f_x = self.x
self.f_y = self.y
self.f_label = f"'{self.label}'"
self.f_node = f"'{self.node}'" if self.node != None else 'null'
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'x': self.x, 'y': self.y, 'label': self.label, 'node': self.node }
def as_xy_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'x': self.x, 'y': self.y }
def _set_label(name: str, cs: ChangeSet) -> DbChangeSet:
old = Label(get_label(name, cs.operations[0]['x'], cs.operations[0]['y']))
raw_new = get_label(name, cs.operations[0]['x'], cs.operations[0]['y'])
new_dict = cs.operations[0]
schema = get_label_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = Label(raw_new)
redo_sql = f"update labels set label = {new.f_label}, node = {new.f_node} where x = {new.f_x} and y = {new.f_y};"
undo_sql = f"update labels set label = {old.f_label}, node = {old.f_node} where x = {old.f_x} and y = {old.f_y};"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_label(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_label(name, cs))
def _add_label(name: str, cs: ChangeSet) -> DbChangeSet:
new = Label(cs.operations[0])
redo_sql = f"insert into labels (x, y, label, node) values ({new.f_x}, {new.f_y}, {new.f_label}, {new.f_node});"
undo_sql = f"delete from labels where x = {new.f_x} and y = {new.f_y};"
redo_cs = g_add_prefix | new.as_dict()
undo_cs = g_delete_prefix | new.as_xy_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_label(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _add_label(name, cs))
def _delete_label(name: str, cs: ChangeSet) -> DbChangeSet:
old = Label(get_label(name, cs.operations[0]['x'], cs.operations[0]['y']))
redo_sql = f"delete from labels where x = {old.f_x} and y = {old.f_y};"
undo_sql = f"insert into labels (x, y, label, node) values ({old.f_x}, {old.f_y}, {old.f_label}, {old.f_node});"
redo_cs = g_delete_prefix | old.as_xy_dict()
undo_cs = g_add_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_label(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _delete_label(name, cs))
def inp_in_label(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
x = float(tokens[0])
y = float(tokens[1])
label = str(tokens[2])
node = str(tokens[3]) if num >= 4 else None
node = f"'{node}'" if node != None else 'null'
return str(f"insert into labels (x, y, label, node) values ({x}, {y}, '{label}', {node});")
def inp_out_label(name: str) -> list[str]:
lines = []
objs = read_all(name, 'select * from labels')
for obj in objs:
x = obj['x']
y = obj['y']
label = obj['label']
node = obj['node'] if obj['node'] != None else ''
lines.append(f'{x} {y} {label} {node}')
return lines
def unset_label_by_node(name: str, node: str) -> ChangeSet:
cs = ChangeSet()
rows = read_all(name, f"select x, y from labels where node = '{node}'")
for row in rows:
cs.append(g_update_prefix | {'type': 'label', 'x': row['x'], 'y': row['y'], 'node': None})
return cs
+39
View File
@@ -0,0 +1,39 @@
from .database import *
def get_backdrop_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'content' : {'type': 'str' , 'optional': False , 'readonly': False} }
def get_backdrop(name: str) -> dict[str, Any]:
e = read(name, f"select * from backdrop")
return { 'content': e['content'] }
def _set_backdrop(name: str, cs: ChangeSet) -> DbChangeSet:
old = get_backdrop(name)
redo_sql = f"update backdrop set content = '{cs.operations[0]['content']}' where content = '{old['content']}';"
undo_sql = f"update backdrop set content = '{old['content']}' where content = '{cs.operations[0]['content']}';"
redo_cs = g_update_prefix | { 'type': 'backdrop', 'content': cs.operations[0]['content'] }
undo_cs = g_update_prefix | { 'type': 'backdrop', 'content': old['content'] }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_backdrop(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_backdrop(name, cs))
def inp_in_backdrop(section: list[str]) -> str:
if section == []:
return str('')
content = '\n'.join(section)
return str(f"update backdrop set content = '{content}';")
def inp_out_backdrop(name: str) -> list[str]:
obj = str(get_backdrop(name)['content'])
return obj.split('\n')
View File
+123
View File
@@ -0,0 +1,123 @@
from .database import *
SCADA_DEVICE_TYPE_PRESSURE = 'PRESSURE'
SCADA_DEVICE_TYPE_DEMAND = 'DEMAND'
SCADA_DEVICE_TYPE_QUALITY = 'QUALITY'
SCADA_DEVICE_TYPE_LEVEL = 'LEVEL'
SCADA_DEVICE_TYPE_FLOW = 'FLOW'
SCADA_DEVICE_TYPE_UNKNOWN = 'UNKNOWN'
def get_scada_device_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str', 'optional': False, 'readonly': True },
'name' : {'type': 'str', 'optional': True , 'readonly': False},
'address': {'type': 'str', 'optional': True , 'readonly': False},
'sd_type': {'type': 'str', 'optional': True , 'readonly': False}}
def get_scada_device(name: str, id: str) -> dict[str, Any]:
sm = try_read(name, f"select * from scada_device where id = '{id}'")
if sm == None:
return {}
d = {}
d['id'] = str(sm['id'])
d['name'] = str(sm['name']) if sm['name'] != None else None
d['address'] = str(sm['address']) if sm['address'] != None else None
d['sd_type'] = str(sm['sd_type']) if sm['sd_type'] != None else None
return d
class ScadaDevice(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'scada_device'
self.id = str(input['id'])
self.name = str(input['name']) if 'name' in input and input['name'] != None else None
self.address = str(input['address']) if 'address' in input and input['address'] != None else None
self.sd_type = str(input['sd_type']) if 'sd_type' in input and input['sd_type'] != None else None
self.f_type = f"'{self.type}'"
self.f_id = f"'{self.id}'"
self.f_name = f"'{self.name}'" if self.name != None else 'null'
self.f_address = f"'{self.address}'" if self.address != None else 'null'
self.f_sd_type = f"'{self.sd_type}'" if self.sd_type != None else 'null'
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id, 'name': self.name, 'address': self.address, 'sd_type': self.sd_type }
def as_id_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id }
def _set_scada_device(name: str, cs: ChangeSet) -> DbChangeSet:
old = ScadaDevice(get_scada_device(name, cs.operations[0]['id']))
raw_new = get_scada_device(name, cs.operations[0]['id'])
new_dict = cs.operations[0]
schema = get_scada_device_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = ScadaDevice(raw_new)
redo_sql = f"update scada_device set name = {new.f_name}, address = {new.f_address}, sd_type = {new.f_sd_type} where id = {new.f_id};"
undo_sql = f"update scada_device set name = {old.f_name}, address = {old.f_address}, sd_type = {old.f_sd_type} where id = {old.f_id};"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_scada_device(name: str, cs: ChangeSet) -> ChangeSet:
if get_scada_device(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _set_scada_device(name, cs), False)
def _add_scada_device(name: str, cs: ChangeSet) -> DbChangeSet:
new = ScadaDevice(cs.operations[0])
redo_sql = f"insert into scada_device (id, name, address, sd_type) values ({new.f_id}, {new.f_name}, {new.f_address}, {new.f_sd_type});"
undo_sql = f"delete from scada_device where id = {new.f_id};"
redo_cs = g_add_prefix | new.as_dict()
undo_cs = g_delete_prefix | new.as_id_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_scada_device(name: str, cs: ChangeSet) -> ChangeSet:
if get_scada_device(name, cs.operations[0]['id']) != {}:
return ChangeSet()
return execute_command(name, _add_scada_device(name, cs), False)
def _delete_scada_device(name: str, cs: ChangeSet) -> DbChangeSet:
old = ScadaDevice(get_scada_device(name, cs.operations[0]['id']))
redo_sql = f"delete from scada_device where id = {old.f_id};"
undo_sql = f"insert into scada_device (id, name, address, sd_type) values ({old.f_id}, {old.f_name}, {old.f_address}, {old.f_sd_type});"
redo_cs = g_delete_prefix | old.as_id_dict()
undo_cs = g_add_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_scada_device(name: str, cs: ChangeSet) -> ChangeSet:
if get_scada_device(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _delete_scada_device(name, cs), False)
def get_all_scada_device_ids(name: str) -> list[str]:
result : list[str] = []
rows = read_all(name, 'select id from scada_device order by id')
for row in rows:
result.append(str(row['id']))
return result
def get_all_scada_devices(name: str) -> list[dict[str, Any]]:
return read_all(name, 'select * from scada_device order by id')
+191
View File
@@ -0,0 +1,191 @@
from .database import *
from .s0_base import *
from .s24_coordinates import *
def get_junction_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'x' : {'type': 'float' , 'optional': False , 'readonly': False},
'y' : {'type': 'float' , 'optional': False , 'readonly': False},
'elevation' : {'type': 'float' , 'optional': False , 'readonly': False},
'links' : {'type': 'str_list' , 'optional': False , 'readonly': True } }
def get_junction(name: str, id: str) -> dict[str, Any]:
j = try_read(name, f"select * from junctions where id = '{id}'")
if j == None:
return {}
xy = get_node_coord(name, id)
d = {}
d['id'] = str(j['id'])
d['x'] = float(xy['x'])
d['y'] = float(xy['y'])
d['elevation'] = float(j['elevation'])
d['links'] = get_node_links(name, id)
return d
# DingZQ, 2025-03-29
def get_all_junctions(name: str) -> list[dict[str, Any]]:
rows = read_all(name, f"select * from junctions")
if rows == None:
return []
result = []
for row in rows:
d = {}
id = str(row['id'])
xy = get_node_coord(name, id)
d['id'] = id
d['x'] = float(xy['x'])
d['y'] = float(xy['y'])
d['elevation'] = float(row['elevation'])
d['links'] = get_node_links(name, id)
result.append(d)
return result
class Junction(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'junction'
self.id = str(input['id'])
self.x = float(input['x'])
self.y = float(input['y'])
self.elevation = float(input['elevation'])
self.f_type = f"'{self.type}'"
self.f_id = f"'{self.id}'"
self.f_elevation = self.elevation
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id, 'x': self.x, 'y': self.y, 'elevation': self.elevation }
def as_id_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id }
def _set_junction(name: str, cs: ChangeSet) -> DbChangeSet:
old = Junction(get_junction(name, cs.operations[0]['id']))
raw_new = get_junction(name, cs.operations[0]['id'])
new_dict = cs.operations[0]
schema = get_junction_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = Junction(raw_new)
redo_sql = f"update junctions set elevation = {new.f_elevation} where id = {new.f_id};"
redo_sql += f"\n{sql_update_coord(new.id, new.x, new.y)}"
undo_sql = sql_update_coord(old.id, old.x, old.y)
undo_sql += f"\nupdate junctions set elevation = {old.f_elevation} where id = {old.f_id};"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_junction(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_junction(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _set_junction(name, cs))
def _add_junction(name: str, cs: ChangeSet) -> DbChangeSet:
new = Junction(cs.operations[0])
redo_sql = f"insert into _node (id, type) values ({new.f_id}, {new.f_type});"
redo_sql += f"\ninsert into junctions (id, elevation) values ({new.f_id}, {new.f_elevation});"
redo_sql += f"\n{sql_insert_coord(new.id, new.x, new.y)}"
undo_sql = sql_delete_coord(new.id)
undo_sql += f"\ndelete from junctions where id = {new.f_id};"
undo_sql += f"\ndelete from _node where id = {new.f_id};"
redo_cs = g_add_prefix | new.as_dict()
undo_cs = g_delete_prefix | new.as_id_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_junction(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_junction(name, cs.operations[0]['id']) != {}:
return ChangeSet()
return execute_command(name, _add_junction(name, cs))
def _delete_junction(name: str, cs: ChangeSet) -> DbChangeSet:
old = Junction(get_junction(name, cs.operations[0]['id']))
redo_sql = sql_delete_coord(old.id)
redo_sql += f"\ndelete from junctions where id = {old.f_id};"
redo_sql += f"\ndelete from _node where id = {old.f_id};"
undo_sql = f"insert into _node (id, type) values ({old.f_id}, {old.f_type});"
undo_sql += f"\ninsert into junctions (id, elevation) values ({old.f_id}, {old.f_elevation});"
undo_sql += f"\n{sql_insert_coord(old.id, old.x, old.y)}"
redo_cs = g_delete_prefix | old.as_id_dict()
undo_cs = g_add_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_junction(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_junction(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _delete_junction(name, cs))
#--------------------------------------------------------------
# [EPA2]
# [IN]
# id elev. (demand) (demand pattern) ;desc
# [OUT]
# id elev. ;desc
#--------------------------------------------------------------
# [EPA3]
# [IN]
# id elev. (demand) (demand pattern)
# [OUT]
# id elev. * * minpressure fullpressure
#--------------------------------------------------------------
def inp_in_junction(line: str, demand_outside: bool) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
id = str(tokens[0])
elevation = float(tokens[1])
demand = float(tokens[2]) if num_without_desc >= 3 and tokens[2] != '*' else None
pattern = str(tokens[3]) if num_without_desc >= 4 and tokens[3] != '*' else None
pattern = f"'{pattern}'" if pattern != None else 'null'
desc = str(tokens[-1]) if has_desc else None
sql = f"insert into _node (id, type) values ('{id}', 'junction');insert into junctions (id, elevation) values ('{id}', {elevation});"
if demand != None and demand_outside == False:
sql += f"insert into demands (junction, demand, pattern) values ('{id}', {demand}, {pattern});"
return str(sql)
def inp_out_junction(name: str) -> list[str]:
lines = []
objs = read_all(name, 'select * from junctions')
for obj in objs:
id = obj['id']
elev = obj['elevation']
desc = ';'
lines.append(f'{id} {elev} {desc}')
return lines
+90
View File
@@ -0,0 +1,90 @@
from .database import *
def get_scada_device_data_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'device_id' : {'type': 'str' , 'optional': False , 'readonly': True },
'data' : {'type': 'list' , 'optional': False , 'readonly': False,
'element': { 'time' : {'type': 'str' , 'optional': False , 'readonly': False },
'value' : {'type': 'float' , 'optional': False , 'readonly': False } }}}
def get_scada_device_data(name: str, device_id: str) -> dict[str, Any]:
sds = read_all(name, f"select * from scada_device_data where device_id = '{device_id}' order by time")
ds = []
for r in sds:
ds.append({ 'time': str(r['time']), 'value': float(r['value']) })
return { 'device_id': device_id, 'data': ds }
def _set_scada_device_data(name: str, cs: ChangeSet) -> DbChangeSet:
device_id = cs.operations[0]['device_id']
old = get_scada_device_data(name, device_id)
new = { 'device_id': device_id, 'data': [] }
f_device_id = f"'{device_id}'"
# TODO: transaction ?
redo_sql = f"delete from scada_device_data where device_id = {f_device_id};"
for tv in cs.operations[0]['data']:
time, value = str(tv['time']), float(tv['value'])
f_time, f_value = f"'{time}'", value
redo_sql += f"\ninsert into scada_device_data (device_id, time, value) values ({f_device_id}, {f_time}, {f_value});"
new['data'].append({ 'time': time, 'value': value })
undo_sql = f"delete from scada_device_data where device_id = {f_device_id};"
for tv in old['data']:
time, value = str(tv['time']), float(tv['value'])
f_time, f_value = f"'{time}'", value
undo_sql += f"\ninsert into scada_device_data (device_id, time, value) values ({f_device_id}, {f_time}, {f_value});"
redo_cs = g_update_prefix | { 'type': 'scada_device_data' } | new
undo_cs = g_update_prefix | { 'type': 'scada_device_data' } | old
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_scada_device_data(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_scada_device_data(name, cs), False)
def _add_scada_device_data(name: str, cs: ChangeSet) -> DbChangeSet:
values = cs.operations[0]
device_id = values['device_id']
time = values['time']
value = float(values['value'])
redo_sql = f"insert into scada_device_data (device_id, time, value) values ('{device_id}', '{time}', {value});"
undo_sql = f"delete from scada_device_data where device_id = '{device_id}' and time = '{time}';"
redo_cs = g_add_prefix | { 'type': 'scada_device_data', 'device_id': device_id, 'time': time, 'value': value }
undo_cs = g_delete_prefix | { 'type': 'scada_device_data', 'device_id': device_id, 'time': time }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_scada_device_data(name: str, cs: ChangeSet) -> ChangeSet:
row = try_read(name, f"select * from scada_device_data where device_id = '{cs.operations[0]['device_id']}' and time = '{cs.operations[0]['time']}'")
if row != None:
return ChangeSet()
return execute_command(name, _add_scada_device_data(name, cs), False)
def _delete_scada_device_data(name: str, cs: ChangeSet) -> DbChangeSet:
values = cs.operations[0]
device_id = values['device_id']
time = values['time']
value = float(read(name, f"select * from scada_device_data where device_id = '{device_id}' and time = '{time}'")['value'])
redo_sql = f"delete from scada_device_data where device_id = '{device_id}' and time = '{time}';"
undo_sql = f"insert into scada_device_data (device_id, time, value) values ('{device_id}', '{time}', {value});"
redo_cs = g_delete_prefix | { 'type': 'scada_device_data', 'device_id': device_id, 'time': time }
undo_cs = g_add_prefix | { 'type': 'scada_device_data', 'device_id': device_id, 'time': time, 'value': value }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_scada_device_data(name: str, cs: ChangeSet) -> ChangeSet:
row = try_read(name, f"select * from scada_device_data where device_id = '{cs.operations[0]['device_id']}' and time = '{cs.operations[0]['time']}'")
if row == None:
return ChangeSet()
return execute_command(name, _delete_scada_device_data(name, cs), False)
+197
View File
@@ -0,0 +1,197 @@
from .database import *
from .s0_base import *
SCADA_TYPE_PRESSURE = 'PRESSURE'
SCADA_TYPE_DEMAND = 'DEMAND'
SCADA_TYPE_QUALITY = 'QUALITY'
SCADA_TYPE_LEVEL = 'LEVEL'
SCADA_TYPE_FLOW = 'FLOW'
SCADA_MODEL_TYPE_JUNCTION = 'JUNCTION'
SCADA_MODEL_TYPE_RESERVOIR = 'RESERVOIR'
SCADA_MODEL_TYPE_TANK = 'TANK'
SCADA_MODEL_TYPE_PIPE = 'PIPE'
SCADA_MODEL_TYPE_PUMP = 'PUMP'
SCADA_MODEL_TYPE_VALVE = 'VALVE'
SCADA_ELEMENT_STATUS_OFFLINE = 'OFF'
SCADA_ELEMENT_STATUS_ONLINE = 'ON'
_scada_model_types = [SCADA_MODEL_TYPE_JUNCTION, SCADA_MODEL_TYPE_RESERVOIR, SCADA_MODEL_TYPE_TANK, SCADA_MODEL_TYPE_PIPE, SCADA_MODEL_TYPE_PUMP, SCADA_MODEL_TYPE_VALVE]
def _check_model(name: str, cs: ChangeSet) -> bool:
has_model_id = 'model_id' in cs.operations[0]
has_model_type = 'model_type' in cs.operations[0]
if has_model_id and has_model_type:
pass
elif has_model_id and not has_model_type:
return False
elif not has_model_id and has_model_type:
return False
elif not has_model_id and not has_model_type:
return True
_model_id = cs.operations[0]['model_id']
_model_type = cs.operations[0]['model_type']
if _model_type == SCADA_MODEL_TYPE_JUNCTION:
return is_junction(name, _model_id)
elif _model_type == SCADA_MODEL_TYPE_RESERVOIR:
return is_reservoir(name, _model_id)
elif _model_type == SCADA_MODEL_TYPE_TANK:
return is_tank(name, _model_id)
elif _model_type == SCADA_MODEL_TYPE_PIPE:
return is_pipe(name, _model_id)
elif _model_type == SCADA_MODEL_TYPE_PUMP:
return is_pump(name, _model_id)
elif _model_type == SCADA_MODEL_TYPE_VALVE:
return is_valve(name, _model_id)
return False
def get_scada_element_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'x' : {'type': 'float' , 'optional': False , 'readonly': False},
'y' : {'type': 'float' , 'optional': False , 'readonly': False},
'device_id' : {'type': 'str' , 'optional': True , 'readonly': False},
'model_id' : {'type': 'str' , 'optional': True , 'readonly': False},
'model_type' : {'type': 'str' , 'optional': True , 'readonly': False},
'status' : {'type': 'str' , 'optional': True , 'readonly': False} }
def get_scada_element(name: str, id: str) -> dict[str, Any]:
sm = try_read(name, f"select * from scada_element where id = '{id}'")
if sm == None:
return {}
d = {}
d['id'] = str(sm['id'])
d['x'] = float(sm['x'])
d['y'] = float(sm['y'])
d['device_id'] = str(sm['device_id']) if sm['device_id'] != None else None
d['model_id'] = str(sm['model_id']) if sm['model_id'] != None else None
d['model_type'] = str(sm['model_type']) if sm['model_type'] != None else None
d['status'] = str(sm['status'])
return d
class ScadaModel(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'scada_element'
self.id = str(input['id'])
self.x = float(input['x'])
self.y = float(input['y'])
self.device_id = str(input['device_id']) if 'device_id' in input and input['device_id'] != None else None
self.model_id = str(input['model_id']) if 'model_id' in input and input['model_id'] != None else None
self.model_type = str(input['model_type']) if 'model_type' in input and input['model_type'] != None else None
self.status = str(input['status']) if 'status' in input and input['status'] != None else SCADA_ELEMENT_STATUS_OFFLINE
self.f_type = f"'{self.type}'"
self.f_id = f"'{self.id}'"
self.f_x = self.x
self.f_y = self.y
self.f_device_id = f"'{self.device_id}'" if self.device_id != None else 'null'
self.f_model_id = f"'{self.model_id}'" if self.model_id != None else 'null'
self.f_model_type = f"'{self.model_type}'" if self.model_type != None else 'null'
self.f_status = f"'{self.status}'"
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id, 'x': self.x, 'y': self.y, 'device_id': self.device_id, 'model_id': self.model_id, 'model_type': self.model_type, 'status': self.status }
def as_id_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id }
def _set_scada_element(name: str, cs: ChangeSet) -> DbChangeSet:
old = ScadaModel(get_scada_element(name, cs.operations[0]['id']))
raw_new = get_scada_element(name, cs.operations[0]['id'])
new_dict = cs.operations[0]
schema = get_scada_element_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = ScadaModel(raw_new)
redo_sql = f"update scada_element set x = {new.f_x}, y = {new.f_y}, device_id = {new.f_device_id}, model_id = {new.f_model_id}, model_type = {new.f_model_type}, status = {new.f_status} where id = {new.f_id};"
undo_sql = f"update scada_element set x = {old.f_x}, y = {old.f_y}, device_id = {old.f_device_id}, model_id = {old.f_model_id}, model_type = {old.f_model_type}, status = {old.f_status} where id = {old.f_id};"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_scada_element(name: str, cs: ChangeSet) -> ChangeSet:
if get_scada_element(name, cs.operations[0]['id']) == {}:
return ChangeSet()
if _check_model(name, cs) == False:
return ChangeSet()
return execute_command(name, _set_scada_element(name, cs))
def _add_scada_element(name: str, cs: ChangeSet) -> DbChangeSet:
new = ScadaModel(cs.operations[0])
redo_sql = f"insert into scada_element (id, x, y, device_id, model_id, model_type, status) values ({new.f_id}, {new.f_x}, {new.f_y}, {new.f_device_id}, {new.f_model_id}, {new.f_model_type}, {new.f_status});"
undo_sql = f"delete from scada_element where id = {new.f_id};"
redo_cs = g_add_prefix | new.as_dict()
undo_cs = g_delete_prefix | new.as_id_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_scada_element(name: str, cs: ChangeSet) -> ChangeSet:
if get_scada_element(name, cs.operations[0]['id']) != {}:
return ChangeSet()
if _check_model(name, cs) == False:
return ChangeSet()
return execute_command(name, _add_scada_element(name, cs))
def _delete_scada_element(name: str, cs: ChangeSet) -> DbChangeSet:
old = ScadaModel(get_scada_element(name, cs.operations[0]['id']))
redo_sql = f"delete from scada_element where id = {old.f_id};"
undo_sql = f"insert into scada_element (id, x, y, device_id, model_id, model_type, status) values ({old.f_id}, {old.f_x}, {old.f_y}, {old.f_device_id}, {old.f_model_id}, {old.f_model_type}, {old.f_status});"
redo_cs = g_delete_prefix | old.as_id_dict()
undo_cs = g_add_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_scada_element(name: str, cs: ChangeSet) -> ChangeSet:
if get_scada_element(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _delete_scada_element(name, cs))
def get_all_scada_element_ids(name: str) -> list[str]:
result : list[str] = []
rows = read_all(name, 'select id from scada_element order by id')
for row in rows:
result.append(str(row['id']))
return result
#
# create table scada_element
# (
# id text primary key
# , x float8 not null
# , y float8 not null
# , device_id text references scada_device(id)
# , model_id varchar(32) -- add constraint in API
# , model_type scada_model_type
# , status scada_element_status not null default 'OFF'
# );
#
# 返回listlist里每个item是dict,内容是 'id':'abc' 这样
# scada_model type 是类似pressureflow之类的,是由Device 决定 的
def get_all_scada_elements(name: str) -> list[dict[str, Any]]:
return read_all(name, 'select * from scada_element order by id')
+95
View File
@@ -0,0 +1,95 @@
from .database import *
from .s32_region_util import from_postgis_polygon, to_postgis_polygon
def get_region_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'boundary' : {'type': 'tuple_list' , 'optional': False , 'readonly': False} }
def get_region(name: str, id: str) -> dict[str, Any]:
r = try_read(name, f"select id, st_astext(boundary) as boundary_geom from region where id = '{id}'")
if r == None:
return {}
d = {}
d['id'] = str(r['id'])
d['boundary'] = from_postgis_polygon(str(r['boundary_geom']))
return d
def _set_region(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
new = cs.operations[0]['boundary']
old = get_region(name, id)['boundary']
redo_sql = f"update region set boundary = st_geomfromtext('{to_postgis_polygon(new)}') where id = '{id}';"
undo_sql = f"update region set boundary = st_geomfromtext('{to_postgis_polygon(old)}') where id = '{id}';"
redo_cs = g_update_prefix | { 'type': 'region', 'id': id, 'boundary': new }
undo_cs = g_update_prefix | { 'type': 'region', 'id': id, 'boundary': old }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_region(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0] or 'boundary' not in cs.operations[0]:
return ChangeSet()
b = cs.operations[0]['boundary']
if len(b) < 4 or b[0] != b[-1]:
return ChangeSet()
if get_region(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _set_region(name, cs))
def _add_region(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
new = cs.operations[0]['boundary']
redo_sql = f"insert into region (id, boundary) values ('{id}', '{to_postgis_polygon(new)}');"
undo_sql = f"delete from region where id = '{id}';"
redo_cs = g_add_prefix | { 'type': 'region', 'id': id, 'boundary': new }
undo_cs = g_delete_prefix | { 'type': 'region', 'id': id }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_region(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0] or 'boundary' not in cs.operations[0]:
return ChangeSet()
b = cs.operations[0]['boundary']
if len(b) < 4 or b[0] != b[-1]:
return ChangeSet()
if get_region(name, cs.operations[0]['id']) != {}:
return ChangeSet()
return execute_command(name, _add_region(name, cs))
def _delete_region(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
old = get_region(name, id)['boundary']
redo_sql = f"delete from region where id = '{id}';"
undo_sql = f"insert into region (id, boundary) values ('{id}', '{to_postgis_polygon(old)}');"
redo_cs = g_delete_prefix | { 'type': 'region', 'id': id }
undo_cs = g_add_prefix | { 'type': 'region', 'id': id, 'boundary': old }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_region(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_region(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _delete_region(name, cs))
def inp_in_region(line: str) -> str:
tokens = line.split()
return str(f"insert into _region (id, type) values ('{tokens[0]}', '{tokens[1]}');")
def inp_in_bound(line: str) -> str:
tokens = line.split()
return tokens[0]
def inp_in_regionnodes(line: str)->str:
tokens = line.split()
return tokens[0]
+463
View File
@@ -0,0 +1,463 @@
import ctypes
import platform
import os
import math
from typing import Any
from .s0_base import get_node_links, get_link_nodes, is_pipe
from .s5_pipes import get_pipe
from .database import read, try_read, read_all, write
from .s24_coordinates import node_has_coord, get_node_coord
def from_postgis_polygon(polygon: str) -> list[tuple[float, float]]:
boundary = polygon.lower().removeprefix('polygon((').removesuffix('))').split(',')
xys = []
for pt in boundary:
xy = pt.split(' ')
xys.append((float(xy[0]), float(xy[1])))
return xys
def to_postgis_polygon(boundary: list[tuple[float, float]]) -> str:
polygon = ''
for pt in boundary:
polygon += f'{pt[0]} {pt[1]},'
return str(f'polygon(({polygon[:-1]}))')
def to_postgis_linestring(boundary: list[tuple[float, float]]) -> str:
line = ''
for pt in boundary:
line += f'{pt[0]} {pt[1]},'
return str(f'linestring({line[:-1]})')
def get_nodes_in_boundary(name: str, boundary: list[tuple[float, float]]) -> list[str]:
api = 'get_nodes_in_boundary'
write(name, f"delete from temp_region where id = '{api}'")
write(name, f"insert into temp_region (id, boundary) values ('{api}', '{to_postgis_polygon(boundary)}')")
nodes: list[str] = []
for row in read_all(name, f"select c.node from coordinates as c, temp_region as r where ST_Intersects(c.coord, r.boundary) and r.id = '{api}'"):
nodes.append(row['node'])
write(name, f"delete from temp_region where id = '{api}'")
return nodes
def _get_links_on_boundary(name: str, nodes: list[str]) -> list[str]:
links: list[str] = []
for node in nodes:
node_links = get_node_links(name, node)
for link in node_links:
if link in links:
continue
link_nodes = get_link_nodes(name, link)
if link_nodes[0] in nodes and link_nodes[1] not in nodes:
links.append(link)
elif link_nodes[0] not in nodes and link_nodes[1] in nodes:
links.append(link)
return links
# if region is general or wda => get_nodes_in_boundary
# if region is dma, sa or vd => get stored nodes in table
def get_nodes_in_region(name: str, region_id: str) -> list[str]:
nodes: list[str] = []
row = try_read(name, f"select r_type from region where id = '{region_id}'")
if row == None:
return nodes
r_type = str(row['r_type'])
if r_type == 'DMA' or r_type == 'SA' or r_type == 'VD':
table = ''
if r_type == 'DMA':
table = 'region_dma'
elif r_type == 'SA':
table = 'region_sa'
elif r_type == 'VD':
table = 'region_vd'
if table != '':
row = try_read(name, f"select nodes from {table} where id = '{region_id}'")
if row != None:
nodes = eval(str(row['nodes']))
if nodes == []:
for row in read_all(name, f"select c.node from coordinates as c, region as r where ST_Intersects(c.coord, r.boundary) and r.id = '{region_id}'"):
nodes.append(row['node'])
return nodes
def get_links_on_region_boundary(name: str, region_id: str) -> list[str]:
nodes = get_nodes_in_region(name, region_id)
print(nodes)
return _get_links_on_boundary(name, nodes)
def calculate_convex_hull(name: str, nodes: list[str]) -> list[tuple[float, float]]:
write(name, f'delete from temp_node')
for node in nodes:
write(name, f"insert into temp_node values ('{node}')")
# TODO: check none
polygon = read(name, f'select st_astext(st_convexhull(st_collect(array(select coord from coordinates where node in (select * from temp_node))))) as boundary' )['boundary']
write(name, f'delete from temp_node')
return from_postgis_polygon(polygon)
def _verify_platform():
_platform = platform.system()
if _platform != "Windows":
raise Exception(f'Platform {_platform} unsupported (not yet)')
def _normal(v: tuple[float, float]) -> tuple[float, float]:
l = math.sqrt(v[0] * v[0] + v[1] * v[1])
return (v[0] / l, v[1] / l)
def _angle(v: tuple[float, float]) -> float:
if v[0] >= 0 and v[1] >= 0:
return math.asin(v[1])
elif v[0] <= 0 and v[1] >= 0:
return math.pi - math.asin(v[1])
elif v[0] <= 0 and v[1] <= 0:
return math.asin(-v[1]) + math.pi
elif v[0] >= 0 and v[1] <= 0:
return math.pi * 2 - math.asin(-v[1])
return 0
def _angle_of_node_link(node: str, link: str, nodes, links) -> float:
n1 = node
n2 = links[link]['node1'] if n1 == links[link]['node2'] else links[link]['node2']
x1, y1 = nodes[n1]['x'], nodes[n1]['y']
x2, y2 = nodes[n2]['x'], nodes[n2]['y']
if y1 == y2:
v = ((x2 - x1) / abs(x2 - x1), 0.0)
else:
v = _normal((x2 - x1, y2 - y1))
return _angle(v)
class Topology:
def __init__(self, db: str, nodes: list[str]) -> None:
self._nodes: dict[str, Any] = {}
self._max_x_node = ''
self._node_list: list[str] = []
for node in nodes:
if not node_has_coord(db, node):
continue
if get_node_links(db, node) == 0:
continue
self._nodes[node] = get_node_coord(db, node) | { 'links': [] }
self._node_list.append(node)
if self._max_x_node == '' or self._nodes[node]['x'] > self._nodes[self._max_x_node]['x']:
self._max_x_node = node
self._links: dict[str, Any] = {}
self._link_list: list[str] = []
for node in self._nodes:
for link in get_node_links(db, node):
candidate = True
link_nodes = get_link_nodes(db, link)
for link_node in link_nodes:
if link_node not in self._nodes:
candidate = False
break
if candidate:
length = get_pipe(db, link)['length'] if is_pipe(db, link) else 0.0
self._links[link] = { 'node1' : link_nodes[0], 'node2' : link_nodes[1], 'length' : length }
self._link_list.append(link)
if link not in self._nodes[link_nodes[0]]['links']:
self._nodes[link_nodes[0]]['links'].append(link)
if link not in self._nodes[link_nodes[1]]['links']:
self._nodes[link_nodes[1]]['links'].append(link)
def nodes(self):
return self._nodes
def node_list(self):
return self._node_list
def max_x_node(self):
return self._max_x_node
def links(self):
return self._links
def link_list(self):
return self._link_list
def _calculate_boundary(cursor: str, t_nodes: dict[str, Any], t_links: dict[str, Any]) -> tuple[list[str], dict[str, list[str]], list[tuple[float, float]]]:
in_angle = 0
vertices: list[str] = []
path: dict[str, list[str]] = {}
while True:
# prevent duplicated node
if len(vertices) > 0 and cursor == vertices[-1]:
break
# prevent duplicated path
if len(vertices) >= 3 and vertices[0] == vertices[-1] and vertices[1] == cursor:
break
vertices.append(cursor)
sorted_links = []
overlapped_link = ''
for link in t_nodes[cursor]['links']:
angle = _angle_of_node_link(cursor, link, t_nodes, t_links)
if angle == in_angle:
overlapped_link = link
continue
sorted_links.append((angle, link))
# work into a branch, return
if len(sorted_links) == 0:
path[overlapped_link] = []
cursor = vertices[-2]
in_angle = _angle_of_node_link(cursor, overlapped_link, t_nodes, t_links)
continue
sorted_links = sorted(sorted_links, key=lambda s:s[0])
out_link = sorted_links[0][1]
for angle, link in sorted_links:
if angle > in_angle:
out_link = link
break
path[out_link] = []
cursor = t_links[out_link]['node1'] if cursor == t_links[out_link]['node2'] else t_links[out_link]['node2']
in_angle = _angle_of_node_link(cursor, out_link, t_nodes, t_links)
boundary: list[tuple[float, float]] = []
for node in vertices:
boundary.append((t_nodes[node]['x'], t_nodes[node]['y']))
return (vertices, path, boundary)
def _collect_new_links(in_links: dict[str, list[str]], t_nodes: dict[str, Any], t_links: dict[str, Any], new_nodes: dict[str, Any], new_links: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
for link, pts in in_links.items():
node1 = t_links[link]['node1']
node2 = t_links[link]['node2']
x1, x2 = t_nodes[node1]['x'], t_nodes[node2]['x']
y1, y2 = t_nodes[node1]['y'], t_nodes[node2]['y']
if node1 not in new_nodes:
new_nodes[node1] = { 'x': x1, 'y': y1, 'links': [] }
if node2 not in new_nodes:
new_nodes[node2] = { 'x': x2, 'y': y2, 'links': [] }
x_delta = x2 - x1
y_delta = y2 - y1
use_x = abs(x_delta) > abs(y_delta)
if len(pts) == 0:
new_links[link] = t_links[link]
else:
sorted_nodes: list[tuple[float, str]] = []
sorted_nodes.append((0.0, node1))
sorted_nodes.append((1.0, node2))
i = 0
for pt in pts:
x, y = new_nodes[pt]['x'], new_nodes[pt]['y']
percent = ((x - x1) / x_delta) if use_x else ((y - y1) / y_delta)
sorted_nodes.append((percent, pt))
i += 1
sorted_nodes = sorted(sorted_nodes, key=lambda s:s[0])
for i in range(1, len(sorted_nodes)):
l = sorted_nodes[i - 1][1]
r = sorted_nodes[i][1]
new_link = f'LINK_[{l}]_[{r}]'
new_links[new_link] = { 'node1': l, 'node2': r }
return (new_nodes, new_links)
def calculate_boundary(name: str, nodes: list[str], accurate = False) -> list[tuple[float, float]]:
topology = Topology(name, nodes)
t_nodes = topology.nodes()
t_links = topology.links()
vertices, path, boundary = _calculate_boundary(topology.max_x_node(), t_nodes, t_links)
if not accurate:
return boundary
api = 'calculate_boundary'
write(name, f"delete from temp_region where id = '{api}'")
# use linestring instead of polygon to reduce strict limitation
# TODO: linestring can not work well
write(name, f"insert into temp_region (id, boundary) values ('{api}', '{to_postgis_polygon(boundary)}')")
write(name, f'delete from temp_node')
for node in nodes:
write(name, f"insert into temp_node values ('{node}')")
for row in read_all(name, f"select n.node from coordinates as c, temp_node as n, temp_region as r where c.node = n.node and ST_Intersects(c.coord, r.boundary) and r.id = '{api}'"):
node = row['node']
write(name, f"delete from temp_node where node = '{node}'")
outside_nodes: list[str] = []
for row in read_all(name, "select node from temp_node"):
outside_nodes.append(row['node'])
# no outside nodes, return
if len(outside_nodes) == 0:
write(name, f'delete from temp_node')
write(name, f"delete from temp_region where id = '{api}'")
return boundary
new_nodes: dict[str, Any] = {}
new_links: dict[str, Any] = {}
boundary_links: dict[str, list[str]] = {}
write(name, "delete from temp_link_2")
for node in outside_nodes:
for link in t_nodes[node]['links']:
node1 = t_links[link]['node1']
node2 = t_links[link]['node2']
if node1 in outside_nodes and node2 not in outside_nodes and node2 not in vertices and link:
if link not in boundary:
boundary_links[link] = []
line = f"LINESTRING({t_nodes[node1]['x']} {t_nodes[node1]['y']}, {t_nodes[node2]['x']} {t_nodes[node2]['y']})"
write(name, f"insert into temp_link_2 values ('{link}', '{line}')")
if node2 in outside_nodes and node1 not in outside_nodes and node1 not in vertices:
if link not in boundary:
boundary_links[link] = []
line = f"LINESTRING({t_nodes[node1]['x']} {t_nodes[node1]['y']}, {t_nodes[node2]['x']} {t_nodes[node2]['y']})"
write(name, f"insert into temp_link_2 values ('{link}', '{line}')")
if node1 in outside_nodes and node2 in outside_nodes:
x1, x2 = t_nodes[node1]['x'], t_nodes[node2]['x']
y1, y2 = t_nodes[node1]['y'], t_nodes[node2]['y']
if node1 not in new_nodes:
new_nodes[node1] = { 'x': x1, 'y': y1, 'links': [] }
if node2 not in new_nodes:
new_nodes[node2] = { 'x': x2, 'y': y2, 'links': [] }
if link not in new_links:
new_links[link] = t_links[link]
# no boundary links, return
if len(boundary_links) == 0:
write(name, "delete from temp_link_2")
write(name, f'delete from temp_node')
write(name, f"delete from temp_region where id = '{api}'")
return boundary
write(name, "delete from temp_link_1")
for link, _ in path.items():
node1 = t_links[link]['node1']
node2 = t_links[link]['node2']
line = f"LINESTRING({t_nodes[node1]['x']} {t_nodes[node1]['y']}, {t_nodes[node2]['x']} {t_nodes[node2]['y']})"
write(name, f"insert into temp_link_1 (link, geom) values ('{link}', '{line}')")
has_intersection = False
for row in read_all(name, f"select l1.link as l, l2.link as r, st_astext(st_intersection(l1.geom, l2.geom)) as p from temp_link_1 as l1, temp_link_2 as l2 where st_intersects(l1.geom, l2.geom)"):
has_intersection = True
link1, link2, pt = str(row['l']), str(row['r']), str(row['p'])
pts = pt.lower().removeprefix('point(').removesuffix(')').split(' ')
xy = (float(pts[0]), float(pts[1]))
new_node = f'NODE_[{link1}]_[{link2}]'
new_nodes[new_node] = { 'x': xy[0], 'y': xy[1], 'links': [] }
path[link1].append(new_node)
boundary_links[link2].append(new_node)
# no intersection, return
if not has_intersection:
write(name, "delete from temp_link_1")
write(name, "delete from temp_link_2")
write(name, 'delete from temp_node')
write(name, f"delete from temp_region where id = '{api}'")
return boundary
new_nodes, new_links = _collect_new_links(path, t_nodes, t_links, new_nodes, new_links)
new_nodes, new_links = _collect_new_links(boundary_links, t_nodes, t_links, new_nodes, new_links)
for link, values in new_links.items():
new_nodes[values['node1']]['links'].append(link)
new_nodes[values['node2']]['links'].append(link)
_, _, boundary = _calculate_boundary(topology.max_x_node(), new_nodes, new_links)
write(name, "delete from temp_link_1")
write(name, "delete from temp_link_2")
write(name, 'delete from temp_node')
write(name, f"delete from temp_region where id = '{api}'")
return boundary
'''
# CClipper2.dll
# int inflate_paths(double* path, size_t size, double delta, int jt, int et, double miter_limit, int precision, double arc_tolerance, double** out_path, size_t* out_size);
# int simplify_paths(double* path, size_t size, double epsilon, int is_closed_path, double** out_path, size_t* out_size);
# void free_paths(double** paths);
'''
def inflate_boundary(name: str, boundary: list[tuple[float, float]], delta: float = 0.5) -> list[tuple[float, float]]:
if boundary[0] == boundary[-1]:
del(boundary[-1])
lib = ctypes.CDLL(os.path.join(os.getcwd(), 'api', 'CClipper2.dll'))
c_size = ctypes.c_size_t(len(boundary) * 2)
c_path = (ctypes.c_double * c_size.value)()
i = 0
for xy in boundary:
c_path[i] = xy[0]
i += 1
c_path[i] = xy[1]
i += 1
c_delta = ctypes.c_double(delta)
JoinType_Square, JoinType_Round, JoinType_Miter = 0, 1, 2
c_jt = ctypes.c_int(JoinType_Square)
EndType_Polygon, EndType_Joined, EndType_Butt, EndType_Square, EndType_Round = 0, 1, 2, 3, 4
c_et = ctypes.c_int(EndType_Polygon)
c_miter_limit = ctypes.c_double(2.0)
c_precision = ctypes.c_int(2)
c_arc_tolerance = ctypes.c_double(0.0)
c_out_path = ctypes.POINTER(ctypes.c_double)()
c_out_size = ctypes.c_size_t(0)
lib.inflate_paths(c_path, c_size, c_delta, c_jt, c_et, c_miter_limit, c_precision, c_arc_tolerance, ctypes.byref(c_out_path), ctypes.byref(c_out_size))
if c_out_size.value == 0:
lib.free_paths(ctypes.byref(c_out_path))
return []
# TODO: simplify_paths :)
result: list[tuple[float, float]] = []
for i in range(0, c_out_size.value, 2):
result.append((c_out_path[i], c_out_path[i + 1]))
result.append(result[0])
lib.free_paths(ctypes.byref(c_out_path))
return result
def inflate_region(name: str, region_id: str, delta: float = 0.5) -> list[tuple[float, float]]:
r = try_read(name, f"select id, st_astext(boundary) as boundary_geom from region where id = '{region_id}'")
if r == None:
return []
boundary = from_postgis_polygon(str(r['boundary_geom']))
return inflate_boundary(name, boundary, delta)
if __name__ == '__main__':
_verify_platform()
+230
View File
@@ -0,0 +1,230 @@
from .database import *
from .s0_base import is_node
from .s32_region_util import to_postgis_polygon
from .s32_region import get_region
def get_district_metering_area_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'boundary' : {'type': 'tuple_list' , 'optional': False , 'readonly': False },
'parent' : {'type': 'str' , 'optional': True , 'readonly': False },
'level' : {'type': 'int' , 'optional': False , 'readonly': True } }
def get_district_metering_area(name: str, id: str) -> dict[str, Any]:
dma = get_region(name, id)
if dma == {}:
return {}
r = try_read(name, f"select * from region_dma where id = '{id}'")
if r == None:
return {}
dma['parent'] = r['parent']
dma['nodes'] = list(eval(r['nodes']))
dma['level'] = 1
if dma['parent'] != None:
parent = dma['parent']
while parent != None:
parent = read(name, f"select parent from region_dma where id = '{parent}'")['parent']
dma['level'] += 1
return dma
def _set_district_metering_area(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
new_boundary = cs.operations[0]['boundary']
old_boundary = get_region(name, id)['boundary']
new_parent = cs.operations[0]['parent']
f_new_parent = f"'{new_parent}'" if new_parent != None else 'null'
new_nodes = cs.operations[0]['nodes']
str_new_nodes = str(new_nodes).replace("'", "''")
old = get_district_metering_area(name, id)
old_parent = old['parent']
f_old_parent = f"'{old_parent}'" if old_parent != None else 'null'
old_nodes = old['nodes']
str_old_nodes = str(old_nodes).replace("'", "''")
redo_sql = f"update region set boundary = st_geomfromtext('{to_postgis_polygon(new_boundary)}') where id = '{id}';"
redo_sql += f"update region_dma set parent = {f_new_parent}, nodes = '{str_new_nodes}' where id = '{id}';"
undo_sql = f"update region_dma set parent = {f_old_parent}, nodes = '{str_old_nodes}' where id = '{id}';"
undo_sql += f"update region set boundary = st_geomfromtext('{to_postgis_polygon(old_boundary)}') where id = '{id}';"
redo_cs = g_update_prefix | { 'type': 'district_metering_area', 'id': id, 'boundary': new_boundary, 'parent': new_parent, 'nodes': new_nodes }
undo_cs = g_update_prefix | { 'type': 'district_metering_area', 'id': id, 'boundary': old_boundary, 'parent': old_parent, 'nodes': old_nodes }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet:
ops = cs.operations
if len(cs.operations) == 0:
return ChangeSet()
op = ops[0]
if 'id' not in op:
return ChangeSet()
dma = get_district_metering_area(name, op['id'])
if dma == {}:
return ChangeSet()
if 'boundary' not in op:
op['boundary'] = dma['boundary']
else:
b = op['boundary']
if len(b) < 4 or b[0] != b[-1]:
return ChangeSet()
if 'parent' not in op:
op['parent'] = dma['parent']
if op['parent'] != None and get_district_metering_area(name, op['parent']) == {}:
return ChangeSet()
if 'nodes' not in op:
op['nodes'] = dma['nodes']
else:
for node in op['nodes']:
if not is_node(name, node):
return ChangeSet()
return execute_command(name, _set_district_metering_area(name, cs))
def _add_district_metering_area(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
boundary = cs.operations[0]['boundary']
parent = cs.operations[0]['parent']
f_parent = f"'{parent}'" if parent != None else 'null'
nodes = cs.operations[0]['nodes']
str_nodes = str(nodes).replace("'", "''")
redo_sql = f"insert into region (id, boundary, r_type) values ('{id}', '{to_postgis_polygon(boundary)}', 'DMA');"
redo_sql += f"insert into region_dma (id, parent, nodes) values ('{id}', {f_parent}, '{str_nodes}');"
undo_sql = f"delete from region_dma where id = '{id}';"
undo_sql += f"delete from region where id = '{id}';"
redo_cs = g_add_prefix | { 'type': 'district_metering_area', 'id': id, 'boundary': boundary, 'parent': parent, 'nodes': nodes }
undo_cs = g_delete_prefix | { 'type': 'district_metering_area', 'id': id }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet:
ops = cs.operations
if len(cs.operations) == 0:
return ChangeSet()
op = ops[0]
if 'id' not in op:
return ChangeSet()
dma = get_district_metering_area(name, op['id'])
if dma != {}:
return ChangeSet()
if 'boundary' not in op:
return ChangeSet()
else:
b = op['boundary']
if len(b) < 4 or b[0] != b[-1]:
return ChangeSet()
if 'parent' not in op:
op['parent'] = None
if op['parent'] != None and get_district_metering_area(name, op['parent']) == {}:
return ChangeSet()
if 'nodes' not in op:
op['nodes'] = []
else:
for node in op['nodes']:
if not is_node(name, node):
return ChangeSet()
return execute_command(name, _add_district_metering_area(name, cs))
def _delete_district_metering_area(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
dma = get_district_metering_area(name, id)
boundary = dma['boundary']
parent = dma['parent']
f_parent = f"'{parent}'" if parent != None else 'null'
nodes = dma['nodes']
str_nodes = str(nodes).replace("'", "''")
redo_sql = f"delete from region_dma where id = '{id}';"
redo_sql += f"delete from region where id = '{id}';"
undo_sql = f"insert into region (id, boundary, r_type) values ('{id}', '{to_postgis_polygon(boundary)}', 'DMA');"
undo_sql += f"insert into region_dma (id, parent, nodes) values ('{id}', {f_parent}, '{str_nodes}');"
redo_cs = g_delete_prefix | { 'type': 'district_metering_area', 'id': id }
undo_cs = g_add_prefix | { 'type': 'district_metering_area', 'id': id, 'boundary': boundary, 'parent': parent, 'nodes': nodes }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def _has_child(name: str, parent: str) -> bool:
return try_read(name, f"select * from region_dma where parent = '{parent}'") != None
def is_descendant_of(name: str, descendant: str, ancestor: str) -> bool:
parent = descendant
while parent != None:
parent = read(name, f"select parent from region_dma where id = '{parent}'")['parent']
if parent == ancestor:
return True
return False
def delete_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet:
ops = cs.operations
if len(cs.operations) == 0:
return ChangeSet()
op = ops[0]
if 'id' not in op:
return ChangeSet()
dma = get_district_metering_area(name, op['id'])
if dma == {}:
return ChangeSet()
#TODO: cascade ?
if _has_child(name, dma['id']):
return ChangeSet()
return execute_command(name, _delete_district_metering_area(name, cs))
def get_all_district_metering_area_ids(name: str) -> list[str]:
ids = []
for row in read_all(name, f"select id from region_dma"):
ids.append(row['id'])
return ids
def get_all_district_metering_areas(name: str) -> list[dict[str, Any]]:
result = []
for id in get_all_district_metering_area_ids(name):
result.append(get_district_metering_area(name, id))
return result
+152
View File
@@ -0,0 +1,152 @@
import ctypes
import os
import numpy as np
import pymetis
from .database import *
from .s0_base import get_nodes
from .s32_region_util import get_nodes_in_region
from .s32_region_util import Topology
PARTITION_TYPE_RB = 0
PARTITION_TYPE_KWAY = 1
'''
adjacency_list = [np.array([4, 2, 1]),
np.array([0, 2, 3]),
np.array([4, 3, 1, 0]),
np.array([1, 2, 5, 6]),
np.array([0, 2, 5]),
np.array([4, 3, 6]),
np.array([5, 3])]
n_cuts, membership = pymetis.part_graph(2, adjacency=adjacency_list)
# n_cuts = 3
# membership = [1, 1, 1, 0, 1, 0, 0]
nodes_part_0 = np.argwhere(np.array(membership) == 0).ravel() # [3, 5, 6]
nodes_part_1 = np.argwhere(np.array(membership) == 1).ravel() # [0, 1, 2, 4]
print(nodes_part_0)
print(nodes_part_1)
'''
def calculate_district_metering_area_for_nodes(name: str, nodes: list[str], part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]:
topology = Topology(name, nodes)
t_nodes = topology.nodes()
t_links = topology.links()
t_node_list = topology.node_list()
adjacency_list = []
for node in t_node_list:
links: list[str] = t_nodes[node]['links']
a_nodes: list[int] = []
for link in links:
if t_links[link]['node1'] == node:
i = t_node_list.index(t_links[link]['node2'])
a_nodes.append(i)
elif t_links[link]['node2'] == node:
i = t_node_list.index(t_links[link]['node1'])
a_nodes.append(i)
adjacency_list.append(np.array(a_nodes))
recursive = part_type == PARTITION_TYPE_RB
n_cuts, membership = pymetis.part_graph(nparts=part_count, adjacency=adjacency_list, recursive=recursive, contiguous=True)
result: list[list[str]] = []
for i in range(0, part_count):
indices: list[int] = list(np.argwhere(np.array(membership) == i).ravel())
index_strs: list[str] = []
for index in indices:
index_strs.append(t_node_list[index])
result.append(index_strs)
return result
def _calculate_district_metering_area_for_nodes(name: str, nodes: list[str], part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]:
if part_type != PARTITION_TYPE_RB and part_type != PARTITION_TYPE_KWAY:
return []
if part_count <= 0:
return []
elif part_count == 1:
return [nodes]
lib = ctypes.CDLL(os.path.join(os.getcwd(), 'api', 'CMetis.dll'))
METIS_NOPTIONS = 40
c_options = (ctypes.c_int64 * METIS_NOPTIONS)()
METIS_OK = 1
result = lib.set_default_options(c_options)
if result != METIS_OK:
return []
METIS_OPTION_PTYPE , METIS_OPTION_CONTIG = 0, 13
c_options[METIS_OPTION_PTYPE] = part_type
c_options[METIS_OPTION_CONTIG] = 1
topology = Topology(name, nodes)
t_nodes = topology.nodes()
t_links = topology.links()
t_node_list = topology.node_list()
t_link_list = topology.link_list()
nedges = len(t_link_list) * 2
c_nvtxs = ctypes.c_int64(len(t_node_list))
c_ncon = ctypes.c_int64(1)
c_xadj = (ctypes.c_int64 * (c_nvtxs.value + 1))()
c_adjncy = (ctypes.c_int64 * nedges)()
c_vwgt = (ctypes.c_int64 * (c_ncon.value * c_nvtxs.value))()
c_adjwgt = (ctypes.c_int64 * nedges)()
c_vsize = (ctypes.c_int64 * c_nvtxs.value)()
c_xadj[0] = 0
l, n = 0, 0
c_xadj_i = 1
for node in t_node_list:
links = t_nodes[node]['links']
for link in links:
node1 = t_links[link]['node1']
node2 = t_links[link]['node2']
c_adjncy[l] = t_node_list.index(node2) if node2 != node else t_node_list.index(node1)
c_adjwgt[l] = 1
l += 1
if len(links) > 0:
c_xadj[c_xadj_i] = l # adjncy.size()
c_xadj_i += 1
c_vwgt[n] = 1
c_vsize[n] = 1
n += 1
part_func = lib.part_graph_recursive if part_type == PARTITION_TYPE_RB else lib.part_graph_kway
c_nparts = ctypes.c_int64(part_count)
c_tpwgts = ctypes.POINTER(ctypes.c_double)()
c_ubvec = ctypes.POINTER(ctypes.c_double)()
c_out_edgecut = ctypes.c_int64(0)
c_out_part = (ctypes.c_int64 * c_nvtxs.value)()
result = part_func(ctypes.byref(c_nvtxs), ctypes.byref(c_ncon), c_xadj, c_adjncy, c_vwgt, c_vsize, c_adjwgt, ctypes.byref(c_nparts), c_tpwgts, c_ubvec, c_options, ctypes.byref(c_out_edgecut), c_out_part)
if result != METIS_OK:
return []
dmas : list[list[str]]= []
for i in range(part_count):
dmas.append([])
for i in range(c_nvtxs.value):
dmas[c_out_part[i]].append(t_node_list[i])
return dmas
def calculate_district_metering_area_for_region(name: str, region: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]:
nodes = get_nodes_in_region(name, region)
return calculate_district_metering_area_for_nodes(name, nodes, part_count, part_type)
def calculate_district_metering_area_for_network(name: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]:
nodes = get_nodes(name)
return calculate_district_metering_area_for_nodes(name, nodes, part_count, part_type)
+47
View File
@@ -0,0 +1,47 @@
from .s32_region_util import calculate_boundary, inflate_boundary
from .s33_dma_cal import *
from .s33_dma import get_all_district_metering_area_ids, get_all_district_metering_areas, get_district_metering_area, is_descendant_of
from .batch_exe import execute_batch_command
def generate_district_metering_area(name: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB, inflate_delta: float = 0.5) -> ChangeSet:
cs = ChangeSet()
dmas = get_all_district_metering_areas(name)
max_level = 0
for dma in dmas:
if dma['level'] > max_level:
max_level = dma['level']
while max_level > 0:
for dma in dmas:
if dma['level'] == max_level:
cs.delete({ 'type': 'district_metering_area', 'id': dma['id'] })
max_level -= 1
i = 1
for nodes in calculate_district_metering_area_for_network(name, part_count, part_type):
boundary = calculate_boundary(name, nodes)
boundary = inflate_boundary(name, boundary, inflate_delta)
cs.add({ 'type': 'district_metering_area', 'id': f"DMA_1_{i}", 'boundary': boundary, 'parent': None, 'nodes': nodes })
i += 1
return execute_batch_command(name, cs)
def generate_sub_district_metering_area(name: str, dma: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB, inflate_delta: float = 0.5) -> ChangeSet:
cs = ChangeSet()
for id in get_all_district_metering_area_ids(name):
if is_descendant_of(name, id, dma):
cs.delete({ 'type': 'district_metering_area', 'id': id })
level = get_district_metering_area(name, dma)['level'] + 1
i = 1
for nodes in calculate_district_metering_area_for_region(name, dma, part_count, part_type):
boundary = calculate_boundary(name, nodes)
boundary = inflate_boundary(name, boundary, inflate_delta)
cs.add({ 'type': 'district_metering_area', 'id': f"DMA_[{dma}]_{level}_{i}", 'boundary': boundary, 'parent': dma, 'nodes': nodes })
i += 1
return execute_batch_command(name, cs)
+217
View File
@@ -0,0 +1,217 @@
from .database import *
from .s0_base import is_node
from .s32_region_util import to_postgis_polygon
from .s32_region import get_region
def get_service_area_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'boundary' : {'type': 'tuple_list' , 'optional': False , 'readonly': False },
'source' : {'type': 'str' , 'optional': False , 'readonly': False },
'time_index' : {'type': 'int' , 'optional': False , 'readonly': False } }
def get_service_area(name: str, id: str) -> dict[str, Any]:
sa = get_region(name, id)
if sa == {}:
return {}
r = try_read(name, f"select * from region_sa where id = '{id}'")
if r == None:
return {}
sa['source'] = r['source']
sa['nodes'] = list(eval(r['nodes']))
sa['time_index'] = r['time_index']
return sa
def _set_service_area(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
new_boundary = cs.operations[0]['boundary']
old_boundary = get_region(name, id)['boundary']
new_source = cs.operations[0]['source']
f_new_source = f"'{new_source}'"
new_nodes = cs.operations[0]['nodes']
str_new_nodes = str(new_nodes).replace("'", "''")
new_time_index = cs.operations[0]['time_index']
old = get_service_area(name, id)
old_source = old['source']
f_old_source = f"'{old_source}'"
old_nodes = old['nodes']
str_old_nodes = str(old_nodes).replace("'", "''")
old_time_index = old['time_index']
redo_sql = f"update region set boundary = st_geomfromtext('{to_postgis_polygon(new_boundary)}') where id = '{id}';"
redo_sql += f"update region_sa set time_index = {new_time_index}, source = {f_new_source}, nodes = '{str_new_nodes}' where id = '{id}';"
undo_sql = f"update region_sa set time_index = {old_time_index}, source = {f_old_source}, nodes = '{str_old_nodes}' where id = '{id}';"
undo_sql += f"update region set boundary = st_geomfromtext('{to_postgis_polygon(old_boundary)}') where id = '{id}';"
redo_cs = g_update_prefix | { 'type': 'service_area', 'id': id, 'boundary': new_boundary, 'time_index': new_time_index, 'source': new_source, 'nodes': new_nodes }
undo_cs = g_update_prefix | { 'type': 'service_area', 'id': id, 'boundary': old_boundary, 'time_index': old_time_index, 'source': old_source, 'nodes': old_nodes }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_service_area(name: str, cs: ChangeSet) -> ChangeSet:
ops = cs.operations
if len(cs.operations) == 0:
return ChangeSet()
op = ops[0]
if 'id' not in op:
return ChangeSet()
sa = get_service_area(name, op['id'])
if sa == {}:
return ChangeSet()
if 'boundary' not in op:
op['boundary'] = sa['boundary']
else:
b = op['boundary']
if len(b) < 4 or b[0] != b[-1]:
return ChangeSet()
if 'time_index' not in op:
op['time_index'] = sa['time_index']
if 'source' not in op:
op['source'] = sa['source']
if not is_node(name, op['source']):
return ChangeSet()
if 'nodes' not in op:
op['nodes'] = sa['nodes']
else:
for node in op['nodes']:
if not is_node(name, node):
return ChangeSet()
return execute_command(name, _set_service_area(name, cs))
def _add_service_area(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
boundary = cs.operations[0]['boundary']
time_index = cs.operations[0]['time_index']
source = cs.operations[0]['source']
f_source = f"'{source}'"
nodes = cs.operations[0]['nodes']
str_nodes = str(nodes).replace("'", "''")
redo_sql = f"insert into region (id, boundary, r_type) values ('{id}', '{to_postgis_polygon(boundary)}', 'SA');"
redo_sql += f"insert into region_sa (id, time_index, source, nodes) values ('{id}', {time_index}, {f_source}, '{str_nodes}');"
undo_sql = f"delete from region_sa where id = '{id}';"
undo_sql += f"delete from region where id = '{id}';"
redo_cs = g_add_prefix | { 'type': 'service_area', 'id': id, 'boundary': boundary, 'time_index': time_index, 'source': source, 'nodes': nodes }
undo_cs = g_delete_prefix | { 'type': 'service_area', 'id': id }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_service_area(name: str, cs: ChangeSet) -> ChangeSet:
ops = cs.operations
if len(cs.operations) == 0:
return ChangeSet()
op = ops[0]
if 'id' not in op:
return ChangeSet()
sa = get_service_area(name, op['id'])
if sa != {}:
return ChangeSet()
if 'boundary' not in op:
return ChangeSet()
else:
b = op['boundary']
if len(b) < 4 or b[0] != b[-1]:
return ChangeSet()
if 'time_index' not in op:
return ChangeSet()
if 'source' not in op:
return ChangeSet()
if not is_node(name, op['source']):
return ChangeSet()
if 'nodes' not in op:
op['nodes'] = []
else:
for node in op['nodes']:
if not is_node(name, node):
return ChangeSet()
return execute_command(name, _add_service_area(name, cs))
def _delete_service_area(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
sa = get_service_area(name, id)
boundary = sa['boundary']
time_index = sa['time_index']
source = sa['source']
f_source = f"'{source}'"
nodes = sa['nodes']
str_nodes = str(nodes).replace("'", "''")
redo_sql = f"delete from region_sa where id = '{id}';"
redo_sql += f"delete from region where id = '{id}';"
undo_sql = f"insert into region (id, boundary, r_type) values ('{id}', '{to_postgis_polygon(boundary)}', 'SA');"
undo_sql += f"insert into region_sa (id, time_index, source, nodes) values ('{id}', {time_index}, {f_source}, '{str_nodes}');"
redo_cs = g_delete_prefix | { 'type': 'service_area', 'id': id }
undo_cs = g_add_prefix | { 'type': 'service_area', 'id': id, 'boundary': boundary, 'time_index': time_index, 'source': source, 'nodes': nodes }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_service_area(name: str, cs: ChangeSet) -> ChangeSet:
ops = cs.operations
if len(cs.operations) == 0:
return ChangeSet()
op = ops[0]
if 'id' not in op:
return ChangeSet()
sa = get_service_area(name, op['id'])
if sa == {}:
return ChangeSet()
return execute_command(name, _delete_service_area(name, cs))
def get_all_service_area_ids(name: str) -> list[str]:
ids = []
for row in read_all(name, f"select id from region_sa"):
ids.append(row['id'])
return ids
def get_all_service_areas(name: str) -> list[dict[str, Any]]:
result = []
for id in get_all_service_area_ids(name):
result.append(get_service_area(name, id))
return result
+198
View File
@@ -0,0 +1,198 @@
import os
import ctypes
from .project_backup import have_project
from .inp_out import dump_inp
def calculate_service_area(name: str) -> list[dict[str, list[str]]]:
if not have_project(name):
raise Exception(f'Not found project [{name}]')
dir = os.path.abspath(os.getcwd())
inp_str = os.path.join(os.path.join(dir, 'db_inp'), name + '.db.inp')
dump_inp(name, inp_str, '2')
toolkit = ctypes.CDLL(os.path.join(os.path.join(dir, 'api'), 'toolkit.dll'))
inp = ctypes.c_char_p(inp_str.encode())
handle = ctypes.c_ulonglong()
toolkit.TK_ServiceArea_Start(inp, ctypes.byref(handle))
c_nodeCount = ctypes.c_size_t()
toolkit.TK_ServiceArea_GetNodeCount(handle, ctypes.byref(c_nodeCount))
nodeCount = c_nodeCount.value
nodeIds: list[str] = []
for n in range(0, nodeCount):
id = ctypes.c_char_p()
toolkit.TK_ServiceArea_GetNodeId(handle, ctypes.c_size_t(n), ctypes.byref(id))
nodeIds.append(id.value.decode())
c_timeCount = ctypes.c_size_t()
toolkit.TK_ServiceArea_GetTimeCount(handle, ctypes.byref(c_timeCount))
timeCount = c_timeCount.value
results: list[dict[str, list[str]]] = []
for t in range(0, timeCount):
c_sourceCount = ctypes.c_size_t()
toolkit.TK_ServiceArea_GetSourceCount(handle, ctypes.c_size_t(t), ctypes.byref(c_sourceCount))
sourceCount = c_sourceCount.value
sources = ctypes.POINTER(ctypes.c_size_t)()
toolkit.TK_ServiceArea_GetSources(handle, ctypes.c_size_t(t), ctypes.byref(sources))
result: dict[str, list[str]] = {}
for s in range(0, sourceCount):
result[nodeIds[sources[s]]] = []
for n in range(0, nodeCount):
concentration = ctypes.POINTER(ctypes.c_double)()
toolkit.TK_ServiceArea_GetConcentration(handle, ctypes.c_size_t(t), ctypes.c_size_t(n), ctypes.byref(concentration))
maxS = sources[0]
maxC = concentration[0]
for s in range(1, sourceCount):
if concentration[s] > maxC:
maxS = sources[s]
maxC = concentration[s]
result[nodeIds[maxS]].append(nodeIds[n])
results.append(result)
toolkit.TK_ServiceArea_End(handle)
return results
'''
import sys
import json
from queue import Queue
from .database import *
from .s0_base import get_node_links, get_link_nodes
sys.path.append('..')
from epanet.epanet import run_project
def _calculate_service_area(name: str, inp, time_index: int = 0) -> dict[str, list[str]]:
sources : dict[str, list[str]] = {}
for node_result in inp['node_results']:
result = node_result['result'][time_index]
if result['demand'] < 0:
sources[node_result['node']] = []
link_flows: dict[str, float] = {}
for link_result in inp['link_results']:
result = link_result['result'][time_index]
link_flows[link_result['link']] = float(result['flow'])
# build source to nodes map
for source in sources:
queue = Queue()
queue.put(source)
while not queue.empty():
cursor = queue.get()
if cursor not in sources[source]:
sources[source].append(cursor)
links = get_node_links(name, cursor)
for link in links:
node1, node2 = get_link_nodes(name, link)
if node1 == cursor and link_flows[link] > 0:
queue.put(node2)
elif node2 == cursor and link_flows[link] < 0:
queue.put(node1)
#return sources
# calculation concentration
concentration_map: dict[str, dict[str, float]] = {}
node_wip: list[str] = []
for source, nodes in sources.items():
for node in nodes:
if node not in concentration_map:
concentration_map[node] = {}
concentration_map[node][source] = 0.0
if node not in node_wip:
node_wip.append(node)
# if only one source, done
for node, concentrations in concentration_map.items():
if len(concentrations) == 1:
node_wip.remove(node)
for key in concentrations.keys():
concentration_map[node][key] = 1.0
node_upstream : dict[str, list[tuple[str, str]]] = {}
for node in node_wip:
if node not in node_upstream:
node_upstream[node] = []
links = get_node_links(name, node)
for link in links:
node1, node2 = get_link_nodes(name, link)
if node2 == node and link_flows[link] > 0:
node_upstream[node].append((link, node1))
elif node1 == node and link_flows[link] < 0:
node_upstream[node].append((link, node2))
while len(node_wip) != 0:
done = []
for node in node_wip:
up_link_nodes = node_upstream[node]
ready = True
for link_node in up_link_nodes:
if link_node[1] in node_wip:
ready = False
break
if ready:
for link_node in up_link_nodes:
if link_node[1] not in concentration_map.keys():
continue
for source, concentration in concentration_map[link_node[1]].items():
concentration_map[node][source] += concentration * abs(link_flows[link_node[0]])
# normalize
sum = 0.0
for source, concentration in concentration_map[node].items():
sum += concentration
for source in concentration_map[node].keys():
concentration_map[node][source] /= sum
done.append(node)
for node in done:
node_wip.remove(node)
source_to_main_node: dict[str, list[str]] = {}
for node, value in concentration_map.items():
max_source = ''
max_concentration = 0.0
for s, c in value.items():
if c > max_concentration:
max_concentration = c
max_source = s
if max_source not in source_to_main_node:
source_to_main_node[max_source] = []
source_to_main_node[max_source].append(node)
return source_to_main_node
def calculate_service_area(name: str) -> list[dict[str, list[str]]]:
inp = json.loads(run_project(name, True))
result: list[dict[str, list[str]]] = []
time_count = len(inp['node_results'][0]['result'])
for i in range(time_count):
sas = _calculate_service_area(name, inp, i)
result.append(sas)
return result
'''
+23
View File
@@ -0,0 +1,23 @@
from .s32_region_util import calculate_boundary, inflate_boundary
from .s34_sa_cal import *
from .s34_sa import get_all_service_area_ids
from .batch_exe import execute_batch_command
from .database import ChangeSet
def generate_service_area(name: str, inflate_delta: float = 0.5) -> ChangeSet:
cs = ChangeSet()
for id in get_all_service_area_ids(name):
cs.delete({'type': 'service_area', 'id': id})
sass = calculate_service_area(name)
time_index = 0
for sas in sass:
for source, nodes in sas.items():
boundary = calculate_boundary(name, nodes)
boundary = inflate_boundary(name, boundary, inflate_delta)
cs.add({ 'type': 'service_area', 'id': f"SA_{source}_{time_index}", 'boundary': boundary, 'time_index': time_index, 'source': source, 'nodes': nodes })
time_index += 1
return execute_batch_command(name, cs)
+202
View File
@@ -0,0 +1,202 @@
from .database import *
from .s0_base import is_node
from .s32_region_util import to_postgis_polygon
from .s32_region import get_region
def get_virtual_district_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'boundary' : {'type': 'tuple_list' , 'optional': False , 'readonly': False },
'center' : {'type': 'str' , 'optional': False , 'readonly': False } }
def get_virtual_district(name: str, id: str) -> dict[str, Any]:
vd = get_region(name, id)
if vd == {}:
return {}
r = try_read(name, f"select * from region_vd where id = '{id}'")
if r == None:
return {}
vd['center'] = r['center']
vd['nodes'] = list(eval(r['nodes']))
return vd
def _set_virtual_district(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
new_boundary = cs.operations[0]['boundary']
old_boundary = get_region(name, id)['boundary']
new_center = cs.operations[0]['center']
f_new_center = f"'{new_center}'"
new_nodes = cs.operations[0]['nodes']
str_new_nodes = str(new_nodes).replace("'", "''")
old = get_virtual_district(name, id)
old_center = old['center']
f_old_center = f"'{old_center}'"
old_nodes = old['nodes']
str_old_nodes = str(old_nodes).replace("'", "''")
redo_sql = f"update region set boundary = st_geomfromtext('{to_postgis_polygon(new_boundary)}') where id = '{id}';"
redo_sql += f"update region_vd set center = {f_new_center}, nodes = '{str_new_nodes}' where id = '{id}';"
undo_sql = f"update region_vd set center = {f_old_center}, nodes = '{str_old_nodes}' where id = '{id}';"
undo_sql += f"update region set boundary = st_geomfromtext('{to_postgis_polygon(old_boundary)}') where id = '{id}';"
redo_cs = g_update_prefix | { 'type': 'virtual_district', 'id': id, 'boundary': new_boundary, 'center': new_center, 'nodes': new_nodes }
undo_cs = g_update_prefix | { 'type': 'virtual_district', 'id': id, 'boundary': old_boundary, 'center': old_center, 'nodes': old_nodes }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_virtual_district(name: str, cs: ChangeSet) -> ChangeSet:
ops = cs.operations
if len(cs.operations) == 0:
return ChangeSet()
op = ops[0]
if 'id' not in op:
return ChangeSet()
vd = get_virtual_district(name, op['id'])
if vd == {}:
return ChangeSet()
if 'boundary' not in op:
op['boundary'] = vd['boundary']
else:
b = op['boundary']
if len(b) < 4 or b[0] != b[-1]:
return ChangeSet()
if 'center' not in op:
op['center'] = vd['center']
if not is_node(name, op['center']):
return ChangeSet()
if 'nodes' not in op:
op['nodes'] = vd['nodes']
else:
for node in op['nodes']:
if not is_node(name, node):
return ChangeSet()
return execute_command(name, _set_virtual_district(name, cs))
def _add_virtual_district(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
boundary = cs.operations[0]['boundary']
center = cs.operations[0]['center']
f_center = f"'{center}'"
nodes = cs.operations[0]['nodes']
str_nodes = str(nodes).replace("'", "''")
redo_sql = f"insert into region (id, boundary, r_type) values ('{id}', '{to_postgis_polygon(boundary)}', 'VD');"
redo_sql += f"insert into region_vd (id, center, nodes) values ('{id}', {f_center}, '{str_nodes}');"
undo_sql = f"delete from region_vd where id = '{id}';"
undo_sql += f"delete from region where id = '{id}';"
redo_cs = g_add_prefix | { 'type': 'virtual_district', 'id': id, 'boundary': boundary, 'center': center, 'nodes': nodes }
undo_cs = g_delete_prefix | { 'type': 'virtual_district', 'id': id }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_virtual_district(name: str, cs: ChangeSet) -> ChangeSet:
ops = cs.operations
if len(cs.operations) == 0:
return ChangeSet()
op = ops[0]
if 'id' not in op:
return ChangeSet()
vd = get_virtual_district(name, op['id'])
if vd != {}:
return ChangeSet()
if 'boundary' not in op:
return ChangeSet()
else:
b = op['boundary']
if len(b) < 4 or b[0] != b[-1]:
return ChangeSet()
if 'center' not in op:
return ChangeSet()
if not is_node(name, op['center']):
return ChangeSet()
if 'nodes' not in op:
op['nodes'] = []
else:
for node in op['nodes']:
if not is_node(name, node):
return ChangeSet()
return execute_command(name, _add_virtual_district(name, cs))
def _delete_virtual_district(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
vd = get_virtual_district(name, id)
boundary = vd['boundary']
center = vd['center']
f_center = f"'{center}'"
nodes = vd['nodes']
str_nodes = str(nodes).replace("'", "''")
redo_sql = f"delete from region_vd where id = '{id}';"
redo_sql += f"delete from region where id = '{id}';"
undo_sql = f"insert into region (id, boundary, r_type) values ('{id}', '{to_postgis_polygon(boundary)}', 'VD');"
undo_sql += f"insert into region_vd (id, center, nodes) values ('{id}', {f_center}, '{str_nodes}');"
redo_cs = g_delete_prefix | { 'type': 'virtual_district', 'id': id }
undo_cs = g_add_prefix | { 'type': 'virtual_district', 'id': id, 'boundary': boundary, 'center': center, 'nodes': nodes }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_virtual_district(name: str, cs: ChangeSet) -> ChangeSet:
ops = cs.operations
if len(cs.operations) == 0:
return ChangeSet()
op = ops[0]
if 'id' not in op:
return ChangeSet()
vd = get_virtual_district(name, op['id'])
if vd == {}:
return ChangeSet()
return execute_command(name, _delete_virtual_district(name, cs))
def get_all_virtual_district_ids(name: str) -> list[str]:
ids = []
for row in read_all(name, f"select id from region_vd"):
ids.append(row['id'])
return ids
def get_all_virtual_districts(name: str) -> list[dict[str, Any]]:
result = []
for id in get_all_virtual_district_ids(name):
result.append(get_virtual_district(name, id))
return result
+66
View File
@@ -0,0 +1,66 @@
from .database import *
from .s0_base import get_node_links
def calculate_virtual_district(name: str, centers: list[str]) -> dict[str, list[Any]]:
write(name, 'delete from temp_vd_topology')
# map node name to index
i = 0
isolated_nodes = []
node_index: dict[str, int] = {}
for row in read_all(name, 'select id from _node'):
node = str(row['id'])
if get_node_links(name, node) == []:
isolated_nodes.append(node)
continue
i += 1
node_index[node] = i
# build topology graph
pipes = read_all(name, 'select node1, node2, length from pipes')
for pipe in pipes:
source = node_index[str(pipe['node1'])]
target = node_index[str(pipe['node2'])]
cost = float(pipe['length'])
write(name, f"insert into temp_vd_topology (source, target, cost) values ({source}, {target}, {cost})")
pumps = read_all(name, 'select node1, node2 from pumps')
for pump in pumps:
source = node_index[str(pump['node1'])]
target = node_index[str(pump['node2'])]
write(name, f"insert into temp_vd_topology (source, target, cost) values ({source}, {target}, 0.0)")
valves = read_all(name, 'select node1, node2 from valves')
for valve in valves:
source = node_index[str(valve['node1'])]
target = node_index[str(valve['node2'])]
write(name, f"insert into temp_vd_topology (source, target, cost) values ({source}, {target}, 0.0)")
# dijkstra distance
node_distance: dict[str, dict[str, Any]] = {}
for center in centers:
for node, index in node_index.items():
if node == center:
node_distance[node] = { 'center': center, 'distance' : 0.0 }
continue
# TODO: check none
distance = float(read(name, f"select max(agg_cost) as distance from pgr_dijkstraCost('select id, source, target, cost from temp_vd_topology', {index}, {node_index[center]}, false)")['distance'])
if node not in node_distance:
node_distance[node] = { 'center': center, 'distance' : distance }
elif distance < node_distance[node]['distance']:
node_distance[node] = { 'center': center, 'distance' : distance }
write(name, 'delete from temp_vd_topology')
# reorganize the distance result
center_node: dict[str, list[str]] = {}
for node, value in node_distance.items():
if value['center'] not in center_node:
center_node[value['center']] = []
center_node[value['center']].append(node)
vds: list[dict[str, Any]] = []
for center, value in center_node.items():
vds.append({ 'center': center, 'nodes': value })
return { 'virtual_districts': vds, 'isolated_nodes': isolated_nodes }
+21
View File
@@ -0,0 +1,21 @@
from .s32_region_util import calculate_boundary, inflate_boundary
from .s35_vd_cal import *
from .s35_vd import get_all_virtual_district_ids
from .batch_exe import execute_batch_command
def generate_virtual_district(name: str, centers: list[str], inflate_delta: float = 0.5) -> ChangeSet:
cs = ChangeSet()
for id in get_all_virtual_district_ids(name):
cs.delete({'type': 'virtual_district', 'id': id})
vds = calculate_virtual_district(name, centers)['virtual_districts']
for vd in vds:
center = vd['center']
nodes = vd['nodes']
boundary = calculate_boundary(name, nodes)
boundary = inflate_boundary(name, boundary, inflate_delta)
cs.add({ 'type': 'virtual_district', 'id': f"VD_{center}", 'boundary': boundary, 'center': center, 'nodes': nodes })
return execute_batch_command(name, cs)
View File
+104
View File
@@ -0,0 +1,104 @@
from .database import ChangeSet
from .s0_base import is_junction, get_nodes
from .s9_demands import get_demand
from .s32_region_util import Topology, get_nodes_in_region
from .batch_exe import execute_batch_command
DISTRIBUTION_TYPE_ADD = 'ADD'
DISTRIBUTION_TYPE_OVERRIDE = 'OVERRIDE'
def calculate_demand_to_nodes(name: str, demand: float, nodes: list[str]) -> dict[str, float]:
if len(nodes) == 0 or demand == 0.0:
return {}
topology = Topology(name, nodes)
t_nodes = topology.nodes()
t_links = topology.links()
length_sum = 0.0
for value in t_links.values():
length_sum += abs(value['length'])
if length_sum <= 0.0:
return {}
demand_per_length = demand / length_sum
result: dict[str, float] = {}
for node, value in t_nodes.items():
if not is_junction(name, node):
continue
demand_per_node = 0.0
for link in value['links']:
demand_per_node += abs(t_links[link]['length']) * demand_per_length * 0.5
result[node] = demand_per_node
return result
def calculate_demand_to_region(name: str, demand: float, region: str) -> dict[str, float]:
nodes = get_nodes_in_region(name, region)
return calculate_demand_to_nodes(name, demand, nodes)
def calculate_demand_to_network(name: str, demand: float) -> dict[str, float]:
nodes = get_nodes(name)
return calculate_demand_to_nodes(name, demand, nodes)
def distribute_demand_to_nodes(name: str, demand: float, nodes: list[str], type: str = DISTRIBUTION_TYPE_ADD) -> ChangeSet:
if len(nodes) == 0 or demand == 0.0:
return ChangeSet()
if type != DISTRIBUTION_TYPE_ADD and type != DISTRIBUTION_TYPE_OVERRIDE:
return ChangeSet()
topology = Topology(name, nodes)
t_nodes = topology.nodes()
t_links = topology.links()
length_sum = 0.0
for value in t_links.values():
length_sum += abs(value['length'])
if length_sum <= 0.0:
return ChangeSet()
demand_per_length = demand / length_sum
cs = ChangeSet()
for node, value in t_nodes.items():
if not is_junction(name, node):
continue
demand_per_node = 0.0
for link in value['links']:
demand_per_node += abs(t_links[link]['length']) * demand_per_length * 0.5
ds = get_demand(name, node)['demands']
if len(ds) == 0:
ds = [{'demand': demand_per_node, 'pattern': None, 'category': None}]
elif type == DISTRIBUTION_TYPE_ADD:
ds[0]['demand'] += demand_per_node
else:
ds[0]['demand'] = demand_per_node
cs.update({'type': 'demand', 'junction': node, 'demands': ds})
return execute_batch_command(name, cs)
def distribute_demand_to_region(name: str, demand: float, region: str, type: str = DISTRIBUTION_TYPE_ADD) -> ChangeSet:
nodes = get_nodes_in_region(name, region)
return distribute_demand_to_nodes(name, demand, nodes, type)
def get_total_base_demand(name:str,region:str)->float:
nodes = get_nodes_in_region(name, region)
t_demands=0.0
for node in nodes:
if not is_junction(name, node):
continue
ds = get_demand(name, node)['demands']
t_demands= t_demands+ds[0]['demand']
return t_demands
+42
View File
@@ -0,0 +1,42 @@
from .database import *
def get_scada_info_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'type' : {'type': 'str' , 'optional': False , 'readonly': True },
'x' : {'type': 'float' , 'optional': False , 'readonly': False},
'y' : {'type': 'float' , 'optional': False , 'readonly': False},
'query_api_id' : {'type': 'str' , 'optional': False , 'readonly': False},
'associated_element_id' : {'type': 'str' , 'optional': False , 'readonly': True } }
def get_scada_info(name: str, id: str) -> dict[str, Any]:
si = try_read(name, f"select * from scada_info where id = '{id}'")
if si is None:
return {}
d = {}
d['id'] = si['id']
d['type'] = si['type']
d['x'] = float(si['x_coor'])
d['y'] = float(si['y_coor'])
d['api_query_id'] = si['api_query_id']
d['associated_element_id'] = si['associated_element_id']
return d
def get_all_scada_info(name: str) -> list[dict[str, Any]]:
sis = read_all(name, f"select * from scada_info")
if sis is None:
return []
d = []
for si in sis:
d.append({ 'id': si['id'],
'type': si['type'],
'x': float(si['x_coor']),
'y': float(si['y_coor']),
'api_query_id': si['api_query_id'],
'associated_element_id': si['associated_element_id'] })
return d
+37
View File
@@ -0,0 +1,37 @@
from .database import *
from .s0_base import *
class User(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'user'
self.id = str(input['user_id'])
self.name = str(input['username'])
self.password = str(input['password'])
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id, 'name': self.name, 'password': self.password }
def as_id_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id }
def get_user_schema(name: str) -> dict[str, dict[Any, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'name' : {'type': 'str' , 'optional': False , 'readonly': False},
'password' : {'type': 'str' , 'optional': False , 'readonly': False} }
def get_user(name: str, user_name: str) -> dict[Any, Any]:
t = try_read(name, f"select * from users where username = '{user_name}'")
if t == None:
return {}
d = {}
d['id'] = str(t['user_id'])
d['name'] = str(t['username'])
# d['password'] = str(t['password'])
return d
def get_all_users(name: str) -> list[dict[Any, Any]]:
return read_all(name, "select * from users")
+193
View File
@@ -0,0 +1,193 @@
from .database import *
from .s0_base import *
from .s24_coordinates import *
def get_reservoir_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'x' : {'type': 'float' , 'optional': False , 'readonly': False},
'y' : {'type': 'float' , 'optional': False , 'readonly': False},
'head' : {'type': 'float' , 'optional': False , 'readonly': False},
'pattern' : {'type': 'str' , 'optional': True , 'readonly': False},
'links' : {'type': 'str_list' , 'optional': False , 'readonly': True } }
def get_reservoir(name: str, id: str) -> dict[str, Any]:
r = try_read(name, f"select * from reservoirs where id = '{id}'")
if r == None:
return {}
xy = get_node_coord(name, id)
d = {}
d['id'] = str(r['id'])
d['x'] = float(xy['x'])
d['y'] = float(xy['y'])
d['head'] = float(r['head'])
d['pattern'] = str(r['pattern']) if r['pattern'] != None else None
d['links'] = get_node_links(name, id)
return d
# DingZQ, 2025-03-29
def get_all_reservoirs(name: str) -> list[dict[str, Any]]:
rows = read_all(name, f"select * from reservoirs")
if rows == None:
return []
result = []
for row in rows:
d = {}
id = str(row['id'])
xy = get_node_coord(name, id)
d['id'] = id
d['x'] = float(xy['x'])
d['y'] = float(xy['y'])
d['head'] = float(row['head']) if row['head'] != None else None
d['pattern'] = str(row['pattern']) if row['pattern'] != None else None
d['links'] = get_node_links(name, id)
result.append(d)
return result
class Reservoir(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'reservoir'
self.id = str(input['id'])
self.x = float(input['x'])
self.y = float(input['y'])
self.head = float(input['head'])
self.pattern = str(input['pattern']) if 'pattern' in input and input['pattern'] != None else None
self.f_type = f"'{self.type}'"
self.f_id = f"'{self.id}'"
self.f_head = self.head
self.f_pattern = f"'{self.pattern}'" if self.pattern != None else 'null'
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id, 'x': self.x, 'y': self.y, 'head': self.head, 'pattern': self.pattern }
def as_id_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id }
def _set_reservoir(name: str, cs: ChangeSet) -> DbChangeSet:
old = Reservoir(get_reservoir(name, cs.operations[0]['id']))
raw_new = get_reservoir(name, cs.operations[0]['id'])
new_dict = cs.operations[0]
schema = get_reservoir_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = Reservoir(raw_new)
redo_sql = f"update reservoirs set head = {new.f_head}, pattern = {new.f_pattern} where id = {new.f_id};"
redo_sql += f"\n{sql_update_coord(new.id, new.x, new.y)}"
undo_sql = sql_update_coord(old.id, old.x, old.y)
undo_sql += f"\nupdate reservoirs set head = {old.f_head}, pattern = {old.f_pattern} where id = {old.f_id};"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_reservoir(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_reservoir(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _set_reservoir(name, cs))
def _add_reservoir(name: str, cs: ChangeSet) -> DbChangeSet:
new = Reservoir(cs.operations[0])
redo_sql = f"insert into _node (id, type) values ({new.f_id}, {new.f_type});"
redo_sql += f"\ninsert into reservoirs (id, head, pattern) values ({new.f_id}, {new.f_head}, {new.f_pattern});"
redo_sql += f"\n{sql_insert_coord(new.id, new.x, new.y)}"
undo_sql = sql_delete_coord(new.id)
undo_sql += f"\ndelete from reservoirs where id = {new.f_id};"
undo_sql += f"\ndelete from _node where id = {new.f_id};"
redo_cs = g_add_prefix | new.as_dict()
undo_cs = g_delete_prefix | new.as_id_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_reservoir(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_reservoir(name, cs.operations[0]['id']) != {}:
return ChangeSet()
return execute_command(name, _add_reservoir(name, cs))
def _delete_reservoir(name: str, cs: ChangeSet) -> DbChangeSet:
old = Reservoir(get_reservoir(name, cs.operations[0]['id']))
redo_sql = sql_delete_coord(old.id)
redo_sql += f"\ndelete from reservoirs where id = {old.f_id};"
redo_sql += f"\ndelete from _node where id = {old.f_id};"
undo_sql = f"insert into _node (id, type) values ({old.f_id}, {old.f_type});"
undo_sql += f"\ninsert into reservoirs (id, head, pattern) values ({old.f_id}, {old.f_head}, {old.f_pattern});"
undo_sql += f"\n{sql_insert_coord(old.id, old.x, old.y)}"
redo_cs = g_delete_prefix | old.as_id_dict()
undo_cs = g_add_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_reservoir(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_reservoir(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _delete_reservoir(name, cs))
#--------------------------------------------------------------
# [EPA2][EPA3][IN][OUT]
# id elev (pattern) ;desc
#--------------------------------------------------------------
def inp_in_reservoir(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
id = str(tokens[0])
head = float(tokens[1])
pattern = str(tokens[2]) if num_without_desc >= 3 else None
pattern = f"'{pattern}'" if pattern != None else 'null'
desc = str(tokens[-1]) if has_desc else None
return str(f"insert into _node (id, type) values ('{id}', 'reservoir');insert into reservoirs (id, head, pattern) values ('{id}', {head}, {pattern});")
def inp_out_reservoir(name: str) -> list[str]:
lines = []
objs = read_all(name, 'select * from reservoirs')
for obj in objs:
id = obj['id']
head = obj['head']
pattern = obj['pattern'] if obj['pattern'] != None else ''
desc = ';'
lines.append(f'{id} {head} {pattern} {desc}')
return lines
def unset_reservoir_by_pattern(name: str, pattern: str) -> ChangeSet:
cs = ChangeSet()
rows = read_all(name, f"select id from reservoirs where pattern = '{pattern}'")
for row in rows:
cs.append(g_update_prefix | {'type': 'reservoir', 'id': row['id'], 'pattern': None})
return cs
+30
View File
@@ -0,0 +1,30 @@
from .database import *
from .s0_base import *
def get_scheme_schema(name: str) -> dict[str, dict[Any, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'name' : {'type': 'str' , 'optional': False , 'readonly': False},
'type' : {'type': 'str' , 'optional': False , 'readonly': False},
"create_time": {'type': 'str' , 'optional': False , 'readonly': True },
"start_time" : {'type': 'str' , 'optional': False , 'readonly': True },
"detail" : {'type': 'str' , 'optional': False , 'readonly': True } }
def get_scheme(name: str, schema_name: str) -> dict[Any, Any]:
t = try_read(name, f"select * from scheme_list where scheme_name = '{schema_name}'")
if t == None:
return {}
d = {}
d['id'] = str(t['scheme_id'])
d['name'] = str(t['scheme_name'])
d['type'] = str(t['scheme_type'])
d['create_time'] = str(t['create_time'])
d['start_time'] = str(t['start_time'])
d['detail'] = str(t['detail'])
return d
def get_all_schemes(name: str) -> list[dict[Any, Any]]:
return read_all(name, "select * from scheme_list")
+87
View File
@@ -0,0 +1,87 @@
from .database import *
from .s0_base import *
import json
def get_pipe_risk_probability_now(name: str, pipe_id: str) -> dict[str, Any]:
t = try_read(name, f"select * from pipe_risk_probability where pipeid = '{pipe_id}'")
if t == None:
return {}
d = {}
d['pipeid'] = str(t['pipeid'])
d['pipeage'] = t['pipeage']
d['risk_probability_now'] = t['risk_probability_now']
return d
def get_pipe_risk_probability(name: str, pipe_id: str) -> dict[str, Any]:
t = try_read(name, f"select * from pipe_risk_probability where pipeid = '{pipe_id}'")
if t == None:
return {}
d = {}
d['pipeid'] = t['pipeid']
d['x'] = t['x']
d['y'] = t['y']
return d
def get_network_pipe_risk_probability_now(name: str) -> list[dict[str, Any]]:
pipe_risk_probability_list = []
with conn[name].cursor(row_factory=dict_row) as cur:
cur.execute(f"select * from pipe_risk_probability")
for record in cur:
#pipe_risk_probability_list.append(record)
t = {}
t['pipeid'] = record['pipeid']
t['pipeage'] = record['pipeage']
t['risk_probability_now'] = record['risk_probability_now']
pipe_risk_probability_list.append(t)
return pipe_risk_probability_list
def get_pipes_risk_probability(name: str, pipe_ids: list[str]) -> list[dict[str, Any]]:
pipe_risk_probability_list = []
with conn[name].cursor(row_factory=dict_row) as cur:
cur.execute(f"select * from pipe_risk_probability")
for record in cur:
if record['pipeid'] in pipe_ids:
t = {}
t['pipeid'] = record['pipeid']
t['x'] = record['x']
t['y'] = record['y']
pipe_risk_probability_list.append(t)
return pipe_risk_probability_list
def get_pipe_risk_probability_geometries(name: str) -> dict[str, Any]:
'''
获取管道的几何信息
返回一个字典,key 是管道的 id,value 是管道的几何信息
几何信息是一个字典,包含 start 和 end 两个 key,value 是管道的起点和终点的坐标
'''
pipe_risk_probability_geometries = {}
key_pipeId = '编码'
# key_startnode = '上游节点'
# key_endnode = '下游节点'
key_geometry = 'geometry'
with conn[name].cursor(row_factory=dict_row) as cur:
cur.execute(f"select *, ST_AsGeoJSON(geometry) AS {key_geometry} from gis_pipe")
for record in cur:
id = record[key_pipeId]
geom = json.loads(record[key_geometry])
pipe_risk_probability_geometries[id] = {
'points': geom['coordinates']
}
for col in record:
if col != key_geometry:
pipe_risk_probability_geometries[id][col] = record[col]
# print(len(pipe_risk_probability_geometries))
return pipe_risk_probability_geometries
+7
View File
@@ -0,0 +1,7 @@
from .database import *
from .s0_base import *
from .s42_sensor_placement import *
import json
def get_all_sensor_placements(name: str) -> list[dict[Any, Any]]:
return read_all(name, "select * from sensor_placement")
+6
View File
@@ -0,0 +1,6 @@
from .database import *
from .s0_base import *
import json
def get_all_burst_locate_results(name: str) -> list[dict[Any, Any]]:
return read_all(name, "select * from burst_locate_result")
+250
View File
@@ -0,0 +1,250 @@
from .database import *
from .s0_base import *
from .s24_coordinates import *
OVERFLOW_YES = 'YES'
OVERFLOW_NO = 'NO'
def get_tank_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'x' : {'type': 'float' , 'optional': False , 'readonly': False},
'y' : {'type': 'float' , 'optional': False , 'readonly': False},
'elevation' : {'type': 'float' , 'optional': False , 'readonly': False},
'init_level' : {'type': 'float' , 'optional': False , 'readonly': False},
'min_level' : {'type': 'float' , 'optional': False , 'readonly': False},
'max_level' : {'type': 'float' , 'optional': False , 'readonly': False},
'diameter' : {'type': 'float' , 'optional': False , 'readonly': False},
'min_vol' : {'type': 'float' , 'optional': False , 'readonly': False},
'vol_curve' : {'type': 'str' , 'optional': True , 'readonly': False},
'overflow' : {'type': 'str' , 'optional': True , 'readonly': False},
'links' : {'type': 'str_list' , 'optional': False , 'readonly': True } }
def get_tank(name: str, id: str) -> dict[str, Any]:
t = try_read(name, f"select * from tanks where id = '{id}'")
if t == None:
return {}
xy = get_node_coord(name, id)
d = {}
d['id'] = str(t['id'])
d['x'] = float(xy['x'])
d['y'] = float(xy['y'])
d['elevation'] = float(t['elevation'])
d['init_level'] = float(t['init_level'])
d['min_level'] = float(t['min_level'])
d['max_level'] = float(t['max_level'])
d['diameter'] = float(t['diameter'])
d['min_vol'] = float(t['min_vol'])
d['vol_curve'] = str(t['vol_curve']) if t['vol_curve'] != None else None
d['overflow'] = str(t['overflow']) if t['overflow'] != None else None
d['links'] = get_node_links(name, id)
return d
# DingZQ, 2025-03-29
def get_all_tanks(name: str) -> list[dict[str, Any]]:
rows = read_all(name, f"select * from tanks")
if rows == None:
return []
result = []
for row in rows:
d = {}
id = str(row['id'])
xy = get_node_coord(name, id)
d['id'] = id
d['x'] = float(xy['x'])
d['y'] = float(xy['y'])
d['elevation'] = float(row['elevation'])
d['init_level'] = float(row['init_level'])
d['min_level'] = float(row['min_level'])
d['max_level'] = float(row['max_level'])
d['diameter'] = float(row['diameter'])
d['min_vol'] = float(row['min_vol'])
d['vol_curve'] = str(row['vol_curve']) if row['vol_curve'] != None else None
d['overflow'] = str(row['overflow']) if row['overflow'] != None else None
d['links'] = get_node_links(name, id)
result.append(d)
return result
class Tank(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'tank'
self.id = str(input['id'])
self.x = float(input['x'])
self.y = float(input['y'])
self.elevation = float(input['elevation'])
self.init_level = float(input['init_level'])
self.min_level = float(input['min_level'])
self.max_level = float(input['max_level'])
self.diameter = float(input['diameter'])
self.min_vol = float(input['min_vol'])
self.vol_curve = str(input['vol_curve']) if 'vol_curve' in input and input['vol_curve'] != None else None
self.overflow = str(input['overflow']) if 'overflow' in input and input['overflow'] != None else None
self.f_type = f"'{self.type}'"
self.f_id = f"'{self.id}'"
self.f_elevation = self.elevation
self.f_init_level = self.init_level
self.f_min_level = self.min_level
self.f_max_level = self.max_level
self.f_diameter = self.diameter
self.f_min_vol = self.min_vol
self.f_vol_curve = f"'{self.vol_curve}'" if self.vol_curve != None else 'null'
self.f_overflow = f"'{self.overflow}'" if self.overflow != None else 'null'
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id, 'x': self.x, 'y': self.y, 'elevation': self.elevation, 'init_level': self.init_level, 'min_level': self.min_level, 'max_level': self.max_level, 'diameter': self.diameter, 'min_vol': self.min_vol, 'vol_curve': self.vol_curve, 'overflow': self.overflow }
def as_id_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id }
def _set_tank(name: str, cs: ChangeSet) -> DbChangeSet:
old = Tank(get_tank(name, cs.operations[0]['id']))
raw_new = get_tank(name, cs.operations[0]['id'])
new_dict = cs.operations[0]
schema = get_tank_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = Tank(raw_new)
redo_sql = f"update tanks set elevation = {new.f_elevation}, init_level = {new.f_init_level}, min_level = {new.f_min_level}, max_level = {new.f_max_level}, diameter = {new.f_diameter}, min_vol = {new.f_min_vol}, vol_curve = {new.f_vol_curve}, overflow = {new.f_overflow} where id = {new.f_id};"
redo_sql += f"\n{sql_update_coord(new.id, new.x, new.y)}"
undo_sql = sql_update_coord(old.id, old.x, old.y)
undo_sql += f"\nupdate tanks set elevation = {old.f_elevation}, init_level = {old.f_init_level}, min_level = {old.f_min_level}, max_level = {old.f_max_level}, diameter = {old.f_diameter}, min_vol = {old.f_min_vol}, vol_curve = {old.f_vol_curve}, overflow = {old.f_overflow} where id = {old.f_id};"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_tank(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_tank(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _set_tank(name, cs))
def _add_tank(name: str, cs: ChangeSet) -> DbChangeSet:
new = Tank(cs.operations[0])
redo_sql = f"insert into _node (id, type) values ({new.f_id}, {new.f_type});"
redo_sql += f"\ninsert into tanks (id, elevation, init_level, min_level, max_level, diameter, min_vol, vol_curve, overflow) values ({new.f_id}, {new.f_elevation}, {new.f_init_level}, {new.f_min_level}, {new.f_max_level}, {new.f_diameter}, {new.f_min_vol}, {new.f_vol_curve}, {new.f_overflow});"
redo_sql += f"\n{sql_insert_coord(new.id, new.x, new.y)}"
undo_sql = sql_delete_coord(new.id)
undo_sql += f"\ndelete from tanks where id = {new.f_id};"
undo_sql += f"\ndelete from _node where id = {new.f_id};"
redo_cs = g_add_prefix | new.as_dict()
undo_cs = g_delete_prefix | new.as_id_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_tank(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_tank(name, cs.operations[0]['id']) != {}:
return ChangeSet()
return execute_command(name, _add_tank(name, cs))
def _delete_tank(name: str, cs: ChangeSet) -> DbChangeSet:
old = Tank(get_tank(name, cs.operations[0]['id']))
redo_sql = sql_delete_coord(old.id)
redo_sql += f"\ndelete from tanks where id = {old.f_id};"
redo_sql += f"\ndelete from _node where id = {old.f_id};"
undo_sql = f"insert into _node (id, type) values ({old.f_id}, {old.f_type});"
undo_sql += f"\ninsert into tanks (id, elevation, init_level, min_level, max_level, diameter, min_vol, vol_curve, overflow) values ({old.f_id}, {old.f_elevation}, {old.f_init_level}, {old.f_min_level}, {old.f_max_level}, {old.f_diameter}, {old.f_min_vol}, {old.f_vol_curve}, {old.f_overflow});"
undo_sql += f"\n{sql_insert_coord(old.id, old.x, old.y)}"
redo_cs = g_delete_prefix | old.as_id_dict()
undo_cs = g_add_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_tank(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_tank(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _delete_tank(name, cs))
#--------------------------------------------------------------
# [EPA2]
# [IN]
# id elev initlevel minlevel maxlevel diam (minvol vcurve overflow) ;desc
# xxx
# * YES
# [OUT]
# id elev initlevel minlevel maxlevel diam minvol (vcurve overflow) ;desc
#--------------------------------------------------------------
# [EPA3]
# id elev initlevel minlevel maxlevel diam minvol (vcurve)
#--------------------------------------------------------------
def inp_in_tank(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
id = str(tokens[0])
elevation = float(tokens[1])
init_level = float(tokens[2])
min_level = float(tokens[3])
max_level = float(tokens[4])
diameter = float(tokens[5])
min_vol = float(tokens[6]) if num_without_desc >= 7 else 0.0
vol_curve = str(tokens[7]) if num_without_desc >= 8 and tokens[7] != '*' else None
vol_curve = f"'{vol_curve}'" if vol_curve != None else 'null'
overflow = str(tokens[8].upper()) if num_without_desc >= 9 else None
overflow = f"'{overflow}'" if overflow != None else 'null'
desc = str(tokens[-1]) if has_desc else None
return str(f"insert into _node (id, type) values ('{id}', 'tank');insert into tanks (id, elevation, init_level, min_level, max_level, diameter, min_vol, vol_curve, overflow) values ('{id}', {elevation}, {init_level}, {min_level}, {max_level}, {diameter}, {min_vol}, {vol_curve}, {overflow});")
def inp_out_tank(name: str) -> list[str]:
lines = []
objs = read_all(name, 'select * from tanks')
for obj in objs:
id = obj['id']
elevation = obj['elevation']
init_level = obj['init_level']
min_level = obj['min_level']
max_level = obj['max_level']
diameter = obj['diameter']
min_vol = obj['min_vol']
vol_curve = obj['vol_curve'] if obj['vol_curve'] != None else ''
overflow = obj['overflow'] if obj['overflow'] != None else ''
if vol_curve == '' and overflow != '':
vol_curve = '*'
desc = ';'
lines.append(f'{id} {elevation} {init_level} {min_level} {max_level} {diameter} {min_vol} {vol_curve} {overflow} {desc}')
return lines
def unset_tank_by_curve(name: str, curve: str) -> ChangeSet:
cs = ChangeSet()
rows = read_all(name, f"select id from tanks where vol_curve = '{curve}'")
for row in rows:
cs.append(g_update_prefix | {'type': 'tank', 'id': row['id'], 'vol_curve': None})
return cs
+214
View File
@@ -0,0 +1,214 @@
from .database import *
from .s0_base import *
PIPE_STATUS_OPEN = 'OPEN'
PIPE_STATUS_CLOSED = 'CLOSED'
PIPE_STATUS_CV = 'CV'
def get_pipe_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'node1' : {'type': 'str' , 'optional': False , 'readonly': False},
'node2' : {'type': 'str' , 'optional': False , 'readonly': False},
'length' : {'type': 'float' , 'optional': False , 'readonly': False},
'diameter' : {'type': 'float' , 'optional': False , 'readonly': False},
'roughness' : {'type': 'float' , 'optional': False , 'readonly': False},
'minor_loss' : {'type': 'float' , 'optional': False , 'readonly': False},
'status' : {'type': 'str' , 'optional': False , 'readonly': False} }
def get_pipe(name: str, id: str) -> dict[str, Any]:
p = try_read(name, f"select * from pipes where id = '{id}'")
if p == None:
return {}
d = {}
d['id'] = str(p['id'])
d['node1'] = str(p['node1'])
d['node2'] = str(p['node2'])
d['length'] = float(p['length'])
d['diameter'] = float(p['diameter'])
d['roughness'] = float(p['roughness'])
d['minor_loss'] = float(p['minor_loss'])
d['status'] = str(p['status'])
return d
# DingZQ, 2025-03-29
def get_all_pipes(name: str) -> list[dict[str, Any]]:
rows = read_all(name, f"select * from pipes")
if rows == None:
return []
result = []
for row in rows:
d = {}
d['id'] = str(row['id'])
d['node1'] = str(row['node1'])
d['node2'] = str(row['node2'])
d['length'] = float(row['length'])
d['diameter'] = float(row['diameter'])
d['roughness'] = float(row['roughness'])
d['minor_loss'] = float(row['minor_loss'])
d['status'] = str(row['status'])
result.append(d)
return result
class Pipe(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'pipe'
self.id = str(input['id'])
self.node1 = str(input['node1'])
self.node2 = str(input['node2'])
self.length = float(input['length'])
self.diameter = float(input['diameter'])
self.roughness = float(input['roughness'])
self.minor_loss = float(input['minor_loss'])
self.status = str(input['status'])
self.f_type = f"'{self.type}'"
self.f_id = f"'{self.id}'"
self.f_node1 = f"'{self.node1}'"
self.f_node2 = f"'{self.node2}'"
self.f_length = self.length
self.f_diameter = self.diameter
self.f_roughness = self.roughness
self.f_minor_loss = self.minor_loss
self.f_status = f"'{self.status}'"
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id, 'node1': self.node1, 'node2': self.node2, 'length': self.length, 'diameter': self.diameter, 'roughness': self.roughness, 'minor_loss': self.minor_loss, 'status': self.status }
def as_id_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id }
def _set_pipe(name: str, cs: ChangeSet) -> DbChangeSet:
old = Pipe(get_pipe(name, cs.operations[0]['id']))
raw_new = get_pipe(name, cs.operations[0]['id'])
new_dict = cs.operations[0]
schema = get_pipe_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = Pipe(raw_new)
redo_sql = f"update pipes set node1 = {new.f_node1}, node2 = {new.f_node2}, length = {new.f_length}, diameter = {new.f_diameter}, roughness = {new.f_roughness}, minor_loss = {new.f_minor_loss}, status = {new.f_status} where id = {new.f_id};"
undo_sql = f"update pipes set node1 = {old.f_node1}, node2 = {old.f_node2}, length = {old.f_length}, diameter = {old.f_diameter}, roughness = {old.f_roughness}, minor_loss = {old.f_minor_loss}, status = {old.f_status} where id = {old.f_id};"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_pipe(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_pipe(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _set_pipe(name, cs))
def _add_pipe(name: str, cs: ChangeSet) -> DbChangeSet:
new = Pipe(cs.operations[0])
redo_sql = f"insert into _link (id, type) values ({new.f_id}, {new.f_type});"
redo_sql += f"\ninsert into pipes (id, node1, node2, length, diameter, roughness, minor_loss, status) values ({new.f_id}, {new.f_node1}, {new.f_node2}, {new.f_length}, {new.f_diameter}, {new.f_roughness}, {new.f_minor_loss}, {new.f_status});"
undo_sql = f"delete from pipes where id = {new.f_id};"
undo_sql += f"\ndelete from _link where id = {new.f_id};"
redo_cs = g_add_prefix | new.as_dict()
undo_cs = g_delete_prefix | new.as_id_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_pipe(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_pipe(name, cs.operations[0]['id']) != {}:
return ChangeSet()
return execute_command(name, _add_pipe(name, cs))
def _delete_pipe(name: str, cs: ChangeSet) -> DbChangeSet:
old = Pipe(get_pipe(name, cs.operations[0]['id']))
redo_sql = f"delete from pipes where id = {old.f_id};"
redo_sql += f"\ndelete from _link where id = {old.f_id};"
undo_sql = f"insert into _link (id, type) values ({old.f_id}, {old.f_type});"
undo_sql += f"\ninsert into pipes (id, node1, node2, length, diameter, roughness, minor_loss, status) values ({old.f_id}, {old.f_node1}, {old.f_node2}, {old.f_length}, {old.f_diameter}, {old.f_roughness}, {old.f_minor_loss}, {old.f_status});"
redo_cs = g_delete_prefix | old.as_id_dict()
undo_cs = g_add_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_pipe(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_pipe(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _delete_pipe(name, cs))
#--------------------------------------------------------------
# [EPA2][EPA3]
# [IN]
# id node1 node2 length diam rcoeff (lcoeff status) ;desc
# [OUT]
# id node1 node2 length diam rcoeff lcoeff (status) ;desc
#--------------------------------------------------------------
def inp_in_pipe(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
id = str(tokens[0])
node1 = str(tokens[1])
node2 = str(tokens[2])
length = float(tokens[3])
diameter = float(tokens[4])
roughness = float(tokens[5])
minor_loss = float(tokens[6])
# status is must-have, here fix input
status = str(tokens[7].upper()) if num_without_desc >= 8 else PIPE_STATUS_OPEN
desc = str(tokens[-1]) if has_desc else None
return str(f"insert into _link (id, type) values ('{id}', 'pipe');insert into pipes (id, node1, node2, length, diameter, roughness, minor_loss, status) values ('{id}', '{node1}', '{node2}', {length}, {diameter}, {roughness}, {minor_loss}, '{status}');")
def inp_out_pipe(name: str) -> list[str]:
lines = []
objs = read_all(name, 'select * from pipes')
for obj in objs:
id = obj['id']
node1 = obj['node1']
node2 = obj['node2']
length = obj['length']
diameter = obj['diameter']
roughness = obj['roughness']
minor_loss = obj['minor_loss']
status = obj['status']
desc = ';'
lines.append(f'{id} {node1} {node2} {length} {diameter} {roughness} {minor_loss} {status} {desc}')
return lines
'''def delete_pipe_by_node(name: str, node: str) -> ChangeSet:
cs = ChangeSet()
rows = read_all(name, f"select id from pipes where node1 = '{node}' or node2 = '{node}'")
for row in rows:
cs.append(g_delete_prefix | {'type': 'pipe', 'id': row['id']})
return cs'''
+231
View File
@@ -0,0 +1,231 @@
from .database import *
from .s0_base import *
def get_pump_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'node1' : {'type': 'str' , 'optional': False , 'readonly': False},
'node2' : {'type': 'str' , 'optional': False , 'readonly': False},
'power' : {'type': 'float' , 'optional': True , 'readonly': False},
'head' : {'type': 'str' , 'optional': True , 'readonly': False},
'speed' : {'type': 'float' , 'optional': True , 'readonly': False},
'pattern' : {'type': 'str' , 'optional': True , 'readonly': False} }
def get_pump(name: str, id: str) -> dict[str, Any]:
p = try_read(name, f"select * from pumps where id = '{id}'")
if p == None:
return {}
d = {}
d['id'] = str(p['id'])
d['node1'] = str(p['node1'])
d['node2'] = str(p['node2'])
d['power'] = float(p['power']) if p['power'] != None else None
d['head'] = str(p['head']) if p['head'] != None else None
d['speed'] = float(p['speed']) if p['speed'] != None else None
d['pattern'] = str(p['pattern']) if p['pattern'] != None else None
return d
# DingZQ, 2025-03-29
def get_all_pumps(name: str) -> list[dict[str, Any]]:
rows = read_all(name, f"select * from pumps")
if rows == None:
return []
result = []
for row in rows:
d = {}
d['id'] = str(row['id'])
d['node1'] = str(row['node1'])
d['node2'] = str(row['node2'])
d['power'] = float(row['power']) if row['power'] != None else None
d['head'] = str(row['head']) if row['head'] != None else None
d['speed'] = float(row['speed']) if row['speed'] != None else None
d['pattern'] = str(row['pattern']) if row['pattern'] != None else None
result.append(d)
return result
class Pump(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'pump'
self.id = str(input['id'])
self.node1 = str(input['node1'])
self.node2 = str(input['node2'])
self.power = float(input['power']) if 'power' in input and input['power'] != None else None
self.head = str(input['head']) if 'head' in input and input['head'] != None else None
self.speed = float(input['speed']) if 'speed' in input and input['speed'] != None else None
self.pattern = str(input['pattern']) if 'pattern' in input and input['pattern'] != None else None
self.f_type = f"'{self.type}'"
self.f_id = f"'{self.id}'"
self.f_node1 = f"'{self.node1}'"
self.f_node2 = f"'{self.node2}'"
self.f_power = self.power if self.power != None else 'null'
self.f_head = f"'{self.head}'" if self.head != None else 'null'
self.f_speed = self.speed if self.speed != None else 'null'
self.f_pattern = f"'{self.pattern}'" if self.pattern != None else 'null'
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id, 'node1': self.node1, 'node2': self.node2, 'power': self.power, 'head': self.head, 'speed': self.speed, 'pattern': self.pattern }
def as_id_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id }
def _set_pump(name: str, cs: ChangeSet) -> DbChangeSet:
old = Pump(get_pump(name, cs.operations[0]['id']))
raw_new = get_pump(name, cs.operations[0]['id'])
new_dict = cs.operations[0]
schema = get_pump_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = Pump(raw_new)
redo_sql = f"update pumps set node1 = {new.f_node1}, node2 = {new.f_node2}, power = {new.f_power}, head = {new.f_head}, speed = {new.f_speed}, pattern = {new.f_pattern} where id = {new.f_id};"
undo_sql = f"update pumps set node1 = {old.f_node1}, node2 = {old.f_node2}, power = {old.f_power}, head = {old.f_head}, speed = {old.f_speed}, pattern = {old.f_pattern} where id = {old.f_id};"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_pump(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_pump(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _set_pump(name, cs))
def _add_pump(name: str, cs: ChangeSet) -> DbChangeSet:
new = Pump(cs.operations[0])
redo_sql = f"insert into _link (id, type) values ({new.f_id}, {new.f_type});"
redo_sql += f"\ninsert into pumps (id, node1, node2, power, head, speed, pattern) values ({new.f_id}, {new.f_node1}, {new.f_node2}, {new.f_power}, {new.f_head}, {new.f_speed}, {new.f_pattern});"
undo_sql = f"delete from pumps where id = {new.f_id};"
undo_sql += f"\ndelete from _link where id = {new.f_id};"
redo_cs = g_add_prefix | new.as_dict()
undo_cs = g_delete_prefix | new.as_id_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_pump(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_pump(name, cs.operations[0]['id']) != {}:
return ChangeSet()
return execute_command(name, _add_pump(name, cs))
def _delete_pump(name: str, cs: ChangeSet) -> DbChangeSet:
old = Pump(get_pump(name, cs.operations[0]['id']))
redo_sql = f"delete from pumps where id = {old.f_id};"
redo_sql += f"\ndelete from _link where id = {old.f_id};"
undo_sql = f"insert into _link (id, type) values ({old.f_id}, {old.f_type});"
undo_sql += f"\ninsert into pumps (id, node1, node2, power, head, speed, pattern) values ({old.f_id}, {old.f_node1}, {old.f_node2}, {old.f_power}, {old.f_head}, {old.f_speed}, {old.f_pattern});"
redo_cs = g_delete_prefix | old.as_id_dict()
undo_cs = g_add_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_pump(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_pump(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _delete_pump(name, cs))
#--------------------------------------------------------------
# [EPA2][EPA3][IN][OUT]
# id node1 node2 KEYWORD value {KEYWORD value ...} ;desc
# where KEYWORD = [POWER,HEAD,PATTERN,SPEED]
#--------------------------------------------------------------
def inp_in_pump(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
id = str(tokens[0])
node1 = str(tokens[1])
node2 = str(tokens[2])
props = {}
for i in range(3, num_without_desc, 2):
props |= { tokens[i].lower(): tokens[i + 1] }
power = float(props['power']) if 'power' in props else None
power = power if power != None else 'null'
head = str(props['head']) if 'head' in props else None
head = f"'{head}'" if head != None else 'null'
speed = float(props['speed']) if 'speed' in props else None
speed = speed if speed != None else 'null'
pattern = str(props['pattern']) if 'pattern' in props else None
pattern = f"'{pattern}'" if pattern != None else 'null'
desc = str(tokens[-1]) if has_desc else None
return str(f"insert into _link (id, type) values ('{id}', 'pump');insert into pumps (id, node1, node2, power, head, speed, pattern) values ('{id}', '{node1}', '{node2}', {power}, {head}, {speed}, {pattern});")
def inp_out_pump(name: str) -> list[str]:
lines = []
objs = read_all(name, 'select * from pumps')
for obj in objs:
id = obj['id']
node1 = obj['node1']
node2 = obj['node2']
power = f"POWER {obj['power']}" if obj['power'] != None else ''
head = f"HEAD {obj['head']}" if obj['head'] != None else ''
speed = f"SPEED {obj['speed']}" if obj['speed'] != None else ''
pattern = f"PATTERN {obj['pattern']}" if obj['pattern'] != None else ''
desc = ';'
lines.append(f'{id} {node1} {node2} {power} {head} {speed} {pattern} {desc}')
return lines
'''def delete_pump_by_node(name: str, node: str) -> ChangeSet:
cs = ChangeSet()
rows = read_all(name, f"select id from pumps where node1 = '{node}' or node2 = '{node}'")
for row in rows:
cs.append(g_delete_prefix | {'type': 'pump', 'id': row['id']})
return cs'''
def unset_pump_by_curve(name: str, curve: str) -> ChangeSet:
cs = ChangeSet()
rows = read_all(name, f"select * from pumps where head = '{curve}'")
for row in rows:
if row['power'] != None:
cs.append(g_update_prefix | {'type': 'pump', 'id': row['id'], 'head': None})
else: # workaround to prevent pump deletion... and I don't want to remove constraint...
cs.append(g_update_prefix | {'type': 'pump', 'id': row['id'], 'head': None, 'power': 0.0})
return cs
def unset_pump_by_pattern(name: str, pattern: str) -> ChangeSet:
cs = ChangeSet()
rows = read_all(name, f"select id from pumps where pattern = '{pattern}'")
for row in rows:
cs.append(g_update_prefix | {'type': 'pump', 'id': row['id'], 'pattern': None})
return cs
+210
View File
@@ -0,0 +1,210 @@
from .database import *
from .s0_base import *
VALVES_TYPE_PRV = 'PRV'
VALVES_TYPE_PSV = 'PSV'
VALVES_TYPE_PBV = 'PBV'
VALVES_TYPE_FCV = 'FCV'
VALVES_TYPE_TCV = 'TCV'
VALVES_TYPE_GPV = 'GPV'
def get_valve_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'node1' : {'type': 'str' , 'optional': False , 'readonly': False},
'node2' : {'type': 'str' , 'optional': False , 'readonly': False},
'diameter' : {'type': 'float' , 'optional': False , 'readonly': False},
'v_type' : {'type': 'str' , 'optional': False , 'readonly': False},
'setting' : {'type': 'str' , 'optional': False , 'readonly': False},
'minor_loss' : {'type': 'float' , 'optional': False , 'readonly': False} }
def get_valve(name: str, id: str) -> dict[str, Any]:
p = try_read(name, f"select * from valves where id = '{id}'")
if p == None:
return {}
d = {}
d['id'] = str(p['id'])
d['node1'] = str(p['node1'])
d['node2'] = str(p['node2'])
d['diameter'] = float(p['diameter'])
d['v_type'] = str(p['v_type'])
d['setting'] = str(p['setting'])
d['minor_loss'] = float(p['minor_loss'])
return d
def get_all_valves(name: str) -> list[dict[str, Any]]:
rows = read_all(name, f"select * from valves")
if rows == None:
return []
result = []
for row in rows:
d = {}
d['id'] = str(row['id'])
d['node1'] = str(row['node1'])
d['node2'] = str(row['node2'])
d['diameter'] = float(row['diameter'])
d['v_type'] = str(row['v_type'])
d['setting'] = str(row['setting'])
d['minor_loss'] = float(row['minor_loss'])
result.append(d)
return result
class Valve(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'valve'
self.id = str(input['id'])
self.node1 = str(input['node1'])
self.node2 = str(input['node2'])
self.diameter = float(input['diameter'])
self.v_type = str(input['v_type'])
self.setting = str(input['setting'])
self.minor_loss = float(input['minor_loss'])
self.f_type = f"'{self.type}'"
self.f_id = f"'{self.id}'"
self.f_node1 = f"'{self.node1}'"
self.f_node2 = f"'{self.node2}'"
self.f_diameter = self.diameter
self.f_v_type = f"'{self.v_type}'"
self.f_setting = f"'{self.setting}'"
self.f_minor_loss = self.minor_loss
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id, 'node1': self.node1, 'node2': self.node2, 'diameter': self.diameter, 'v_type': self.v_type, 'setting': self.setting, 'minor_loss': self.minor_loss }
def as_id_dict(self) -> dict[str, Any]:
return { 'type': self.type, 'id': self.id }
def _set_valve(name: str, cs: ChangeSet) -> DbChangeSet:
old = Valve(get_valve(name, cs.operations[0]['id']))
raw_new = get_valve(name, cs.operations[0]['id'])
new_dict = cs.operations[0]
schema = get_valve_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = Valve(raw_new)
redo_sql = f"update valves set node1 = {new.f_node1}, node2 = {new.f_node2}, diameter = {new.f_diameter}, v_type = {new.f_v_type}, setting = {new.f_setting}, minor_loss = {new.f_minor_loss} where id = {new.f_id};"
undo_sql = f"update valves set node1 = {old.f_node1}, node2 = {old.f_node2}, diameter = {old.f_diameter}, v_type = {old.f_v_type}, setting = {old.f_setting}, minor_loss = {old.f_minor_loss} where id = {old.f_id};"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_valve(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_valve(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _set_valve(name, cs))
def _add_valve(name: str, cs: ChangeSet) -> DbChangeSet:
new = Valve(cs.operations[0])
redo_sql = f"insert into _link (id, type) values ({new.f_id}, {new.f_type});"
redo_sql += f"\ninsert into valves (id, node1, node2, diameter, v_type, setting, minor_loss) values ({new.f_id}, {new.f_node1}, {new.f_node2}, {new.f_diameter}, {new.f_v_type}, {new.f_setting}, {new.f_minor_loss});"
undo_sql = f"delete from valves where id = {new.f_id};"
undo_sql += f"\ndelete from _link where id = {new.f_id};"
redo_cs = g_add_prefix | new.as_dict()
undo_cs = g_delete_prefix | new.as_id_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_valve(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_valve(name, cs.operations[0]['id']) != {}:
return ChangeSet()
return execute_command(name, _add_valve(name, cs))
def _delete_valve(name: str, cs: ChangeSet) -> DbChangeSet:
old = Valve(get_valve(name, cs.operations[0]['id']))
redo_sql = f"delete from valves where id = {old.f_id};"
redo_sql += f"\ndelete from _link where id = {old.f_id};"
undo_sql = f"insert into _link (id, type) values ({old.f_id}, {old.f_type});"
undo_sql += f"\ninsert into valves (id, node1, node2, diameter, v_type, setting, minor_loss) values ({old.f_id}, {old.f_node1}, {old.f_node2}, {old.f_diameter}, {old.f_v_type}, {old.f_setting}, {old.f_minor_loss});"
redo_cs = g_delete_prefix | old.as_id_dict()
undo_cs = g_add_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_valve(name: str, cs: ChangeSet) -> ChangeSet:
if 'id' not in cs.operations[0]:
return ChangeSet()
if get_valve(name, cs.operations[0]['id']) == {}:
return ChangeSet()
return execute_command(name, _delete_valve(name, cs))
#--------------------------------------------------------------
# [EPA2][EPA3][IN][OUT]
# id node1 node2 diam type setting (lcoeff lcurve)
# for GPV, setting is string = head curve id
# [NOT SUPPORT] for PCV, add loss curve if present
#--------------------------------------------------------------
def inp_in_valve(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
id = str(tokens[0])
node1 = str(tokens[1])
node2 = str(tokens[2])
diameter = float(tokens[3])
v_type = str(tokens[4].upper())
setting = str(tokens[5])
minor_loss = float(tokens[6]) if len(tokens) >= 7 else 0.0
desc = str(tokens[-1]) if has_desc else None
return str(f"insert into _link (id, type) values ('{id}', 'valve');insert into valves (id, node1, node2, diameter, v_type, setting, minor_loss) values ('{id}', '{node1}', '{node2}', {diameter}, '{v_type}', '{setting}', {minor_loss});")
def inp_out_valve(name: str) -> list[str]:
lines = []
objs = read_all(name, 'select * from valves')
for obj in objs:
id = obj['id']
node1 = obj['node1']
node2 = obj['node2']
diameter = obj['diameter']
v_type = obj['v_type']
setting = obj['setting']
minor_loss = obj['minor_loss']
desc = ';'
lines.append(f'{id} {node1} {node2} {diameter} {v_type} {setting} {minor_loss} {desc}')
return lines
'''def delete_valve_by_node(name: str, node: str) -> ChangeSet:
cs = ChangeSet()
rows = read_all(name, f"select id from valves where node1 = '{node}' or node2 = '{node}'")
for row in rows:
cs.append(g_delete_prefix | {'type': 'valve', 'id': row['id']})
return cs'''
+142
View File
@@ -0,0 +1,142 @@
from typing import Any
from .database import ChangeSet, execute_command, try_read, read_all, DbChangeSet, g_update_prefix
TAG_TYPE_NODE = 'NODE'
TAG_TYPE_LINK = 'LINK'
def get_tag_schema(name: str) -> dict[str, dict[str, Any]]:
return { 't_type' : {'type': 'str' , 'optional': False , 'readonly': False},
'id' : {'type': 'str' , 'optional': False , 'readonly': False},
'tag' : {'type': 'str' , 'optional': True , 'readonly': False},}
def get_tags(name: str) -> list[dict[str, Any]]:
results: list[dict[str, Any]] = []
rows = read_all(name, "select * from tags_node")
for row in rows:
tag = str(row['tag']) if row['tag'] != None else None
results.append({ 't_type': TAG_TYPE_NODE, 'id': str(row['id']), 'tag': tag })
rows = read_all(name, "select * from tags_link")
for row in rows:
tag = str(row['tag']) if row['tag'] != None else None
results.append({ 't_type': TAG_TYPE_LINK, 'id': str(row['id']), 'tag': tag })
return results
def get_tag(name: str, t_type: str, id: str) -> dict[str, Any]:
t = None
if t_type == TAG_TYPE_NODE:
t = try_read(name, f"select * from tags_node where id = '{id}'")
elif t_type == TAG_TYPE_LINK:
t = try_read(name, f"select * from tags_link where id = '{id}'")
if t is None:
return { 't_type': t_type, 'id': id, 'tag': None }
d = {}
d['t_type'] = t_type
d['id'] = str(t['id'])
d['tag'] = str(t['tag']) if t['tag'] is not None else None
return d
class Tag(object):
def __init__(self, input: dict[str, Any]) -> None:
self.type = 'tag'
self.t_type = str(input['t_type'])
self.id = str(input['id'])
self.tag = str(input['tag']) if 'tag' in input and input['tag'] != None else None
self.f_type = f"'{self.type}'"
self.f_t_type = f"'{self.t_type}'"
self.f_id = f"'{self.id}'"
self.f_tag = f"'{self.tag}'" if self.tag != None else 'null'
def as_dict(self) -> dict[str, Any]:
return { 'type': self.type, 't_type': self.t_type, 'id': self.id, 'tag': self.tag }
def _set_tag(name: str, cs: ChangeSet) -> DbChangeSet:
old = Tag(get_tag(name, cs.operations[0]['t_type'], cs.operations[0]['id']))
raw_new = get_tag(name, cs.operations[0]['t_type'], cs.operations[0]['id'])
new_dict = cs.operations[0]
schema = get_tag_schema(name)
for key, value in schema.items():
if key in new_dict and not value['readonly']:
raw_new[key] = new_dict[key]
new = Tag(raw_new)
table = ''
if cs.operations[0]['t_type'] == TAG_TYPE_NODE:
table = 'tags_node'
elif cs.operations[0]['t_type'] == TAG_TYPE_LINK:
table = 'tags_link'
else:
raise Exception('Only support NODE and Link')
redo_sql = f"delete from {table} where id = {new.f_id};"
if new.tag is not None:
redo_sql += f"\ninsert into {table} (id, tag) values ({new.f_id}, {new.f_tag});"
undo_sql = f"delete from {table} where id = {old.f_id};"
if old.tag is not None:
undo_sql += f"\ninsert into {table} (id, tag) values ({old.f_id}, {old.f_tag});"
redo_cs = g_update_prefix | new.as_dict()
undo_cs = g_update_prefix | old.as_dict()
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_tag(name: str, cs: ChangeSet) -> ChangeSet:
if 't_type' not in cs.operations[0] or 'id' not in cs.operations[0] or 'tag' not in cs.operations[0]:
return ChangeSet()
return execute_command(name, _set_tag(name, cs))
def inp_in_tag(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
t_type = str(tokens[0].upper())
id = str(tokens[1])
tag = str(tokens[2])
if t_type == TAG_TYPE_NODE:
return str(f"insert into tags_node (id, tag) values ('{id}', '{tag}');")
elif t_type == TAG_TYPE_LINK:
return str(f"insert into tags_link (id, tag) values ('{id}', '{tag}');")
return str('')
def inp_out_tag(name: str) -> list[str]:
lines = []
objs = read_all(name, 'select * from tags_node')
for obj in objs:
t_type = TAG_TYPE_NODE
id = obj['id']
tag = obj['tag']
lines.append(f'{t_type} {id} {tag}')
objs = read_all(name, 'select * from tags_link')
for obj in objs:
t_type = TAG_TYPE_LINK
id = obj['id']
tag = obj['tag']
lines.append(f'{t_type} {id} {tag}')
return lines
def delete_tag_by_node(name: str, node: str) -> ChangeSet:
row = try_read(name, f"select * from tags_node where id = '{node}'")
if row is None:
return ChangeSet()
return ChangeSet(g_update_prefix | {'type': 'tag', 't_type': TAG_TYPE_NODE, 'id': node, 'tag': None })
def delete_tag_by_link(name: str, link: str) -> ChangeSet:
row = try_read(name, f"select * from tags_link where id = '{link}'")
if row is None:
return ChangeSet()
return ChangeSet(g_update_prefix | {'type': 'tag', 't_type': TAG_TYPE_LINK, 'id': link, 'tag': None })
+115
View File
@@ -0,0 +1,115 @@
from .database import read_all, ChangeSet, DbChangeSet, g_update_prefix, execute_command, try_read
from typing import Any
def get_demand_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'junction' : {'type': 'str' , 'optional': False , 'readonly': True },
'demands' : {'type': 'list' , 'optional': False , 'readonly': False,
'element': { 'demand' : {'type': 'float' , 'optional': False , 'readonly': False },
'pattern' : {'type': 'str' , 'optional': True , 'readonly': False },
'category': {'type': 'str' , 'optional': True , 'readonly': False }}}}
def get_demand(name: str, junction: str) -> dict[str, Any]:
des = read_all(name, f"select * from demands where junction = '{junction}' order by _order")
ds = []
for r in des:
d = {}
d['demand'] = float(r['demand'])
d['pattern'] = str(r['pattern']) if r['pattern'] != None else None
d['category'] = str(r['category']) if r['category'] != None else None
ds.append(d)
return { 'junction': junction, 'demands': ds }
def _set_demand(name: str, cs: ChangeSet) -> DbChangeSet:
junction = cs.operations[0]['junction']
old = get_demand(name, junction)
new = { 'junction': junction, 'demands': [] }
f_junction = f"'{junction}'"
# TODO: transaction ?
redo_sql = f"delete from demands where junction = {f_junction};"
for r in cs.operations[0]['demands']:
demand = float(r['demand'])
pattern = str(r['pattern']) if 'pattern' in r and r['pattern'] != None else None
category = str(r['category']) if 'category' in r and r['category'] != None else None
f_demand = demand
f_pattern = f"'{pattern}'" if pattern is not None else 'null'
f_category = f"'{category}'" if category is not None else 'null'
redo_sql += f"\ninsert into demands (junction, demand, pattern, category) values ({f_junction}, {f_demand}, {f_pattern}, {f_category});"
new['demands'].append({ 'demand': demand, 'pattern': pattern, 'category': category })
undo_sql = f"delete from demands where junction = {f_junction};"
for r in old['demands']:
demand = float(r['demand'])
pattern = str(r['pattern']) if 'pattern' in r and r['pattern'] != None else None
category = str(r['category']) if 'category' in r and r['category'] != None else None
f_demand = demand
f_pattern = f"'{pattern}'" if pattern is not None else 'null'
f_category = f"'{category}'" if category is not None else 'null'
undo_sql += f"\ninsert into demands (junction, demand, pattern, category) values ({f_junction}, {f_demand}, {f_pattern}, {f_category});"
redo_cs = g_update_prefix | { 'type': 'demand' } | new
undo_cs = g_update_prefix | { 'type': 'demand' } | old
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_demand(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, _set_demand(name, cs))
#--------------------------------------------------------------
# [EPA2][EPA3][IN][OUT]
# node base_demand (pattern) ;category
#--------------------------------------------------------------
def inp_in_demand(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
junction = str(tokens[0])
demand = float(tokens[1])
pattern = str(tokens[2]) if num_without_desc >= 3 else None
pattern = f"'{pattern}'" if pattern is not None else 'null'
category = str(tokens[3]) if num_without_desc >= 4 else None
category = f"'{category}'" if category is not None else 'null'
return str(f"insert into demands (junction, demand, pattern, category) values ('{junction}', {demand}, {pattern}, {category});")
def inp_out_demand(name: str) -> list[str]:
lines = []
objs = read_all(name, "select * from demands order by _order")
for obj in objs:
junction = obj['junction']
demand = obj['demand']
pattern = obj['pattern'] if obj['pattern'] is not None else ''
category = f";{obj['category']}" if obj['category'] is not None else ';'
lines.append(f'{junction} {demand} {pattern} {category}')
return lines
def delete_demand_by_junction(name: str, junction: str) -> ChangeSet:
row = try_read(name, f"select * from demands where junction = '{junction}'")
if row is None:
return ChangeSet()
return ChangeSet(g_update_prefix | {'type': 'demand', 'junction': junction, 'demands': []})
def unset_demand_by_pattern(name: str, pattern: str) -> ChangeSet:
cs = ChangeSet()
rows = read_all(name, f"select distinct junction from demands where pattern = '{pattern}'")
for row in rows:
ds = get_demand(name, row['junction'])
for d in ds['demands']:
d['pattern'] = None
cs.append(g_update_prefix | {'type': 'demand', 'junction': row['junction'], 'demands': ds['demands']})
return cs
+90
View File
@@ -0,0 +1,90 @@
s1_title = 'title'
s2_junction = 'junction'
s3_reservoir = 'reservoir'
s4_tank = 'tank'
s5_pipe = 'pipe'
s6_pump = 'pump'
s7_valve = 'valve'
s8_tag = 'tag'
s9_demand = 'demand'
s10_status = 'status'
s11_pattern = 'pattern'
s12_curve = 'curve'
s13_control = 'control'
s14_rule = 'rule'
s15_energy = 'energy'
s15_pump_energy = 'pump_energy'
s16_emitter = 'emitter'
s17_quality = 'quality'
s18_source = 'source'
s19_reaction = 'reaction'
s19_pipe_reaction = 'pipe_reaction'
s19_tank_reaction = 'tank_reaction'
s20_mixing = 'mixing'
s21_time = 'time'
s22_report = 'report'
s23_option = 'option'
s23_option_v3 = 'option_v3'
s24_coordinate = 'coordinate'
s25_vertex = 'vertex'
s26_label = 'label'
s27_backdrop = 'backdrop'
s28_end = 'end'
s29_scada_device = 'scada_device'
s30_scada_device_data = 'scada_device_data'
s31_scada_element = 'scada_element'
s32_region = 'region'
s33_dma = 'district_metering_area'
s34_sa = 'service_area'
s35_vd = 'virtual_district'
TITLE = 'TITLE'
JUNCTIONS = 'JUNCTIONS'
RESERVOIRS = 'RESERVOIRS'
TANKS = 'TANKS'
PIPES = 'PIPES'
PUMPS = 'PUMPS'
VALVES = 'VALVES'
TAGS = 'TAGS'
DEMANDS = 'DEMANDS'
STATUS = 'STATUS'
PATTERNS = 'PATTERNS'
CURVES = 'CURVES'
CONTROLS = 'CONTROLS'
RULES = 'RULES'
ENERGY = 'ENERGY'
EMITTERS = 'EMITTERS'
QUALITY = 'QUALITY'
SOURCES = 'SOURCES'
REACTIONS = 'REACTIONS'
MIXING = 'MIXING'
TIMES = 'TIMES'
REPORT = 'REPORT'
OPTIONS = 'OPTIONS'
COORDINATES = 'COORDINATES'
VERTICES = 'VERTICES'
REGION='REGION'
BOUND='BOUND'
REGION_NODES='DATA_NODE_OF_REGION'
LABELS = 'LABELS'
BACKDROP = 'BACKDROP'
END = 'END'
section_name = [TITLE, JUNCTIONS, RESERVOIRS, TANKS, PIPES,
PUMPS, VALVES, TAGS, DEMANDS, STATUS,
PATTERNS, CURVES, CONTROLS, RULES, ENERGY,
EMITTERS, QUALITY, SOURCES, REACTIONS, MIXING,
TIMES, REPORT, OPTIONS, COORDINATES, VERTICES,
REGION, BOUND, REGION_NODES, LABELS, BACKDROP, END]
# DingZQ, 2025-02-04
# 我们在从服务器调用run_project的时候
# 会将 database的project内容dump成 epanet v2 的inp文件,然后调用 runepanet.exe 去计算结果
# 其中上面的 SECTION REGION, BOUND, REGION_NODES 在 epanet v2 中没有,是我们自己定制的
# 所以需要将这些 section 从 section_name 中移除
section_names_for_epanetv2 = [TITLE, JUNCTIONS, RESERVOIRS, TANKS, PIPES,
PUMPS, VALVES, TAGS, DEMANDS, STATUS,
PATTERNS, CURVES, CONTROLS, RULES, ENERGY,
EMITTERS, QUALITY, SOURCES, REACTIONS, MIXING,
TIMES, REPORT, OPTIONS, COORDINATES, VERTICES,
LABELS, BACKDROP, END]
BIN
View File
Binary file not shown.
File diff suppressed because it is too large Load Diff
+109
View File
@@ -0,0 +1,109 @@
import wntr
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import sklearn.cluster
import os
class QD_KMeans(object):
def __init__(self, wn, num_monitors):
# self.inp = inp
self.cluster_num = num_monitors # 聚类中心个数,也即测压点个数
self.wn=wn
self.monitor_nodes = []
self.coords = []
self.junction_nodes = {} # Added missing initialization
def get_junctions_coordinates(self):
for junction_name in self.wn.junction_name_list:
junction = self.wn.get_node(junction_name)
self.junction_nodes[junction_name] = junction.coordinates
self.coords.append(junction.coordinates )
# print(f"Total junctions: {self.junction_coordinates}")
def select_monitoring_points(self):
if not self.coords: # Add check if coordinates are collected
self.get_junctions_coordinates()
coords = np.array(self.coords)
coords_normalized = (coords - coords.min(axis=0)) / (coords.max(axis=0) - coords.min(axis=0))
kmeans = sklearn.cluster.KMeans(n_clusters= self.cluster_num, random_state=42)
kmeans.fit(coords_normalized)
for center in kmeans.cluster_centers_:
distances = np.sum((coords_normalized - center) ** 2, axis=1)
nearest_node = self.wn.junction_name_list[np.argmin(distances)]
self.monitor_nodes.append(nearest_node)
return self.monitor_nodes
def visualize_network(self):
"""Visualize network with monitoring points"""
ax=wntr.graphics.plot_network(self.wn,
node_attribute=self.monitor_nodes,
node_size=30,
title='Optimal sensor')
plt.show()
def kmeans_sensor_placement(name: str, sensor_num: int, min_diameter: int) -> list:
inp_name = f'./db_inp/{name}.db.inp'
wn= wntr.network.WaterNetworkModel(inp_name)
wn_cluster=QD_KMeans(wn, sensor_num)
# Select monitoring pointse
sensor_ids= wn_cluster.select_monitoring_points()
# wn_cluster.visualize_network()
return sensor_ids
if __name__ == "__main__":
#sensorindex = get_ID(name='suzhouhe_2024_cloud_0817', sensor_num=30, min_diameter=500)
sensorindex = kmeans_sensor_placement(name='szh', sensor_num=50, min_diameter=300)
print(sensorindex)
+115
View File
@@ -0,0 +1,115 @@
import schedule
import time
import datetime
import shutil
import redis
import urllib.request
import influxdb_api
import msgpack
import datetime
# 将 Query的信息 序列号到 redis/json 默认不支持datetime,需要自定义
# 自定义序列化函数
# 序列化处理器
def encode_datetime(obj):
"""将datetime转换为可序列化的字典结构"""
if isinstance(obj, datetime.datetime):
return {
'__datetime__': True,
'as_str': obj.strftime("%Y%m%dT%H:%M:%S.%f")
}
return obj
# 反序列化处理器
def decode_datetime(obj):
"""将字典还原为datetime对象"""
if '__datetime__' in obj:
return datetime.datetime.strptime(
obj['as_str'], "%Y%m%dT%H:%M:%S.%f"
)
return obj
##########################
# 需要用Python 3.12 来运行才能提高performance
##########################
def queryallrecordsbydate(querydate: str, redis_client: redis.Redis):
cache_key = f"queryallrecordsbydate_{querydate}"
exists = redis_client.exists(cache_key)
if not exists:
nodes_links: tuple = influxdb_api.query_all_records_by_date(query_date=querydate)
redis_client.set(cache_key, msgpack.packb(nodes_links, default=encode_datetime))
def queryallrecordsbydate_by_url(querydate: str):
print(f'queryallrecordsbydate: {querydate}')
try:
response = urllib.request.urlopen(
f"http://localhost/queryallrecordsbydate/?querydate={querydate}"
)
html = response.read().decode("utf-8")
except urllib.error.URLError as e:
print("Error")
def queryallscadarecordsbydate(querydate: str, redis_client: redis.Redis):
cache_key = f"queryallscadarecordsbydate_{querydate}"
exists = redis_client.exists(cache_key)
if not exists:
result_dict = influxdb_api.query_all_SCADA_records_by_date(query_date=querydate)
redis_client.set(cache_key, msgpack.packb(result_dict, default=encode_datetime))
def queryallscadarecordsbydate_by_url(querydate: str):
print(f'queryallscadarecordsbydate: {querydate}')
try:
response = urllib.request.urlopen(
f"http://localhost/queryallscadarecordsbydate/?querydate={querydate}"
)
html = response.read().decode("utf-8")
except urllib.error.URLError as e:
print("Error")
def auto_cache_data():
# 初始化 Redis 连接
# 用redis 限制并发访u
redis_client = redis.Redis(host="localhost", port=6379, db=0)
# auto cache data for the last 3 days
today = datetime.date.today()
for i in range(1, 4):
prev_day = today - datetime.timedelta(days=i)
str_prev_day = prev_day.strftime('%Y-%m-%d')
print(str_prev_day)
queryallrecordsbydate(str_prev_day, redis_client)
queryallscadarecordsbydate(str_prev_day, redis_client)
redis_client.close()
def auto_cache_data_by_url():
# auto cache data for the last 3 days
today = datetime.date.today()
for i in range(1, 4):
prev_day = today - datetime.timedelta(days=i)
str_prev_day = prev_day.strftime('%Y-%m-%d')
print(str_prev_day)
queryallrecordsbydate_by_url(str_prev_day)
queryallscadarecordsbydate_by_url(str_prev_day)
if __name__ == "__main__":
auto_cache_data_by_url()
# auto run in the midnight
schedule.every().day.at("03:00").do(auto_cache_data_by_url)
while True:
schedule.run_pending()
time.sleep(1)
+156
View File
@@ -0,0 +1,156 @@
from logging.handlers import TimedRotatingFileHandler
import influxdb_api
import os
import logging
import globals
from datetime import datetime, timedelta, timezone
import schedule
import time
import shutil
from influxdb_client import InfluxDBClient, BucketsApi, WriteApi, OrganizationsApi, Point, QueryApi
import simulation
import influxdb_info
import project_info
def setup_logger():
# 创建日志目录
log_dir = "logs"
os.makedirs(log_dir, exist_ok=True)
# 配置基础日志格式
log_format = "%(asctime)s - %(levelname)s - %(message)s"
formatter = logging.Formatter(log_format)
# 创建主 Logger
logger = logging.getLogger()
logger.setLevel(logging.INFO) # 全局日志级别
# --- 1. 按日期分割的日志文件 Handler ---
log_file = os.path.join(log_dir, "simulation.log")
file_handler = TimedRotatingFileHandler(
filename=log_file,
when="midnight", # 每天午夜轮转
interval=1,
backupCount=7,
encoding="utf-8"
)
file_handler.suffix = "simulation-%Y-%m-%d.log" # 文件名格式
file_handler.setFormatter(formatter)
file_handler.setLevel(logging.INFO) # 文件记录所有级别日志
# --- 2. 控制台实时输出 Handler ---
console_handler = logging.StreamHandler() # 默认输出到 sys.stderr (控制台)
console_handler.setFormatter(formatter)
console_handler.setLevel(logging.INFO) # 控制台仅显示 INFO 及以上级别
# 将 Handler 添加到 Logger
logger.addHandler(file_handler)
#logger.addHandler(console_handler)
return logger
logger = setup_logger()
# 2025/02/01
def get_next_time() -> str:
"""
获取下一个1分钟时间点,返回格式为字符串'YYYY-MM-DDTHH:MM:00+08:00'
:return: 返回字符串格式的时间,表示下一个1分钟的时间点
"""
# 获取当前时间,并设定为北京时间
now = datetime.now() # now 类型为 datetime,表示当前本地时间
# 获取当前的分钟,并且将秒和微秒置为零
current_time = now.replace(second=0, microsecond=0) # current_time 类型为 datetime,时间的秒和微秒部分被清除
return current_time.strftime('%Y-%m-%dT%H:%M:%S+08:00')
# 2025/02/06
def store_realtime_SCADA_data_job() -> None:
"""
定义的任务1,每分钟执行1次,每次执行时,更新get_real_value_time并调用store_realtime_SCADA_data_to_influxdb函数
:return: None
"""
# 获取当前时间并更新get_real_value_time,转换为字符串格式
get_real_value_time: str = get_next_time() # get_real_value_time 类型为 str,格式为'2025-02-01T18:45:00+08:00'
# 调用函数执行任务
influxdb_api.store_realtime_SCADA_data_to_influxdb(get_real_value_time)
logger.info('{} -- Successfully store realtime SCADA data.'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
# 2025/02/06
def get_next_15minute_time() -> str:
"""
获取下一个15分钟的时间点,返回格式为字符串'YYYY-MM-DDTHH:MM:00+08:00'
:return: 返回字符串格式的时间,表示下一个15分钟执行时间点
"""
now = datetime.now()
# 向上舍入到下一个15分钟
next_15minute = (now.minute // 15 + 1) * 15 - 15
if next_15minute == 60:
next_15minute = 0
now = now + timedelta(hours=1)
next_time = now.replace(minute=next_15minute, second=0, microsecond=0)
return next_time.strftime('%Y-%m-%dT%H:%M:%S+08:00')
# 2025/02/07
def run_simulation_job() -> None:
"""
定义的任务3,每15分钟执行一次在store_realtime_SCADA_data_to_influxdb之后执行run_simulation。
:return: None
"""
# 获取当前时间,并检查是否是整点15分钟
current_time = datetime.now()
if current_time.minute % 15 == 0:
print(f"{current_time.strftime('%Y-%m-%d %H:%M:%S')} -- Start simulation task.")
# 计算前,获取scada_info中的信息,按照设定的方法修改pg数据库
simulation.query_corresponding_element_id_and_query_id(project_info.name)
simulation.query_corresponding_pattern_id_and_query_id(project_info.name)
region_result = simulation.query_non_realtime_region(project_info.name)
globals.source_outflow_region_id = simulation.get_source_outflow_region_id(project_info.name, region_result)
globals.realtime_region_pipe_flow_and_demand_id = simulation.query_realtime_region_pipe_flow_and_demand_id(project_info.name, region_result)
globals.pipe_flow_region_patterns = simulation.query_pipe_flow_region_patterns(project_info.name)
globals.non_realtime_region_patterns = simulation.query_non_realtime_region_patterns(project_info.name, region_result)
globals.source_outflow_region_patterns, realtime_region_pipe_flow_and_demand_patterns = simulation.get_realtime_region_patterns(project_info.name,
globals.source_outflow_region_id,
globals.realtime_region_pipe_flow_and_demand_id)
modify_pattern_start_time: str = get_next_15minute_time() # 获取下一个15分钟时间点
# print(modify_pattern_start_time)
simulation.run_simulation(name=project_info.name, simulation_type="realtime", modify_pattern_start_time=modify_pattern_start_time)
logger.info('{} -- Successfully run simulation and store realtime simulation result.'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
else:
logger.info(f"{current_time.strftime('%Y-%m-%d %H:%M:%S')} -- Skipping the simulation task.")
# 2025/02/06
def realtime_task() -> None:
"""
定时执行任务1和,使用schedule库每1分钟执行一次store_realtime_SCADA_data_job函数。
该任务会一直运行,定期调用store_realtime_SCADA_data_job获取SCADA数据。
:return:
"""
# 等待到整分对齐
now = datetime.now()
wait_seconds = 60 - now.second
time.sleep(wait_seconds)
# 使用 .at(":00") 指定在每分钟的第0秒执行
schedule.every(1).minute.at(":00").do(store_realtime_SCADA_data_job)
# 每15分钟执行一次run_simulation_job
schedule.every(1).minute.at(":00").do(run_simulation_job)
# 持续执行任务,检查是否有待执行的任务
while True:
schedule.run_pending() # 执行所有待处理的定时任务
time.sleep(1) # 暂停1秒,避免过于频繁的任务检查
if __name__ == "__main__":
url = influxdb_info.url
token = influxdb_info.token
org_name = influxdb_info.org
client = InfluxDBClient(url=url, token=token)
# step2: 先查询pg数据库中scada_info的信息,然后存储SCADA数据到SCADA_data这个bucket里
influxdb_api.query_pg_scada_info_realtime(project_info.name)
# 自动执行
realtime_task()
+139
View File
@@ -0,0 +1,139 @@
import influxdb_api
import globals
from datetime import datetime, timedelta, timezone
import schedule
import os
import logging
from logging.handlers import TimedRotatingFileHandler
import time
from influxdb_client import InfluxDBClient, BucketsApi, WriteApi, OrganizationsApi, Point, QueryApi
import influxdb_info
import project_info
def setup_logger():
# 创建日志目录
log_dir = "logs"
os.makedirs(log_dir, exist_ok=True)
# 配置基础日志格式
log_format = "%(asctime)s - %(levelname)s - %(message)s"
formatter = logging.Formatter(log_format)
# 创建主 Logger
logger = logging.getLogger()
logger.setLevel(logging.INFO) # 全局日志级别
# --- 1. 按日期分割的日志文件 Handler ---
log_file = os.path.join(log_dir, "scada.log")
file_handler = TimedRotatingFileHandler(
filename=log_file,
when="midnight", # 每天午夜轮转
interval=1,
backupCount=7,
encoding="utf-8"
)
file_handler.suffix = "scada-%Y-%m-%d.log" # 文件名格式
file_handler.setFormatter(formatter)
file_handler.setLevel(logging.INFO) # 文件记录 INFO 及以上级别
# --- 2. 控制台实时输出 Handler ---
console_handler = logging.StreamHandler() # 默认输出到 sys.stderr (控制台)
console_handler.setFormatter(formatter)
console_handler.setLevel(logging.INFO) # 控制台仅显示 INFO 及以上级别
# 将 Handler 添加到 Logger
logger.addHandler(file_handler)
# logger.addHandler(console_handler)
return logger
logger = setup_logger()
# 2025/02/01
def get_next_time() -> str:
"""
获取下一个1分钟时间点,返回格式为字符串'YYYY-MM-DDTHH:MM:00+08:00'
:return: 返回字符串格式的时间,表示下一个1分钟的时间点
"""
# 获取当前时间,并设定为北京时间
now = datetime.now() # now 类型为 datetime,表示当前本地时间
# 获取当前的分钟,并且将秒和微秒置为零
current_time = now.replace(second=0, microsecond=0) # current_time 类型为 datetime,时间的秒和微秒部分被清除
return current_time.strftime('%Y-%m-%dT%H:%M:%S+08:00')
# 2025/02/06
def get_next_period_time() -> str:
"""
获取下一个6小时时间点,返回格式为字符串'YYYY-MM-DDTHH:00:00+08:00'
:return: 返回字符串格式的时间,表示下一个6小时执行时间点
"""
# 获取当前时间,并设定为北京时间
now = datetime.now() # now 类型为 datetime,表示当前本地时间
# 获取当前的小时数并计算下一个6小时时间点
next_period_hour = (now.hour // 6 + 1) * 6 - 6 # next_period_hour 类型为 int,表示下一个6小时时间点的小时部分
# 如果计算的小时大于23,表示进入第二天,调整为00:00
if next_period_hour >= 24:
next_period_hour = 0
now = now + timedelta(days=1) # 如果超过24小时,日期增加1天
# 将秒和微秒部分清除,构建出下一个6小时点的datetime对象
next_period_time = now.replace(hour=next_period_hour, minute=0, second=0, microsecond=0)
return next_period_time.strftime('%Y-%m-%dT%H:%M:%S+08:00') # 格式化为指定的字符串格式并返回
# 2025/02/06
def store_non_realtime_SCADA_data_job() -> None:
"""
定义的任务2,每6小时执行一次,在0点、6点、12点、18点执行,执行时,更新get_history_data_end_time并调用store_non_realtime_SCADA_data_to_influxdb函数
:return: None
"""
# 获取当前时间
current_time = datetime.now()
# 只在0点、6点、12点、18点执行任务
# if current_time.hour % 6 == 0 and current_time.minute == 0:
if current_time.minute % 10 == 0:
logger.info(f"{current_time.strftime('%Y-%m-%d %H:%M:%S')} -- Start store non realtime SCADA data task.")
# 获取下一个6小时的时间点,并更新get_history_data_end_time
get_history_data_end_time: str = get_next_time() # get_history_data_end_time 类型为 str,格式为'2025-02-06T12:00:00+08:00'
# print(get_next_time)
# 调用函数执行任务
influxdb_api.store_non_realtime_SCADA_data_to_influxdb(get_history_data_end_time)
logger.info('{} -- Successfully store non realtime SCADA data.'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
else:
logger.info(f"{current_time.strftime('%Y-%m-%d %H:%M:%S')} -- Skipping store non realtime SCADA data task.")
# 2025/02/06
def store_non_realtime_SCADA_data_task() -> None:
"""
定时执行6小时的任务,使用schedule库每分钟执行一次store_non_realtime_SCADA_data_job函数。
该任务会一直运行,定期调用store_non_realtime_SCADA_data_job获取SCADA数据。
:return:
"""
# 等待到整分对齐
now = datetime.now()
wait_seconds = 60 - now.second
time.sleep(wait_seconds)
try:
# 每分钟检查一次,执行store_non_realtime_SCADA_data_job
schedule.every(1).minute.at(":00").do(store_non_realtime_SCADA_data_job)
# 持续执行任务,检查是否有待执行的任务
while True:
schedule.run_pending() # 执行所有待处理的定时任务
time.sleep(1) # 暂停1秒,避免过于频繁的任务检查
pass
except Exception as e:
logger.error(f"Error occurred in store_non_realtime_SCADA_data_task: {e}")
if __name__ == "__main__":
url = influxdb_info.url
token = influxdb_info.token
org_name = influxdb_info.org
client = InfluxDBClient(url=url, token=token)
# step2: 先查询pg数据库中scada_info的信息,然后存储SCADA数据到SCADA_data这个bucket里
influxdb_api.query_pg_scada_info_non_realtime(project_info.name)
# 自动执行
store_non_realtime_SCADA_data_task()
+1
View File
@@ -0,0 +1 @@
python build_pyd.py build
+25
View File
@@ -0,0 +1,25 @@
from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize([
"main.py",
"auto_realtime.py",
"auto_store_non_realtime_SCADA_data.py",
"tjnetwork.py",
"online_Analysis.py",
"sensitivity.py",
"run_simlation.py",
"run_simulation.py",
"get_hist_data.py",
"get_realValue.py",
"get_data.py",
"get_current_total_Q.py",
"get_current_status.py",
"influxdb_api.py",
"influxdb_query_SCADA_data.py",
"sensor_placement.py",
"simulation.py",
"time_api.py",
"api/*.py",
"epanet/*.py"
]))
+6
View File
@@ -0,0 +1,6 @@
from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize([
"api/project.py"
]))
+5
View File
@@ -0,0 +1,5 @@
from tjnetwork import *
if __name__ == '__main__':
clean_project()
delete_project('project')
+22
View File
@@ -0,0 +1,22 @@
import sys
from tjnetwork import *
def main():
argc = len(sys.argv)
if argc < 2 or argc > 4:
print("copy_project source [count]")
return
source = sys.argv[1]
if not have_project(source):
print(f"{source} is not available")
if argc == 2:
copy_project(source, f"{source}_1")
elif argc == 3:
count = int(sys.argv[2])
for i in range(1, 1 + count):
copy_project(source, f"{source}_{i}")
if __name__ == '__main__':
main()
+13
View File
@@ -0,0 +1,13 @@
import sys
from tjnetwork import *
def main():
if len(sys.argv) != 2:
print("create_project which_inp")
return
inp = sys.argv[1]
read_inp(inp, f'./inp/{inp}.inp', '2')
if __name__ == '__main__':
main()
+13
View File
@@ -0,0 +1,13 @@
import sys
from tjnetwork import *
def main():
if len(sys.argv) != 2:
print("create_project which_inp")
return
inp = sys.argv[1]
read_inp(inp, f'./inp/{inp}.inp', '3')
if __name__ == '__main__':
main()
+136
View File
@@ -0,0 +1,136 @@
import psycopg as pg
sql_create = [
"script/sql/create/0.base.sql",
"script/sql/create/1.title.sql",
"script/sql/create/2.junctions.sql",
"script/sql/create/3.reservoirs.sql",
"script/sql/create/4.tanks.sql",
"script/sql/create/5.pipes.sql",
"script/sql/create/6.pumps.sql",
"script/sql/create/7.valves.sql",
"script/sql/create/8.tags.sql",
"script/sql/create/9.demands.sql",
"script/sql/create/10.status.sql",
"script/sql/create/11.patterns.sql",
"script/sql/create/12.curves.sql",
"script/sql/create/13.controls.sql",
"script/sql/create/14.rules.sql",
"script/sql/create/15.energy.sql",
"script/sql/create/16.emitters.sql",
"script/sql/create/17.quality.sql",
"script/sql/create/18.sources.sql",
"script/sql/create/19.reactions.sql",
"script/sql/create/20.mixing.sql",
"script/sql/create/21.times.sql",
"script/sql/create/22.report.sql",
"script/sql/create/23.options.sql",
"script/sql/create/24.coordinates.sql",
"script/sql/create/25.vertices.sql",
"script/sql/create/26.labels.sql",
"script/sql/create/27.backdrop.sql",
"script/sql/create/28.end.sql",
"script/sql/create/29.scada_device.sql",
"script/sql/create/30.scada_device_data.sql",
"script/sql/create/31.scada_element.sql",
"script/sql/create/32.region.sql",
"script/sql/create/33.dma.sql",
"script/sql/create/34.sa.sql",
"script/sql/create/35.vd.sql",
"script/sql/create/36.wda.sql",
"script/sql/create/37.history_patterns_flows.sql",
"script/sql/create/38.scada_info.sql",
"script/sql/create/39.users.sql",
"script/sql/create/40.scheme_list.sql",
"script/sql/create/41.pipe_risk_probability.sql",
"script/sql/create/42.sensor_placement.sql",
"script/sql/create/43.burst_locate_result.sql",
"script/sql/create/extension_data.sql",
"script/sql/create/operation.sql"
]
sql_drop = [
"script/sql/drop/operation.sql",
"script/sql/drop/extension_data.sql",
"script/sql/drop/43.burst_locate_result.sql",
"script/sql/drop/42.sensor_placement.sql",
"script/sql/drop/41.pipe_risk_probability.sql",
"script/sql/drop/40.scheme_list.sql",
"script/sql/drop/39.users.sql",
"script/sql/drop/38.scada_info.sql",
"script/sql/drop/37.history_patterns_flows.sql",
"script/sql/drop/36.wda.sql",
"script/sql/drop/35.vd.sql",
"script/sql/drop/34.sa.sql",
"script/sql/drop/33.dma.sql",
"script/sql/drop/32.region.sql",
"script/sql/drop/31.scada_element.sql",
"script/sql/drop/30.scada_device_data.sql",
"script/sql/drop/29.scada_device.sql",
"script/sql/drop/28.end.sql",
"script/sql/drop/27.backdrop.sql",
"script/sql/drop/26.labels.sql",
"script/sql/drop/25.vertices.sql",
"script/sql/drop/24.coordinates.sql",
"script/sql/drop/23.options.sql",
"script/sql/drop/22.report.sql",
"script/sql/drop/21.times.sql",
"script/sql/drop/20.mixing.sql",
"script/sql/drop/19.reactions.sql",
"script/sql/drop/18.sources.sql",
"script/sql/drop/17.quality.sql",
"script/sql/drop/16.emitters.sql",
"script/sql/drop/15.energy.sql",
"script/sql/drop/14.rules.sql",
"script/sql/drop/13.controls.sql",
"script/sql/drop/12.curves.sql",
"script/sql/drop/11.patterns.sql",
"script/sql/drop/10.status.sql",
"script/sql/drop/9.demands.sql",
"script/sql/drop/8.tags.sql",
"script/sql/drop/7.valves.sql",
"script/sql/drop/6.pumps.sql",
"script/sql/drop/5.pipes.sql",
"script/sql/drop/4.tanks.sql",
"script/sql/drop/3.reservoirs.sql",
"script/sql/drop/2.junctions.sql",
"script/sql/drop/1.title.sql",
"script/sql/drop/0.base.sql"
]
def create_template():
with pg.connect(conninfo="dbname=postgres host=127.0.0.1", autocommit=True) as conn:
with conn.cursor() as cur:
cur.execute("create database project")
with pg.connect(conninfo="dbname=project host=127.0.0.1") as conn:
with conn.cursor() as cur:
cur.execute('create extension postgis cascade')
cur.execute('create extension pgrouting cascade')
for sql in sql_create:
with open(sql, "r", encoding="utf-8") as f:
cur.execute(f.read())
print(f'executed {sql}')
conn.commit()
def have_template():
with pg.connect(conninfo="dbname=postgres host=127.0.0.1", autocommit=True) as conn:
with conn.cursor() as cur:
cur.execute("select * from pg_database where datname = 'project'")
return cur.rowcount > 0
def delete_template():
with pg.connect(conninfo="dbname=project host=127.0.0.1") as conn:
with conn.cursor() as cur:
for sql in sql_drop:
with open(sql, "r", encoding="utf-8") as f:
cur.execute(f.read())
print(f'executed {sql}')
conn.commit()
with pg.connect(conninfo="dbname=postgres host=127.0.0.1", autocommit=True) as conn:
with conn.cursor() as cur:
cur.execute("drop database project")
if __name__ == "__main__":
if (have_template()):
delete_template()
create_template()
View File
+12
View File
@@ -0,0 +1,12 @@
import sys
from tjnetwork import *
def main():
if len(sys.argv) != 2:
print("delete_project name")
return
delete_project(sys.argv[1])
if __name__ == '__main__':
main()

Some files were not shown because too many files have changed in this diff Show More