Building the Application in Visual Basic
In this section, we will walk you through the steps involved in building the Visual Basic project. If you would rather skip the details involved in constructing this project, you can find the completed project in the \LINGO17\Programming Samples\VBasic\Staff1 folder.
You can build the project as follows:
1. | Start Visual Basic and then issue the File|NewProject command. |
2. | Select Standard Exe and click the OK button. |
3. | Use the resource editor to format the project's form until it resembles the following: |
Now, add handler code for the two buttons in the form. The Exit button is easy. All the Exit button does when the user clicks it is exit the application. So, double click on the Exit button and enter the following code:
Private Sub Exit_Click()
End
End Sub
A little more work will be required to setup the Solve button. When the Solve button is pressed, the application will retrieve the staffing requirements from the form, pass them along with the model to the LINGO script processor (LGVBSCRIPT) to solve the model, retrieve the solution, and post the solution in the form.
First, we must declare the external LINGO function. Do this by adding the \LINGO17\Programming Samples\LINGD17.BAS module to the project using the Project|Add Module command in VB. This module contains the definitions of all the exported function in the LINGO DLL, and makes them available to our project.
Now, add the handler code for the Solve button. Go to the form, double click on the Solve button, and enter the following code:
Private Sub Solve_Click()
' Calls the LINGO DLL to solve the staffing
' model in STAFFPTR.LNG. Staffing
' requirements are taken from the dialog
' box.
' Get the staffing needs from the dialog box
Dim dNeeds(7) As Double
For i = 1 To 7
dNeeds(i) = Needs(i - 1).Text
Next i
' Create the LINGO environment object
Dim pLINGO As Long
pLINGO = LScreateEnvLng()
If pLINGO = 0 Then
MsgBox ("Unable to create LINGO Environment.")
GoTo FinalExit
End If
' Open LINGO's log file
Dim nError As Long
nError = LSopenLogFileLng(pLINGO, "LINGO.log")
If nError <> 0 Then GoTo ErrorExit
' Pass memory transfer pointers to LINGO
Dim dStart(7) As Double, dOnDuty(7) As Double
Dim dTotal As Double, dStatus As Double
' @POINTER(1)
nError = LSsetPointerLng(pLINGO, dNeeds(1), nPointersNow)
If nError <> 0 Then GoTo ErrorExit
' @POINTER(2)
nError = LSsetPointerLng(pLINGO, dStart(1), nPointersNow)
If nError <> 0 Then GoTo ErrorExit
' @POINTER(3)
nError = LSsetPointerLng(pLINGO, dOnDuty(1), nPointersNow)
If nError <> 0 Then GoTo ErrorExit
' @POINTER(4)
nError = LSsetPointerLng(pLINGO, dTotal, nPointersNow)
If nError <> 0 Then GoTo ErrorExit
' @POINTER(5)
nError = LSsetPointerLng(pLINGO, dStatus, nPointersNow)
If nError <> 0 Then GoTo ErrorExit
' Build LINGO's command script (commands
' are terminated with an ASCII 10
Dim cScript As String
' Causes LINGO to echo input
cScript = "SET ECHOIN 1" & Chr(10)
' Read in the model file
cScript = cScript & _
"TAKE \LINGO17\SAMPLES\STAFFPTR.LNG" & Chr(10)
' Solve the model
cScript = cScript & "GO" & Chr(10)
' Quit LINGO DLL
cScript = cScript & "QUIT" & Chr(10)
' Mark end of script with a null byte
cScript = cScript & Chr(0)
' Run the script
dStatus = -1#
nError = LSexecuteScriptLng(pLINGO, cScript)
' Close the log file
LScloseLogFileLng (pLINGO)
' Problems?
If nError > 0 Or dStatus <> 0 Then
MsgBox ("Unable to solve!")
GoTo ErrorExit
End If
' Place Start values in dialog box
For i = 1 To 7
Start(i - 1).Caption = dStart(i)
Next i
' Place On Duty values in dialog box
For i = 1 To 7
OnDuty(i - 1).Caption = dOnDuty(i)
Next i
' Put Total staffing in dialog box
Total.Caption = dTotal
LSdeleteEnvLng (pLINGO)
GoTo FinalExit:
ErrorExit:
MsgBox ("LINGO Error Code: " & nError&)
LSdeleteEnvLng (pLINGO)
FinalExit:
End Sub
The first section of the Solve_Click procedure is straightforward and deals with extracting the user’s staffing requirements from the dialog box. Note that the data is stored in a double precision array, rather than as integers. This is because these values will be passed to LINGO, which only passes numeric values in double precision format.
The first call to LINGO creates the LINGO environment object with the following code:
' Create the LINGO environment object
Dim pLINGO As Long
pLINGO = LScreateEnvLng()
If pLINGO = 0 Then
MsgBox ("Unable to create LINGO Environment.")
GoTo FinalExit
End If
Next, a log file for LINGO is established with the call:
' Open LINGO's log file
Dim nError As Long
nError = LSopenLogFileLng(pLINGO, "LINGO.log")
If nError <> 0 Then GoTo ErrorExit
As mentioned above, opening a log file for LINGO is good practice, at least when you’re debugging the application. If something should go wrong, the log file will generally contain a helpful clue.
The next step is to pass LINGO the physical addresses it will use to resolve the @POINTER() function references used in the data section of the model (refer to the The Model section for more details). This is done as follows:
' Pass memory transfer pointers to LINGO
Dim dStart(7) As Double, dOnDuty(7) As Double
Dim dTotal As Double, dStatus As Double
' @POINTER(1)
nError = LSsetPointerLng(pLINGO, dNeeds(1), nPointersNow)
If nError <> 0 Then GoTo ErrorExit
' @POINTER(2)
nError = LSsetPointerLng(pLINGO, dStart(1), nPointersNow)
If nError <> 0 Then GoTo ErrorExit
' @POINTER(3)
nError = LSsetPointerLng(pLINGO, dOnDuty(1), nPointersNow)
If nError <> 0 Then GoTo ErrorExit
' @POINTER(4)
nError = LSsetPointerLng(pLINGO, dTotal, nPointersNow)
If nError <> 0 Then GoTo ErrorExit
' @POINTER(5)
nError = LSsetPointerLng(pLINGO, dStatus, nPointersNow)
If nError <> 0 Then GoTo ErrorExit
In summary, when LINGO is called to solve the model, the staffing needs are passed to LINGO in the dNeeds array via the @POINTER( 1) reference. Solution information is passed from LINGO back to the application in the dStart, dOnDuty, dTotal, and dStatus structures via @POINTER() references 2 through 5, respectively. If any of this is unfamiliar, review The @POINTER() Function section.
Next, the following code is used to build the command script:
' Build LINGO's command script (commands
' are terminated with an ASCII 10
Dim cScript As String
' Causes LINGO to echo input
cScript = "SET ECHOIN 1" & Chr(10)
' Read in the model file
cScript = cScript & _
"TAKE \LINGO17\SAMPLES\STAFFPTR.LNG" & Chr(10)
' Solve the model
cScript = cScript & "GO" & Chr(10)
' Quit LINGO DLL
cScript = cScript & "QUIT" & Chr(10)
' Mark end of script with a null byte
cScript = cScript & Chr(0)
The script consists of four commands, which are each terminated with a new line character (ASCII 10). The end of the script is terminated with a NULL character (ASCII 0). These commands and their functions are:
Command |
Function |
SET ECHOIN 1 |
Causes LINGO to echo input to the log file. This is a useful feature while building and debugging an application. |
TAKE |
Loads the model from a disk file. The TAKE command may be used to load model files, as in this example. It may also be used to run nested command scripts contained in files. |
GO |
Calls the solver to optimize the model. |
QUIT |
Closes down LINGO’s script processor and returns control to the calling application. |
At this point, the script is ready to be passed off to LINGO for processing with the following call:
' Run the script
dStatus = -1#
nError = LSexecuteScriptLng(pLINGO, cScript)
Note that dStatus is initialized to –1. LINGO returns the model status through memory transfer location number 5 (i.e., @POINTER( 5)) to the dStatus variable. LINGO will only return a status value if it was able to solve the model. If an unexpected error were to occur, LINGO might not ever reach the solution phase. In that case, dStatus would never be set. Initializing dStatus to a negative value tests for this situation. Given that LINGO returns only non-negative status codes, a negative status code would indicate a problem. This method of error trapping is effective, but not particularly elegant. Another method that involves specifying an error callback routine is demonstrated below.
Now, LINGO’s log file may be closed down by calling LScloseLogFileLng():
' Close the log file
LScloseLogFileLng (pLINGO)
Next, the following code tests to see if LINGO was able to find an optimal solution:
' Problems?
If nError > 0 Or dStatus <> 0 Then
MsgBox ("Unable to solve!")
GoTo ErrorExit
End If
Note that the code returned in nError pertains only to the mechanical execution of the script processor. It has nothing to do with the status of the model’s solution, which is returned in dStatus via the use of the @POINTER() and @STATUS() functions (see The Model section). A model may actually be infeasible or unbounded, and the error code returned by LSexecuteScriptLng()will give no indication. Thus, it is important to add a mechanism to return a solution’s status to the calling application, as done here with the @STATUS() -> @POINTER(5) -> dStatus link. The end result in the sample code is that "Unable to solve" is printed if either error condition occurs. A more user-friendly application would offer more specific information regarding the error condition.
As a final step, in order to avoid a memory leak in the application, remember to free up LINGO’s environment when through:
LSdeleteEnvLng (pLINGO)
GoTo FinalExit:
If everything has been entered correctly, you should now be able to run the project.