ContentsIndexHome
PreviousUpNext
C# Callback example

The callback example in C#.

The LoggingCallback and the DebuggingCallback are both implementations of the ICallBack. 

The LoggingCallback logs the minor errors in stead of throwing them, the DebuggingCallback asserts false if an InvalidUse error is encountered. 

The ErrorCallBack run function makes some calls on the SDK which will cause errors, to show how the implementations of the ICallBack will react.

  1: using System;
  2: using System.Collections.Generic;
  3: using System.Text;
  4: using Aimms;
  5: using System.IO;
  6: using System.Diagnostics;
  7: 
  8: namespace example
  9: {
 10: 
 11:     /// <summary>
 12:     /// This example demonstrates the custom handling of errors.
 13:     /// It overrides the default behavior in which AimmsExceptions are thrown in all error cases in two ways:
 14:     /// The LoggingCallback logs all minor errors, and keeps throwing the severe ones
 15:     /// The DebuggingCallback logs as well, and asserts where errors can only be expected if the model was called erroneously.
 16:     /// </summary>
 17:     class errorCallBack
 18:     {
 19:         class LoggingCallback : ICallBack
 20:         {
 21:             virtual public void onMessages(IList<AimmsMessage> messages)
 22:             {
 23:                 /* Overridden behavior:
 24:                    Only throw if an error is caused by the communication layer or the AIMMS model. 
 25:                    Send message to the Console instead in other cases
 26:                 */
 27: 
 28:                 for (int m = 0; m < messages.Count; ++m) {
 29:                     switch (messages[m].Code) {
 30:                         case AimmsMessageCode.AM_Info:
 31:                             Console.WriteLine("info: " + messages[m].Message);
 32:                             break;
 33:                         case AimmsMessageCode.AM_Warning:
 34:                             Console.WriteLine("warning: " + messages[m].Message);
 35:                             break;
 36:                         case AimmsMessageCode.AM_InvalidUse:
 37:                             Console.WriteLine("invalid use: " + messages[m].Message);
 38:                             break;
 39:                         case AimmsMessageCode.AM_Runtime:
 40:                             Console.WriteLine("runtime error: " + messages[m].Message);
 41:                             break;
 42:                         case AimmsMessageCode.AM_License:
 43:                             throw new AimmsLicenseException(messages[m].Message);
 44:                         case AimmsMessageCode.AM_Communication:
 45:                             throw new AimmsCommunicationException(messages[m].Message);
 46:                         case AimmsMessageCode.AM_General:  // AIMMS Model errors
 47:                             throw new AimmsException(messages[m].Code, messages[0].Message);
 48:                         default:
 49:                             throw new AimmsException(messages[m].Code, messages[0].Message);
 50:                     }
 51: 
 52:                     /* 
 53:             
 54:                     * The rationale behind this distinction is that in the cases in which now exceptions are thrown, 
 55:                       the state of the data is not known. For example: if during the flush of the buffers an error on 
 56:                       a specific value is detected by the AIMMS model, it is not known which part of the remaining data 
 57:                       is already written to the model. 
 58:                     */
 59: 
 60:                 }
 61:             }
 62:         };
 63: 
 64:         class DebuggingCallback : ICallBack
 65:         {
 66: 
 67:             virtual public void onMessages(IList<AimmsMessage> messages)
 68:             {
 69:                 /* Overridden behavior:
 70:            
 71:                     assert if an AM_InvalidUse is encountered, because that is an indication of a bug. 
 72:                     For example: adding a string to a numerical parameter gives AM_InvalidUse. 
 73:                 */
 74: 
 75:                 for (int m = 0; m < messages.Count; ++m) {
 76:                     switch (messages[m].Code) {
 77:                         case AimmsMessageCode.AM_InvalidUse:
 78:                             Console.WriteLine("invalid use: " + messages[m].Message);
 79:                             Debug.Assert(false);
 80:                             break;
 81:                         default:
 82:                             Console.WriteLine(messages[m].Code + ":" + messages[m].Message);
 83:                             break;
 84:                     }
 85: 
 86:                 }
 87:             }
 88: 
 89:         };
 90: 
 91: 
 92:         public static void run(ISession session, TextWriter os)
 93:         {
 94:             // Activate one of the lines below to attach the custom Callback of choice to the session.
 95:             LoggingCallback cb = new LoggingCallback();
 96:             // DebuggingCallback cb = new DebuggingCallback();
 97: 
 98:             session.setCallBack(cb);
 99: 
100:             //To illustrate the behavior of our callback, make some mistakes:
101: 
102:             //Try to retrieve the set Locations as a multidimensional parameter.
103:             //This is a InvalidUse error: 
104:             //  LoggingCallback will not throw
105:             //  DebuggingCallback will assert here. 
106:             IMultiDimData parameterLocations = session.openMultiDim("Locations");
107: 
108:             if (parameterLocations == null)
109:             {
110:                 os.WriteLine("Expected an InvalidUse error!");
111:             }
112: 
113:             IMultiDimData parameterSupply = session.openMultiDim("Supply");
114:             //Do not forget to check the result if the callback does not throw on each error!
115:             if (parameterSupply == null)
116:             {
117:                 os.WriteLine("Did not expect an error here!");
118:             }
119:             else
120:             {
121:                 // Since we did not add any depots, we will get a runtime error here:
122:                 parameterSupply.setValue(new Tuple("London"), 20.0);
123: 
124:                 // Using "insert" will resolve this error, since it adds London to the index domain of Supply if not existing. 
125:                 parameterSupply.insert(new Tuple("London"), 20.0);
126: 
127:                 // Supply is a numeric parameter: it will not allow strings as value:
128:                 // DebuggingCallback will assert.
129:                 parameterSupply.insert(new Tuple("Paris"), "thirty");
130:             }
131: 
132:             IMultiDimData parameterDemand = session.openMultiDim("Demand");
133: 
134:             if (parameterDemand == null)
135:             {
136:                 os.WriteLine("Did not expect an error here!");
137:             }
138:             else
139:             {
140:                 ITuple tup = parameterDemand.createTuple();
141: 
142:                 // "London" exists in the set Locations ( because we added it to Depots by inserting in Supply)
143:                 // But the index domain of Demand is Customers, which  is still empty.
144:                 // The nocheck_ functions are less expensive because they do not check this in advance.
145:                 // Cout will not show an error after these actions:
146:                 tup[0].nocheck_setLabel("London");
147:                 parameterDemand.setValue(tup, 90);
148:                 tup.close();
149: 
150:                 // But this performance improvement comes with a downside:
151:                 try
152:                 {
153:                     // The mistake will become apparent when the data is send to the model. 
154:                     // Then it is undetermined wether the inserted value for "London" in Supply is indeed inserted in Aimms.
155:                     // If Londen is added to the domainset and this set is flushed before the Supply is flushed,
156:                     // the Supply of Londen will be set to its new value without an error.
157:                     // If Londen is not added or Supply is flushed earlier (which may be due to an internal flush),
158:                     // then an error will occur and even other buffered modifications to Supply may not have been inserted in Aimms.
159:   
160:                     session.updateData();
161: 
162:                 }
163:                 catch (AimmsException e)
164:                 {
165:                     os.WriteLine(e.Message);
166:                 }
167: 
168:                 parameterSupply.close();
169:                 parameterDemand.close();
170:             }
171:         }
172:     }
173: }