最近有一些朋友问我关于java程序调用lingo的问题,为了方便大家了解一些过程,下面就将java调用的详细过程重新写一下。在介绍java调用前首先介绍下,他们之间调用的原理。
原理:一个完整的java-lingo程序,必须包含三个要素,java程序-model文件-lingo软件,这三这个缺一不可,并且model文件与java程序是相互对应的也就是说如果你修改了其中任何一个文件其他的文件也许要做相应的修改,明确了这点后再来说明调用过程,首先java调用任何其他非java程序肯定是通过JNI将模型所需的数据传递给lingo模型文件,而后Lingo程序再去求解模型,最后再将求解后的结果返回。至于JNI的原理以及相关知识这里不做介绍,有时间的朋友可以搜一下相关知识。这里主要介绍的是Lingo部分,为了方便介绍这里举个例子:
MODEL:
SETS:
DAYS/ MON TUE WED THU FRI SAT SUN/:
NEEDS,START,ONDUTY;
ENDSETS
[OBJECTIVE]MIN = @SUM(DAYS(I):START(I));
@FOR(DAYS(TODAY):
!Calculate number on duty;
ONDUTY(TODAY)=
@SUM(DAYS(D)|D #LE# 5:
START(@WRAP(TODAY- D + 1,
@SIZE(DAYS))));
!Enforce staffing requirement;
ONDUTY(TODAY)>= NEEDS(TODAY);
@GIN(START);
);
DATA:
NEEDS= @POINTER(1);
@POINTER(2)= START;
@POINTER(3)= ONDUTY;
@POINTER(4)= OBJECTIVE;
@POINTER(5)= @STATUS();
ENDDATA
END
这是lng模型文件,文件的model部分不多做介绍,这里主要介绍的是DATA部分,学过Lingo的朋友应该知道,Lingo模型的DATA部分,是模型的数据部分,用于初始模型中某些变量的值,DATA中NEEDS的左边写着@POINTER(1)而@POINTER(2)却写着START,这表示什么意思呢,原来NEEDS=@POINTER(1)表示,NEEDS这个变量的值是从外界获取,@PIONT(2)=START这句话的意思是告诉外界@POINTER(2)这个地方的值获取的是START的值,其他同理。说完这句后相信知道一点指针的朋友就应该知道,@POINT()这个函数表示的是一个内存的指针,并且这个指针被lingo和外界共同保留(这里指的是java程序),外界与lingo的交互正是通过这些指针进行的,实际上就是变量内存共享。说道这里还有一个疑问,那就是外界是怎样知道,当前指针所对应的变量呢,这就要看你的程序执行顺序了,在java调用lingo时,@poiter(i)这个指针的开辟是在执行lng.LSsetPointerLng这个函数后,当第一次执行lng.LSsetPointerLng时,则对应着@POINTER(1),第二次对应着@PINTER(2),依次类推,第I次执行,则对应这@POINTER(I).因此如果在lingo模型文件中某个变量对应着@ponter(k),则对于他的值赋予在java代码中就必须在前面已经执行了k-1次lng.LSsetPointerLng.至此原理已经介绍完毕,下面就来就一个值日安排的问题来给出具体的例子.
Lingo的安装:这里并不是要说明如何下载与安装lingo,而是说明安装完毕后所需要另外做的几件事情。
1.在Linux系统下,在命令终端中进入lingo安装目录下的bin/linux32目录,而后执行
sudo./lingovars.sh以及sudo./symlinks.sh在执行完此命令后,还必须将bin/linux32这个目录加入LD_LIBRARY_PATH环境变量中。
2.在windows中则必须将lingo.exe所在目录加入PATH环境变量中,这个设置可以通过,我的电脑--》属性-》高级-》环境变量-》Path中添加得到。
如果不这么做在运行java程序时就会报加载库文件异常。
Java代码的编写:在Eclipse中新建一个java工程,并且引入lingo安装文件下的Lingo11.jar(具体名称因版本而稍有不同)。再新建一个java文件代码如下:
package com.xuen.test;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import javax.swing.*;
import com.lindo.Lingd11;
public class Staff1 implements ActionListener
{
// Load the Lingo JNI interface
static {
System.loadLibrary( "Lingj11");
}
// Create a new Lingo object, which we will use to interface with Lingo
Lingd11 lng = new Lingd11();
// Stores the Lingo JNI environment pointer
Object pnLngEnv;
JFrame f;
JPanel p;
JTextField jtfNeeds[] = new JTextField[ 7];
JTextField jtfStart[] = new JTextField[ 7];
JTextField jtfOnDuty[] = new JTextField[ 7];
JTextField jtfTotal;
JButton jbtnSolve = new JButton("Solve");
JButton jbtnExit = new JButton("Exit");
int nLastIterationCount;
public Staff1()
{
// Create the frame and panel
f = new JFrame("Lingo staffing example using Java");
p = new JPanel(); // t l b r
p.setBorder( BorderFactory.createEmptyBorder( 30, 10, 30, 50));
p.setLayout( new GridLayout(10, 4, 40, 20));
// Add the widgets.
addWidgets();
// Add the panel to the frame.
f.getContentPane().add( p, BorderLayout.CENTER);
// Exit when the window is closed.
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Show the frame
f.pack();
f.setVisible(true);
// no solver iterations performed yet;
nLastIterationCount = -1;
}
private void addWidgets()
{
String sDays[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
JLabel jl[] = new JLabel[ 7];
// Row 1...the headers
JLabel jlHdrDay = new JLabel( "Day:", JLabel.RIGHT);
p.add( jlHdrDay);
JLabel jlHdrNeeds = new JLabel( "Needs:", JLabel.RIGHT);
p.add( jlHdrNeeds);
JLabel jlHdrStart = new JLabel( "Start:", JLabel.RIGHT);
p.add( jlHdrStart);
JLabel jlHdrOnDuty = new JLabel( "On Duty:", JLabel.RIGHT);
p.add( jlHdrOnDuty);
// Rows 2-8...days of the week
for ( int i = 0; i < 7; i++)
{
jl[ i] = new JLabel( sDays[ i], JLabel.RIGHT);
p.add( jl[ i]);
jtfNeeds[ i] = new JTextField( "0", 5);
jtfNeeds[ i].setHorizontalAlignment( JTextField.RIGHT);
p.add( jtfNeeds[ i]);
jtfStart[ i] = new JTextField( "0", JLabel.RIGHT);
jtfStart[ i].setEditable( false);
jtfStart[ i].setHorizontalAlignment( JTextField.RIGHT);
p.add( jtfStart[ i]);
jtfOnDuty[ i] = new JTextField( "0", JLabel.RIGHT);
jtfOnDuty[ i].setEditable( false);
jtfOnDuty[ i].setHorizontalAlignment( JTextField.RIGHT);
p.add( jtfOnDuty[ i]);
}
// Row 9...displays total of Start column
JLabel jlNull1 = new JLabel( " ", JLabel.RIGHT);
p.add( jlNull1);
JLabel jlTotal = new JLabel( "Total...", JLabel.RIGHT);
p.add( jlTotal);
jtfTotal = new JTextField( "0", JLabel.RIGHT);
jtfTotal.setEditable( false);
jtfTotal.setHorizontalAlignment( JTextField.RIGHT);
p.add( jtfTotal);
JLabel jlNull3 = new JLabel( " ", JLabel.RIGHT);
p.add( jlNull3);
// Row 10...Solve and Exit buttons
JLabel jlNull4 = new JLabel( " ", JLabel.RIGHT);
p.add( jlNull4);
JLabel jlNull5 = new JLabel( " ", JLabel.RIGHT);
p.add( jlNull5);
jbtnSolve.addActionListener( this);
p.add( jbtnSolve);
jbtnExit.addActionListener( this);
p.add( jbtnExit);
}
// Implementation of ActionListener interface.
public void actionPerformed( ActionEvent event)
{
Object source = event.getSource();
if ( source == jbtnSolve)
{
Solve();
} else {
System.exit( 0);
}
}
private static int MySolverCallback( Object pnLng, int iLoc, Object jobj)
{
Staff1 s = (Staff1) jobj;
int nIterations[] = new int [1];
s.lng.LSgetCallbackInfoIntLng( pnLng, Lingd11.LS_IINFO_ITERATIONS_LNG, nIterations);
if ( nIterations[0] != s.nLastIterationCount)
{
s.nLastIterationCount = nIterations[0];
System.out.println("In Java callback function...iterations = " + s.nLastIterationCount);
}
return( 0);
}
private static int MyErrorCallback( Object pnLng, int nErrorCode, String jsErrMessage, Object jobj)
{
System.out.println("Lingo error code: " + nErrorCode);
System.out.println("Lingo error message:\n\n " + jsErrMessage);
return( 0);
}
private void Solve()
{
int nErr;
Double d1=0.;
double dNeeds[] = new double[7];
double dStart[] = new double [7];
double dOnDuty[] = new double [7];
double dObj[] = new double [1];
double dStatus[] = new double [1];
System.out.println("\nSolving...");
// get the staffing requirements
for ( int i=0; i<7; i++)
{
String s = jtfNeeds[i].getText();
try {
dNeeds[ i] = d1.valueOf( s);
}
catch ( Exception e)
{
dNeeds[ i] = 0.;
}
}
// clear output fields
for ( int i=0; i<7; i++)
{
jtfStart[i].setText( " ");
jtfOnDuty[i].setText( " ");
}
jtfTotal.setText( " ");
// create the Lingo environment
pnLngEnv = lng.LScreateEnvLng();
if ( pnLngEnv == null)
{
System.out.println( "***Unable to create Lingo environment***");
return;
}
// open a log file
nErr = lng.LSopenLogFileLng( pnLngEnv, "lingo.log");
if ( nErr != lng.LSERR_NO_ERROR_LNG )
{
System.out.println( "***LSopenLogFileLng() error***: " + nErr);
return;
}
// pass lingo a pointer to the staffing needs
int[] nPointersNow = new int[1];
nErr = lng.LSsetPointerLng( pnLngEnv, dNeeds, nPointersNow);
if ( nErr != lng.LSERR_NO_ERROR_LNG )
{
System.out.println( "***LSsetPointerLng() error***: " + nErr);
return;
}
// pass pointers to output areas
nErr = lng.LSsetPointerLng( pnLngEnv, dStart, nPointersNow);
if ( nErr != lng.LSERR_NO_ERROR_LNG )
{
System.out.println( "***LSsetPointerLng() error***: " + nErr);
return;
}
nErr = lng.LSsetPointerLng( pnLngEnv, dOnDuty, nPointersNow);
if ( nErr != lng.LSERR_NO_ERROR_LNG )
{
System.out.println( "***LSsetPointerLng() error***: " + nErr);
return;
}
nErr = lng.LSsetPointerLng( pnLngEnv, dObj, nPointersNow);
if ( nErr != lng.LSERR_NO_ERROR_LNG )
{
System.out.println( "***LSsetPointerLng() error***: " + nErr);
return;
}
nErr = lng.LSsetPointerLng( pnLngEnv, dStatus, nPointersNow);
if ( nErr != lng.LSERR_NO_ERROR_LNG )
{
System.out.println( "***LSsetPointerLng() error***: " + nErr);
return;
}
// pass Lingo the name of the solver callback function...
nErr = lng.LSsetCallbackSolverLng( pnLngEnv, "MySolverCallback", this);
// ...and the error callback function
nErr = lng.LSsetCallbackErrorLng( pnLngEnv, "MyErrorCallback", this);
// construct the script
// echo input to log file
String sScript = "SET ECHOIN 1" + "\n";
// load the model from disk
File f=new File("Staffptr.lng");
sScript = sScript + "TAKE "+f.getAbsolutePath() + "\n";
// view the formulation
sScript = sScript + "LOOK ALL" + "\n";
// solve
sScript = sScript + "GO" + "\n";
// exit script processor
sScript = sScript + "QUIT" + "\n";
// run the script
dStatus[0] = -1;
nLastIterationCount = -1;
nErr = lng.LSexecuteScriptLng( pnLngEnv, sScript);
if ( nErr != lng.LSERR_NO_ERROR_LNG )
{
System.out.println( "***LSexecuteScriptLng error***: " + nErr);
return;
}
// clear the pointers to force update of local arrays
// ***NOTE*** solution won't get passed to local arrays until
// LSclearPointersLng is called!!!
nErr = lng.LSclearPointersLng( pnLngEnv);
if ( nErr != lng.LSERR_NO_ERROR_LNG )
{
System.out.println( "***LSclearPointerLng() error***: " + nErr);
return;
}
// check the solution status
if ( dStatus[0] != lng.LS_STATUS_GLOBAL_LNG)
System.out.println( "***Unable to Solve*** dStatus:" + dStatus[0]);
// close Lingo's log file
nErr = lng.LScloseLogFileLng( pnLngEnv);
if ( nErr != lng.LSERR_NO_ERROR_LNG )
{
System.out.println( "***LScloseLogFileLng() error***: " + nErr);
return;
}
// delete the Lingo environment
lng.LSdeleteEnvLng( pnLngEnv);
// post the solution
Integer nTotal = 0;
for ( int i=0; i<7; i++)
{
Double d = dStart[i];
Integer n = d.intValue();
jtfStart[i].setText( n.toString());
nTotal = nTotal + n;
d = dOnDuty[i];
n = d.intValue();
jtfOnDuty[i].setText( n.toString());
}
jtfTotal.setText( nTotal.toString());
//System.out.println( "free Java memory: " + Runtime.getRuntime().freeMemory());
}
// main method
public static void main(String[] args) {
Staff1 s = new Staff1();
}
}
编写完上述代码后还需编写Lingo模型文件,在工程文件夹下新建一个STAFFPTR.LNG的文本文件,并在其中写入如下内容:
MODEL:
SETS:
DAYS/ MON TUE WED THU FRI SAT SUN/:
NEEDS,START, ONDUTY;
ENDSETS
[OBJECTIVE]MIN = @SUM( DAYS( I): START( I));
@FOR(DAYS( TODAY):
!Calculate number on duty;
ONDUTY(TODAY) =
@SUM(DAYS( D)| D #LE# 5:
START(@WRAP( TODAY - D + 1, @SIZE( DAYS))));
!Enforce staffing requirement;
ONDUTY(TODAY) >= NEEDS( TODAY);
@GIN(START);
);
DATA:
NEEDS= @POINTER( 1);
@POINTER(2) = START;
@POINTER(3) = ONDUTY;
@POINTER(4) = OBJECTIVE;
@POINTER(5) = @STATUS();
ENDDATA
END
完成后右键运行即可,如果运行时报如下错误,
Exceptionin thread "main" java.lang.UnsatisfiedLinkError:
则说明你的安装过程有错(或者你是用的lingo精简版),重新执行上面所说的Lingo的安装过程,以及配置eclipse的runconfigurations的环境变量。
最后说明一点,其实这些知识lingo的帮助文件说明的很清楚,对于一项陌生的应用帮助文件是最好的老师。