利用jni输出swmm的部分out文件内容

SWMM(storm water management model,暴雨洪水管理模型)是一个动态的降水-径流模拟模型,主要用于模拟城市某一单一降水事件或长期的水量和水质模拟。

对于这个swmm我只需要了解这么多,从自己业务角度去理解,swmm接受一些统计数据,然后经过一些分析处理,生成一些输出文件,这些可以通过dos命令来办到(凭印象打的,可能有所出入):

swmm5 inputfile rptfile outfile

生成的rptfile是一个txt文本,里面的数据非专业人士看不懂,也不想去了解,重点是outfile,它是一个二进制文件,目前的任务就是看看里面到底存放了什么东西,网上看了下这方面相关的东西, 在 https://www.openswmm.org/Topic/4471/reading-output-from-swmm5 发现有一个py程序可以读取它,但我并不想折腾py,太不了解了,那个页面同时提到了有个 http://epa.gov/nrmrl/wswrd/wq/models/swmm/swmm5_iface.zip 提供了c程序来读取(居然是美国环保局…),但是那个地址已经不存在了,在官网搜了一下之后发现下载地址移到了 https://www.epa.gov/sites/production/files/2016-09/swmm5_iface.zip

下载下来打开发现结构如下,

QQ截图20170424224320.png

其中,test.c是一个读取文件的demo,swmm5_iface.c封装了一些基本的操作方法,包括上面dos命令转化,其他文件就看不懂了。

下载vc++跑一边(对c完全是一窍不通,只能边学边查边跑了),这里有两个需要注意的地方,第一个是需要关联swmm5.lib,在下面输入框中输入lib的绝对路径即可:

QQ截图20170424225126.png

第二个是需要将swmm5.dll拷贝到test.c的目录下,执行之后打印了一串数字,虽然看不懂,但至少可以从文件中读取内容了。

跑通了c之后接来下就是利用jni来打印了,步骤如下

找个记事本,建立一个名为ReadSwmmOutput的java文件,内容入如下:

class ReadSwmmOutput {
	/**
	 * 读取out 文件,
	 * 
	 * @param location
	 *            文件绝对路径
	 * @return 0 如果读取成功,其他读取失败
	 */
	public native int read(String location);

	/**
	 * 关闭读取的out文件
	 */
	public native void close();

	/**
	 * 打印部分内容
	 */
	public native void print();

	/**
	 * @param in
	 * @param rpt
	 * @param out
	 * @return
	 */
	public native int convert(String in, String rpt, String out);

	public static void main(String[] args) {
		ReadSwmmOutput output = new ReadSwmmOutput();
		// int status = output.convert("C:/Users/mhlx/Desktop/test/input/1.inp",
		// "H:/2.rpt", "H:/2.out");
		// System.out.println(status);
		output.read("H:/2.out");
		try {
			output.print();
		} finally {
			output.close();
		}
	}

	static {
		System.loadLibrary("Swmm5");
		System.loadLibrary("HelloWorld");
	}
}

然后利用javac编译 javac ReadSwmmOutput.java 然后利用javah生成头文件 javah -jni ReadSwmmOutput

生成的头文件如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class ReadSwmmOutput */

#ifndef _Included_ReadSwmmOutput
#define _Included_ReadSwmmOutput
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     ReadSwmmOutput
 * Method:    read
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_ReadSwmmOutput_read
  (JNIEnv *, jobject, jstring);

/*
 * Class:     ReadSwmmOutput
 * Method:    print
 */
JNIEXPORT void JNICALL Java_ReadSwmmOutput_print
  (JNIEnv *, jobject);

/*
 * Class:     ReadSwmmOutput
 * Method:    close
 */
JNIEXPORT void JNICALL Java_ReadSwmmOutput_close
  (JNIEnv *, jobject);

JNIEXPORT jint JNICALL Java_ReadSwmmOutput_convert
  (JNIEnv *, jobject, jstring,jstring,jstring);

#ifdef __cplusplus
}
#endif
#endif

有了头文件之后就是编写c文件,说来搞笑,当初就是因为搞不定c去学的java。。。,七拼八凑的c文件如下:

#include <stdio.h>
#include "swmm5_iface.h"
#include "ReadSwmmOutput.h"

JNIEXPORT jint JNICALL Java_ReadSwmmOutput_read
(JNIEnv *env, jobject obj, jstring string){
char* str = (*env)->GetStringUTFChars(env, string, 0);
int i =  OpenSwmmOutFile(str);
 (*env)->ReleaseStringUTFChars(env, string, 0);
return i;
}

JNIEXPORT void JNICALL Java_ReadSwmmOutput_print
(JNIEnv *env, jobject obj){
	int i;
	float x,y,z;
	    printf("\nTime       Total     Total     Total");
         printf("\nPeriod  Rainfall    Runoff   Outflow");
         printf("\n====================================");
         for (i=1; i<=SWMM_Nperiods; i++)
         {
             GetSwmmResult(3, 0, 1, i, &x);
             GetSwmmResult(3, 0, 4, i, &y);
             GetSwmmResult(3, 0, 11, i, &z);
             printf("\n%6d  %8.2f  %8.2f  %8.2f", i, x, y, z);
         }
         printf("\n");
}

JNIEXPORT void JNICALL Java_ReadSwmmOutput_close
(JNIEnv *env, jobject obj) {

     CloseSwmmOutFile();
}

JNIEXPORT jint JNICALL Java_ReadSwmmOutput_convert
(JNIEnv *env, jobject obj, jstring in,jstring rpt,jstring out){

char* inStr = (*env)->GetStringUTFChars(env, in, 0);
char* rptStr = (*env)->GetStringUTFChars(env, rpt, 0);
char* outStr = (*env)->GetStringUTFChars(env, out, 0);
int status =  RunSwmmDll(inStr,rptStr,outStr);

 (*env)->ReleaseStringUTFChars(env, in, 0);
 (*env)->ReleaseStringUTFChars(env, rpt, 0);
 (*env)->ReleaseStringUTFChars(env, out, 0);
return status;
}

c需要手动释放内存,我不了解jni一些方法,这里仅仅为了得到结果,忽视过程

此时编译是无法通过的,因为没有将jni.h文件放入include文件夹,将jni.h、jawt_md.h和jni_md.h三个文件放入include文件夹之后即可编译,最后得到一个dll文件。

得到dll文件后需要设置library path,eclipse中可以这样操作:

QQ截图20170424230853.png

双击Native Library Location设置dll所在目录即可。

还有一点需要注意,由于swmm是32位dll,所以需要用32位的jdk。

最后附上一个自己做的demo: jni读取out文件内容,需32位jdk.zip