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: }