/// <summary> /// This method obtains the data from the GAMS file using the low level API /// </summary> /// <param name="_source"></param> /// <param name="file"></param> /// <param name="table"></param> /// <param name="where"></param> /// <returns></returns> public static IEnumerable <QvxDataRow> GetGamsDataLowLevel(string _source, string file, QvxTable table, List <string> selectedFields, List <QueryExtractor.WhereCondition> where) { const int GMS_VAL_LEVEL = 0; const int GMS_VAL_MARGINAL = 1; const int GMS_VAL_LOWER = 2; const int GMS_VAL_UPPER = 3; const int GMS_VAL_SCALE = 4; //const int GMS_VAL_MAX = 5; string msg = string.Empty, VarName = string.Empty, sText = string.Empty, UelName = string.Empty; int ErrNr = 0, SymNr = 0, SymTyp = 0, ADim = 0, ACount = 0, AUser = 0, NRec = 0, FDim = 0, j = 0, i = 0, IDum = 0; using (gdxcs gdx = new gdxcs(ref msg)) { gdx.gdxOpenRead(_source + "\\" + file, ref ErrNr); gdx.gdxFindSymbol(table.TableName, ref SymNr); gdx.gdxSymbolInfo(SymNr, ref VarName, ref ADim, ref SymTyp); gdx.gdxSymbolInfoX(SymNr, ref ACount, ref AUser, ref sText); gdx.gdxDataReadRawStart(SymNr, ref NRec); Dictionary <int, string>[] dimensionCache = new Dictionary <int, string> [ADim]; if (where.Count == 0) { // No WHERE clause double[] Vals = new double[gamsglobals.val_max]; int[] Keys = new int[gamsglobals.maxdim]; while (gdx.gdxDataReadRaw(ref Keys, ref Vals, ref FDim) != 0) { QvxDataRow row = mapRow(Keys, Vals); if (row != null) { yield return(row); } } } else { // WHERE clause string[] strFilter = Enumerable.Repeat("", table.Fields.Count()).ToArray(); var emptyFilters = new List <QvxField>(); foreach (var whereCondition in where) { int m = Array.FindIndex(table.Fields, element => element.FieldName == whereCondition.Field); if (m >= 0) { if (m > (ADim - 1)) { // Only dimensions can be filtered throw new QvxPleaseSendReplyException(QvxResult.QVX_UNSUPPORTED_COMMAND, String.Format("Field \"{0}\" is not a dimension and can't be filtered", whereCondition.Field)); } else if ("".Equals(whereCondition.Value)) { // GAMS API doesn't allow empty filters, so we have to filter them ourselves emptyFilters.Add(table.Fields[m]); } else { strFilter[m] = whereCondition.Value; } } else { throw new QvxPleaseSendReplyException(QvxResult.QVX_FIELD_NOT_FOUND, String.Format("The field \"{0}\" is not valid", whereCondition.Field)); } } using (BlockingCollection <QvxDataRow> buffer = new BlockingCollection <QvxDataRow>()) { // Start reading thread Exception exception = null; var readTask = Task.Run(() => { try { gdx.gdxDataReadRawFastFilt(SymNr, strFilter, FilterProc); } catch (Exception e) { exception = e; } finally { // Signal the end of the data buffer.CompleteAdding(); } }); int FilterProc(IntPtr IndxPtr, IntPtr ValsPtr, IntPtr Uptr) { double[] managedArrayVals = new double[gamsglobals.val_max]; Marshal.Copy(ValsPtr, managedArrayVals, 0, gamsglobals.val_max); int[] managedArrayIndx = new int[gamsglobals.maxdim]; Marshal.Copy(IndxPtr, managedArrayIndx, 0, gamsglobals.maxdim); QvxDataRow row = mapRow(managedArrayIndx, managedArrayVals, emptyFilters); if (row != null) { buffer.Add(row); } return(1); } // Writing process foreach (QvxDataRow row in buffer.GetConsumingEnumerable()) { yield return(row); } if (exception != null) { throw exception; } } } QvxDataRow mapRow(int[] Keys, double[] Vals, List <QvxField> emptyFilters = null) { i = 0; var row = new QvxDataRow(); // Read the dimensions for (j = 0; j < ADim; j++) { if (dimensionCache[j] == null) { dimensionCache[j] = new Dictionary <int, string>(); } UelName = null; if (dimensionCache[j].ContainsKey(Keys[j])) { UelName = dimensionCache[j][Keys[j]]; } else { gdx.gdxUMUelGet(Keys[j], ref UelName, ref IDum); dimensionCache[j][Keys[j]] = UelName; } if (UelName != null) { QvxField field = table.Fields[i++]; if (selectedFields.Contains(field.FieldName)) { row[field] = UelName; } // we check the empty filters, as GAMS API doesn't do it if (emptyFilters != null && emptyFilters.Contains(field) && !string.IsNullOrEmpty(UelName)) { return(null); } } } switch (SymTyp) { // SET case gamsglobals.dt_set: if (gdx.gdxGetElemText((int)Vals[GMS_VAL_LEVEL], ref msg, ref IDum) != 0) { QvxField field2 = table.Fields[i++]; if (selectedFields.Contains(field2.FieldName)) { row[field2] = msg; } } else { QvxField field2 = table.Fields[i++]; if (selectedFields.Contains(field2.FieldName)) { row[field2] = Y_STRING; } } break; // PARAMETER case gamsglobals.dt_par: // Value readValueField(row, table.Fields[i++], table.Fields[i++], Vals[GMS_VAL_LEVEL]); if (!string.IsNullOrEmpty(sText) && ADim == 0) { QvxField field = table.Fields[i++]; if (selectedFields.Contains(field.FieldName)) { row[field] = sText; } } break; // EQUATION and VARIABLE case gamsglobals.dt_equ: case gamsglobals.dt_var: int[] gms_values = { GMS_VAL_LEVEL, GMS_VAL_MARGINAL, GMS_VAL_LOWER, GMS_VAL_UPPER, GMS_VAL_SCALE }; foreach (int gms_value in gms_values) { // Value readValueField(row, table.Fields[i++], table.Fields[i++], Vals[gms_value]); } break; } return(row); } /// <summary>This method reads a value separated in two fields, the first with the numeric value and the second with the special value description.</summary> void readValueField(QvxDataRow pRow, QvxField numberField, QvxField specialField, double pVal) { Boolean isSpecialValue = false; // Value if (selectedFields.Contains(numberField.FieldName)) { pRow[numberField] = val2str(pVal, msg, out isSpecialValue, true, false); } // Value (Special) if (selectedFields.Contains(specialField.FieldName)) { pRow[specialField] = val2str(pVal, msg, out isSpecialValue, false, true); } else if (isSpecialValue) { // If the value is special, but the "Special value" column is not selected, we throw an error throw new QvxPleaseSendReplyException(QvxResult.QVX_FIELD_NOT_FOUND, String.Format("The field \"{0}\" contains special values, so the field \"{1}\" has to be selected", numberField.FieldName, specialField.FieldName)); } } /// <summary>This method generates the final value of a field, that can be a normal number, an acronym, infinite, ...</summary> /// <param name="returnNumber">If true, the value of a number will be returned as a double</param> /// <param name="returnSpecial">If true, the value of a special value (infinite, epsilon, acronym, ...) will be returned as a string</param> dynamic val2str(double val, string s, out Boolean isSpecial, Boolean returnNumber = true, Boolean returnSpecial = true) { string[] gmsSVText = { "UNdef", "NA", "+Inf", "-Inf", "Eps", "0", "AcroN" }; int sv = 0; if (gdx.gdxAcronymName(val, ref s) != 0) { isSpecial = true; return(returnSpecial ? s : null); } else { gdx.gdxMapValue(val, ref sv); if (gamsglobals.sv_normal != sv) { isSpecial = true; return(returnSpecial ? gmsSVText[sv] : null); } else { isSpecial = false; if (returnNumber) { return(val); } else { return(null); } } } } } }