///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2017 Statoil ASA // // ResInsight is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RiaSocketCommand.h" #include "RiaSocketServer.h" #include "RiaSocketDataTransfer.h" #include "RiaSocketTools.h" #include "RiaApplication.h" #include "RiaPreferences.h" #include "RigActiveCellInfo.h" #include "RigCaseCellResultsData.h" #include "RigEclipseCaseData.h" #include "RigMainGrid.h" #include "Rim3dOverlayInfoConfig.h" #include "RimCellEdgeColors.h" #include "RimCellRangeFilterCollection.h" #include "RimEclipseCase.h" #include "RimEclipseCellColors.h" #include "RimEclipseInputCase.h" #include "RimEclipseInputProperty.h" #include "RimEclipseInputPropertyCollection.h" #include "RimEclipsePropertyFilterCollection.h" #include "RimEclipseView.h" #include "RimIntersectionCollection.h" #include "RimReservoirCellResultsStorage.h" #include "RimSimWellInViewCollection.h" #include //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- class RiaGetNNCConnections: public RiaSocketCommand { public: static QString commandName () { return QString("GetNNCConnections"); } bool interpretCommand(RiaSocketServer* server, const QList& args, QDataStream& socketStream) override { RimEclipseCase* rimCase = RiaSocketTools::findCaseFromArgs(server, args); // Write data back to octave: columnCount, GridNr I J K GridNr I J K if (!(rimCase && rimCase->eclipseCaseData() && rimCase->eclipseCaseData()->mainGrid())) { // No data available socketStream << (quint64)0; return true; } RigMainGrid* mainGrid = rimCase->eclipseCaseData()->mainGrid(); size_t connectionCount = mainGrid->nncData()->connections().size(); socketStream << (quint64)connectionCount; for (const RigConnection& connection : mainGrid->nncData()->connections()) { const RigCell& cell1 = mainGrid->globalCellArray()[connection.m_c1GlobIdx]; const RigCell& cell2 = mainGrid->globalCellArray()[connection.m_c2GlobIdx]; sendCellInfo(socketStream, cell1); sendCellInfo(socketStream, cell2); } return true; } static void sendCellInfo(QDataStream& socketStream, const RigCell& cell) { RigGridBase* hostGrid = cell.hostGrid(); size_t gridLocalCellIndex = cell.gridLocalCellIndex(); size_t i, j, k; hostGrid->ijkFromCellIndex(gridLocalCellIndex, &i, &j, &k); socketStream << (qint32)hostGrid->gridIndex(); socketStream << (qint32)(i+1) << (qint32)(j+1) << (qint32)(k+1); } }; static bool RiaGetNNCConnections_init = RiaSocketCommandFactory::instance()->registerCreator(RiaGetNNCConnections::commandName()); //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- class RiaGetDynamicNNCValues: public RiaSocketCommand { public: static QString commandName () { return QString("GetDynamicNNCValues"); } bool interpretCommand(RiaSocketServer* server, const QList& args, QDataStream& socketStream) override { RimEclipseCase* rimCase = RiaSocketTools::findCaseFromArgs(server, args); // Write data back to octave: connectionCount, timeStepCount, property values if (!(rimCase && rimCase->eclipseCaseData() && rimCase->eclipseCaseData()->mainGrid())) { // No data available socketStream << (quint64)0 << (quint64)0; return true; } QString propertyName = args[2]; RigMainGrid* mainGrid = rimCase->eclipseCaseData()->mainGrid(); const std::vector< std::vector >* nncValues = mainGrid->nncData()->dynamicConnectionScalarResultByName(propertyName); if (nncValues == nullptr) { socketStream << (quint64)0 << (quint64)0; return true; } std::vector requestedTimeSteps; if (args.size() > 3) { bool timeStepReadError = false; for (int argIdx = 3; argIdx < args.size(); ++argIdx) { bool conversionOk = false; int tsIdx = args[argIdx].toInt(&conversionOk); if (conversionOk) { requestedTimeSteps.push_back(tsIdx); } else { timeStepReadError = true; } } if (timeStepReadError) { server->showErrorMessage(RiaSocketServer::tr("ResInsight SocketServer: riGetDynamicNNCValues : \n") + RiaSocketServer::tr("An error occurred while interpreting the requested time steps.")); } } else { for (size_t timeStep = 0; timeStep < nncValues->size(); ++timeStep) { requestedTimeSteps.push_back(timeStep); } } // then the connection count and time step count. size_t connectionCount = mainGrid->nncData()->connections().size(); size_t timeStepCount = requestedTimeSteps.size(); socketStream << (quint64)connectionCount; socketStream << (quint64)timeStepCount; for (size_t timeStep : requestedTimeSteps) { const std::vector& timeStepValues = nncValues->at(timeStep); RiaSocketTools::writeBlockData(server, server->currentClient(), (const char *)timeStepValues.data(), sizeof(double) * timeStepValues.size()); } return true; } }; static bool RiaGetDynamicNNCValues_init = RiaSocketCommandFactory::instance()->registerCreator(RiaGetDynamicNNCValues::commandName()); //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- class RiaGetStaticNNCValues: public RiaSocketCommand { public: static QString commandName () { return QString("GetStaticNNCValues"); } bool interpretCommand(RiaSocketServer* server, const QList& args, QDataStream& socketStream) override { RimEclipseCase* rimCase = RiaSocketTools::findCaseFromArgs(server, args); QString propertyName = args[2]; // Write data back to octave: connectionCount, property values if (!(rimCase && rimCase->eclipseCaseData() && rimCase->eclipseCaseData()->mainGrid())) { // No data available socketStream << (quint64)0; return true; } RigMainGrid* mainGrid = rimCase->eclipseCaseData()->mainGrid(); const std::vector* nncValues = mainGrid->nncData()->staticConnectionScalarResultByName(propertyName); if (nncValues == nullptr) { socketStream << (quint64)0; return true; } // connection count size_t connectionCount = mainGrid->nncData()->connections().size(); socketStream << (quint64)connectionCount; RiaSocketTools::writeBlockData(server, server->currentClient(), (const char *)nncValues->data(), sizeof(double) * nncValues->size()); return true; } }; static bool RiaGetStaticNNCValues_init = RiaSocketCommandFactory::instance()->registerCreator(RiaGetStaticNNCValues::commandName()); //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- class RiaGetNNCPropertyNames: public RiaSocketCommand { public: static QString commandName () { return QString("GetNNCPropertyNames"); } bool interpretCommand(RiaSocketServer* server, const QList& args, QDataStream& socketStream) override { RimEclipseCase* rimCase = RiaSocketTools::findCaseFromArgs(server, args); if (!(rimCase && rimCase->eclipseCaseData() && rimCase->eclipseCaseData()->mainGrid())) { // No data available socketStream << (quint64)0; return true; } RigNNCData* nncData = rimCase->eclipseCaseData()->mainGrid()->nncData(); std::vector propertyTypes; std::vector propertyNames; std::vector resultTypes; std::vector resultTypeNames; resultTypes.push_back(RigNNCData::NNC_DYNAMIC); resultTypeNames.push_back("DynamicNative"); resultTypes.push_back(RigNNCData::NNC_STATIC); resultTypeNames.push_back("StaticNative"); resultTypes.push_back(RigNNCData::NNC_GENERATED); resultTypeNames.push_back("Generated"); for (size_t rtIdx = 0; rtIdx < resultTypes.size(); ++rtIdx) { std::vector availableParameters = nncData->availableProperties(resultTypes[rtIdx]); for (const QString& parameter : availableParameters) { propertyNames.push_back(parameter); propertyTypes.push_back(resultTypeNames[rtIdx]); } } qint64 byteCount = 0; for (size_t ptIdx = 0; ptIdx < propertyNames.size(); ++ptIdx) { byteCount += propertyNames[ptIdx].size() * sizeof(QChar); byteCount += propertyTypes[ptIdx].size() * sizeof(QChar); } // Byte count socketStream << byteCount; // Parameter count socketStream << (quint64)propertyNames.size(); for (size_t ptIdx = 0; ptIdx < propertyNames.size(); ++ptIdx) { socketStream << propertyNames[ptIdx]; socketStream << propertyTypes[ptIdx]; } return true; } }; static bool RiaGetNNCPropertyNames_init = RiaSocketCommandFactory::instance()->registerCreator(RiaGetNNCPropertyNames::commandName()); //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- class RiaSetNNCProperty: public RiaSocketCommand { public: RiaSetNNCProperty() : m_currentReservoir(nullptr), m_currentEclResultAddress(), m_timeStepCountToRead(0), m_bytesPerTimeStepToRead(0), m_currentTimeStepNumberToRead(0), m_invalidConnectionCountDetected(false), m_porosityModelEnum(RiaDefines::MATRIX_MODEL) {} static QString commandName () { return QString("SetNNCProperty"); } bool interpretCommand(RiaSocketServer* server, const QList& args, QDataStream& socketStream) override { RimEclipseCase* rimCase = RiaSocketTools::findCaseFromArgs(server, args); QString propertyName = args[2]; // Find the requested data, or create a set if we are setting data and it is not found if (!(rimCase && rimCase->eclipseCaseData() && rimCase->eclipseCaseData()->mainGrid())) { QString caseId = args[1]; server->showErrorMessage(RiaSocketServer::tr("ResInsight SocketServer: \n") + RiaSocketServer::tr("Could not find case with id %1").arg(caseId)); return true; } // If we have not read the header and there are data enough: Read it. // Do nothing if we have not enough data if (m_timeStepCountToRead == 0 || m_bytesPerTimeStepToRead == 0) { if (server->currentClient()->bytesAvailable() < (int)sizeof(quint64)*2) return true; socketStream >> m_timeStepCountToRead; socketStream >> m_bytesPerTimeStepToRead; } RigNNCData* nncData = rimCase->eclipseCaseData()->mainGrid()->nncData(); auto nncResults = nncData->generatedConnectionScalarResultByName(propertyName); if (nncResults == nullptr) { nncData->makeGeneratedConnectionScalarResult(propertyName, m_timeStepCountToRead); } if (rimCase && rimCase->results(m_porosityModelEnum)) { bool ok = createIJKCellResults(rimCase->results(m_porosityModelEnum), propertyName); if (!ok) { server->showErrorMessage(RiaSocketServer::tr("ResInsight SocketServer: \n") + RiaSocketServer::tr("Could not find the property named: \"%2\"").arg(propertyName)); return true; } RigEclipseResultAddress resAddr(QString("%1IJK").arg(propertyName)); rimCase->results(m_porosityModelEnum)->ensureKnownResultLoaded(resAddr); nncData->setEclResultAddress(propertyName, resAddr); } // Create a list of all the requested time steps m_requestedTimesteps.clear(); if (args.size() <= 4) { // Select all for (size_t tsIdx = 0; tsIdx < m_timeStepCountToRead; ++tsIdx) { m_requestedTimesteps.push_back(tsIdx); } } else { bool timeStepReadError = false; for (int argIdx = 4; argIdx < args.size(); ++argIdx) { bool conversionOk = false; int tsIdx = args[argIdx].toInt(&conversionOk); if (conversionOk) { m_requestedTimesteps.push_back(tsIdx); } else { timeStepReadError = true; } } if (timeStepReadError) { server->showErrorMessage(RiaSocketServer::tr("ResInsight SocketServer: riSetNNCProperty : \n") + RiaSocketServer::tr("An error occurred while interpreting the requested time steps.")); } } if (! m_requestedTimesteps.size()) { server->showErrorMessage(RiaSocketServer::tr("ResInsight SocketServer: \n") + RiaSocketServer::tr("No time steps specified")); return true; } m_currentReservoir = rimCase; m_currentPropertyName = propertyName; if (server->currentClient()->bytesAvailable()) { return this->interpretMore(server, server->currentClient()); } return false; } private: static bool createIJKCellResults(RigCaseCellResultsData* results, QString propertyName) { bool ok; ok = scalarResultExistsOrCreate(results, QString("%1IJK").arg(propertyName)); if (!ok) return false; ok = scalarResultExistsOrCreate(results, QString("%1I").arg(propertyName)); if (!ok) return false; ok = scalarResultExistsOrCreate(results, QString("%1J").arg(propertyName)); if (!ok) return false; ok = scalarResultExistsOrCreate(results, QString("%1K").arg(propertyName)); return ok; } static bool scalarResultExistsOrCreate(RigCaseCellResultsData* results, QString propertyName) { RigEclipseResultAddress resAddr(RiaDefines::GENERATED, propertyName); if ( !results->ensureKnownResultLoaded(resAddr) ) { results->createResultEntry(resAddr, true); } std::vector< std::vector >* scalarResultFrames = nullptr; scalarResultFrames = &(results->modifiableCellScalarResultTimesteps(resAddr)); size_t timeStepCount = results->maxTimeStepCount(); scalarResultFrames->resize(timeStepCount); return true; } public: bool interpretMore(RiaSocketServer* server, QTcpSocket* currentClient) override { if (m_invalidConnectionCountDetected) return true; // If nothing should be read, or we already have read everything, do nothing if ((m_timeStepCountToRead == 0) || (m_currentTimeStepNumberToRead >= m_timeStepCountToRead) ) return true; if (!currentClient->bytesAvailable()) return false; if (m_timeStepCountToRead != m_requestedTimesteps.size()) { CVF_ASSERT(false); } // Check if a complete timestep is available, return and whait for readyRead() if not if (currentClient->bytesAvailable() < (int)m_bytesPerTimeStepToRead) return false; RigNNCData* nncData = m_currentReservoir->eclipseCaseData()->mainGrid()->nncData(); size_t connectionCountFromOctave = m_bytesPerTimeStepToRead / sizeof(double); size_t connectionCount = nncData->connections().size(); std::vector< std::vector >* resultsToAdd = nncData->generatedConnectionScalarResultByName(m_currentPropertyName); if (connectionCountFromOctave != connectionCount) { server->showErrorMessage(RiaSocketServer::tr("ResInsight SocketServer: \n") + RiaSocketServer::tr("The number of connections in the data coming from octave does not match the case: '%1'\n").arg(m_currentReservoir->caseUserDescription()) + RiaSocketServer::tr(" Octave: %1\n").arg(connectionCountFromOctave) + RiaSocketServer::tr(" %1: Connection count: %2").arg(m_currentReservoir->caseUserDescription()).arg(connectionCount)); connectionCountFromOctave = 0; m_invalidConnectionCountDetected = true; currentClient->abort(); return true; } for (size_t tIdx = 0; tIdx < m_timeStepCountToRead; ++tIdx) { size_t tsId = m_requestedTimesteps[tIdx]; resultsToAdd->at(tsId).resize(connectionCount, HUGE_VAL); } double* internalMatrixData = nullptr; QDataStream socketStream(currentClient); socketStream.setVersion(riOctavePlugin::qtDataStreamVersion); // Read available complete time step data while ((currentClient->bytesAvailable() >= (int)m_bytesPerTimeStepToRead) && (m_currentTimeStepNumberToRead < m_timeStepCountToRead)) { internalMatrixData = resultsToAdd->at(m_requestedTimesteps[m_currentTimeStepNumberToRead]).data(); QStringList errorMessages; if (!RiaSocketDataTransfer::readBlockDataFromSocket(currentClient, (char*)(internalMatrixData), m_bytesPerTimeStepToRead, errorMessages)) { for (int i = 0; i < errorMessages.size(); i++) { server->showErrorMessage(errorMessages[i]); } currentClient->abort(); return true; } ++m_currentTimeStepNumberToRead; } // If we have read all the data, refresh the views if (m_currentTimeStepNumberToRead == m_timeStepCountToRead) { if (m_currentReservoir != nullptr) { // Create a new input property if we have an input reservoir RimEclipseInputCase* inputRes = dynamic_cast(m_currentReservoir); if (inputRes) { RimEclipseInputProperty* inputProperty = inputRes->inputPropertyCollection()->findInputProperty(m_currentPropertyName); if (!inputProperty) { inputProperty = new RimEclipseInputProperty; inputProperty->resultName = m_currentPropertyName; inputProperty->eclipseKeyword = ""; inputProperty->fileName = ""; inputRes->inputPropertyCollection()->inputProperties.push_back(inputProperty); inputRes->inputPropertyCollection()->updateConnectedEditors(); } inputProperty->resolvedState = RimEclipseInputProperty::RESOLVED_NOT_SAVED; } if( m_currentEclResultAddress.isValid() && // Will never be valid because it is never set. What is correct behaviour ? m_currentReservoir->eclipseCaseData() && m_currentReservoir->eclipseCaseData()->results(m_porosityModelEnum) ) { m_currentReservoir->eclipseCaseData()->results(m_porosityModelEnum)->recalculateStatistics(m_currentEclResultAddress); } for (size_t i = 0; i < m_currentReservoir->reservoirViews.size(); ++i) { if (m_currentReservoir->reservoirViews[i]) { // As new result might have been introduced, update all editors connected m_currentReservoir->reservoirViews[i]->cellResult()->updateConnectedEditors(); // It is usually not needed to create new display model, but if any derived geometry based on generated data (from Octave) // a full display model rebuild is required m_currentReservoir->reservoirViews[i]->scheduleCreateDisplayModelAndRedraw(); m_currentReservoir->reservoirViews[i]->crossSectionCollection()->scheduleCreateDisplayModelAndRedraw2dIntersectionViews(); } } } return true; } return false; } private: RimEclipseCase* m_currentReservoir; RigEclipseResultAddress m_currentEclResultAddress; QString m_currentPropertyName; std::vector m_requestedTimesteps; RiaDefines::PorosityModelType m_porosityModelEnum; quint64 m_timeStepCountToRead; quint64 m_bytesPerTimeStepToRead; size_t m_currentTimeStepNumberToRead; bool m_invalidConnectionCountDetected; }; static bool RiaSetNNCProperty_init = RiaSocketCommandFactory::instance()->registerCreator(RiaSetNNCProperty::commandName());