java读取SWMM模型out文件完整版
最近由于公司项目业务涉及相关swmm模型相关问题,具体就是将swmm模拟出的二进制out文件中相关数据读取出来,对于从来没有听说过swmm的我来说,又开始了面向某论坛编程。在浏览了好多帖子的时候最终找到一篇对自己帮助很大的帖子(https://blog.youkuaiyun.com/yangshengquan95/article/details/92656044)这篇其实很详细的说明了java使用jna通过ddl来读取out文件,我这儿主要分享一下在实际完整操作过程中的坑以及解决办法,同时记录自己在一次涉及陌生的业务模型的心得。
1.先说一波错误压压惊吧
① java.lang.UnsatisfiedLinkError: %1 不是有效的 Win32 应用程序
32位的dll,jdk是64位是报错,将64位jdk 换成 32位jdk后不再报该错误;我使的是idea 安装32位jdk直接SDKs一指定就OK了
② Exception in thread “main” java.lang.UnsatisfiedLinkError: Unable to load library ‘TEST_API1’: Native library ()
这个错比较宽泛,最不起眼的就是jdk安装路径是不是有中文或者非法字符(我就遇见了,之前就有中文) 动态库名字错误或者存放位置不对
网上推荐放在jdk的bin目录下,我这样直接放在静态资源resources也能加载
③ 在读取out文件数据的时候,java程序和swmm模拟软件结果不一致的情况(部分节点的结果会不一致)
一开始我以为动态链接库有问题了呢,最后查阅资料发现他这个swmm的模拟软件分中英文版本,一定要以英文版本的swmm软件为准,英文版我都对上了。
④ Exception in thread “main” java.lang.UnsatisfiedLinkError: Error looking up function ‘TEST_OpenService1’: 找不到指定的程序。
在java中定义的接口中的方法名和动态库中的函数名不一致导致,这个错细心一点就ok了(一般这个我都是cv操作的😁)
2.再说一下结果处理方面的问题吧
① 时间问题处理
/**
* 模拟开始日期 相对于12/31/1899年子夜12点的天数
* @return
*/
public double getSWMM_StartDate();
先说说我这边的实际情况吧 返回的是相对于相对于12/31/1899年子夜12点的天数,那么小数部分就是时分秒,这的模拟开始时间刚好是8:00,那么换算为天就是1/3天,这时候我们就不能直接上去算,需要使用java的精确计算类BigDecimal(),经过一系列操作,终于划算出了预期的时间。
/**
* 43939.333333333336
* 1900-01-01 到 模拟时间 的天数
*
* @param doubleDate 模拟开始日期
* @return
*/
public static String getStringRealDate(double doubleDate) {
/* 用doubleDate的整数数部分计算年月日 */
Calendar instance = Calendar.getInstance();
// 参考时间为1900-1-1 0:0:0 从1899年12月31日子夜开始 Calendar类月份从零开始计算
instance.set(1900, 0, 1, 0, 0, 0);
instance.add(Calendar.DAY_OF_YEAR, ((int) doubleDate) - 2);
String YMDDate = instance.get(Calendar.YEAR)
+ "-" + (instance.get(Calendar.MARCH) + 1)
+ "-" + instance.get(Calendar.DAY_OF_MONTH);
/* 用doubleDate的小数部分计算时分秒 精确计算 */
SimpleDateFormat simpl = new SimpleDateFormat(" HH:mm:ss");
long startTime = 0;
try {
startTime = simpl.parse(" 0:0:0").getTime();
} catch (ParseException e) {
System.err.println("时分秒转换异常 ==> " +e.getMessage());
}
BigDecimal swmm_bigDecimal = new BigDecimal(doubleDate);
BigDecimal startMMTime = swmm_bigDecimal.multiply(new BigDecimal(24 * 3600 * 1000));
BigDecimal bigDecimal = new BigDecimal(startTime);
String HMSDate = simpl.format(bigDecimal.longValue() + startMMTime.longValue());
String realDate = YMDDate + HMSDate;
return realDate;
}
inp文件与读取的结果对应一下下,好像是一致的,有会更好的方法,给我私信一下(谢谢)
模拟报告时间步长单位是秒 这儿就是四分钟
② 对于结果数据参数问题我再说一下吧
其实官方有一个文档的对参数说的很详细的 函数调用说明.chm。(下载SWHH软件应该有的,也可以找我我给你)
服务接口里面的计算结果值的GetSwmmResult() 这个是读取的核心,他的参数决定了取的是哪一个文案的数据值一下我就好好说一下参数问题吧
/**
* 计算结果值
* @param iType 0:SUBCATCH 1:NODE(节点) 2:link(管段) 3:系统参数
* @param iIndex 当前节点(|管段)下标
* @param vIndex 节点(管段)对应属性代码
* @param period 当前时段索引
* @return
*/
public float GetSwmmResult(int iType, int iIndex, int vIndex, int period);
@param iType: 它的选项可以为
-
0: 子汇水面积;
-
1: 节点;
-
2: 管段
-
3: 系统
@param iIndex:
当前节点(管段|子汇水面积|)下标 注意:系统参数可以忽略
遍历的时候就是子汇水面积总数(节点总数|管段总数)
官方文档解释:
itemIndex 参数确定了已知类的项,通过从0开始的连续数字报告,以出现在SWMMH 5输入文件项的相同次序赋值。例如,假设系统包含了5的连接节点,ID名称分别为J1, J2, J3, J4和J5被列出,以工程输入文件相同的次序,但是结果仅仅征询铰点J2和J4。那么 itemIndex o为0时,将检索到J2的结果;同时itemIndex 为 1是将检索到J4的结果。itemIndex 的任何其他数值是非法的。对于系统项, 忽略itemIndex 参数。
说的挺长的,其实挺清楚的,不知道为啥刚接触我没理解,所以我以我的理解说一下:
这个参数的取值要依据*.inp文件进行关联,从0开始的连续数字报告,以出现在SWMMH 5输入文件项的相同次序赋值,每一个 iType里面对应了好多二级类型,他们的顺序是定义好的,读完才读取下一个直到总数读完。
二级类型(拿节点管段来说吧)
节点二级类型代码为:
0 = 铰点(汇节点)
1 = 排放口
2 = 蓄水
3 = 分流器
管段二级类型代码为:
0 = 管渠
1 = 水泵
2 = 孔口
3 = 堰
4 = 出水口
这个二级类型代码只是一个顺序而言,而他们各自的个数根据实际的*inp文件解读。
@param vIndex : 这是变量 (文案的代码)
这个参数的选择取决于被搜索的对象类,列于附录B的输出文件 -报告变量内。
报告变量在那个官方文档里面,我还是贴出来吧
这边我关注了节点和管段 验证都没有问题 其他的应该也没啥问题(有问题再说啊😋)
@param period : 当前时段的索引
getSWMM_Nperiods() 遍历就完事了,但是注意了 从1开始
3.说说这次调研总结总结吧
这边我关注了节点和管段 验证都没有问题 其他的应该也没啥问题(有问题再说啊😋)
@param period : 当前时段的索引
getSWMM_Nperiods() 遍历就完事了,但是注意了 从1开始
3.说说这次调研总结总结吧
刚开始接触SWMM的时候,一头雾水,咨询水专业的相关老铁们,面向某度、某论坛,但是总算小有结果吧,查阅资料过程中说pyswmm也能操作,以后一定要搞一下(毕竟我也是一个某蛇的爱好者),第一次发这个博客,各位多多指教啊!