/* * Nomad Instrument Control Software * * Copyright 2011 Institut Laue-Langevin * * Licensed under the EUPL, Version 1.1 only (the "License"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * * http://joinup.ec.europa.eu/software/page/eupl * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. */ #include #include #include #include #include #include #include #include #include #include #include #include "RemoteMatlabReq.h" namespace tas { using namespace std; using namespace common; using namespace boost; using namespace cameo; using boost::property_tree::ptree; const std::string RemoteMatlabReq::TYPE = "tas_remote_matlab_req"; const std::string RemoteMatlabReq::MATLAB_APPLICATION = "matlab"; const std::string RemoteMatlabReq::NUMOR_RESPONDER = "tas_numor"; const int32 RemoteMatlabReq::LOG_TYPE = 1; const int32 RemoteMatlabReq::LIVE_TYPE = 2; RemoteMatlabReq::RemoteMatlabReq(const std::string& name) : RemoteMatlab(name) { } RemoteMatlabReq::RemoteMatlabReq(const RemoteMatlabReq& controller) : RemoteMatlab(controller) { } RemoteMatlabReq::~RemoteMatlabReq() { // Stop the remote application. m_matlabApplication->stop(); // Wait for the termination application::State state = m_matlabApplication->waitFor(); cout << "Matlab application terminated with state " << application::toString(state) << endl; } void RemoteMatlabReq::postConfiguration() { RemoteMatlab::postConfiguration(); registerUpdater(scanController->serializerEvent, &RemoteMatlabReq::updateNumor, this); //registerUpdater(scanController->getCount()->statusMessage, &RemoteMatlabReq::updateCountStatusMessage, this); registerUpdater(countSpy->statusMessage, &RemoteMatlabReq::updateCountStatusMessage, this); if (serverEndpoint() == "") { m_server.reset(new Server(application::This::getServer().getEndpoint())); } else { m_server.reset(new Server(serverEndpoint())); } initialized = initApplication(); } bool RemoteMatlabReq::initApplication() { // Do not initialize if it is already done if (initialized()) { return true; } try { // Start the Matlab server if (!m_server->isAvailable(10000)) { cout << "Matlab server is not available" << endl; return false; } cout << "Matlab server is connected to " << getName() << endl; m_matlabApplication = m_server->connect(MATLAB_APPLICATION); if (m_matlabApplication->exists()) { // The application exists from a previous server session m_matlabApplication->kill(); application::State state = m_matlabApplication->waitFor(); cout << "Terminated matlab application " << state << endl; } m_matlabApplication = m_server->start(MATLAB_APPLICATION); if (!m_matlabApplication->exists()) { cout << "No matlab application" << endl; return false; } // Create the requester. m_requester = application::Requester::create(*m_matlabApplication, NUMOR_RESPONDER); if (m_requester.get() == 0) { cout << "requester error" << endl; return false; } // Application initialized. initialized = true; return true; } catch (...) { // Currently an exception can occur during isAvailable. cout << "Unexpected exception during matlab connection" << endl; } return false; } void RemoteMatlabReq::updateNumor() { // Send the numor to be computed if there is something to serialize if (scanController->serializerEvent() && active()) { // Send the numor if (test()) { sendNumor(testNumor(), false); } else { sendNumor(scanController->numor(), false); } } } void RemoteMatlabReq::updateCountStatusMessage() { if ((countSpy->statusMessage() == "Close data") && scanController->getScanInfo()->running() && active()) { cout << "Send the numor for live refresh" << endl; // Send the numor if (test()) { sendNumor(testNumor(), true); } else { sendNumor(scanController->numor(), true); } } } void RemoteMatlabReq::sendNumor(int32 numor, bool live) { if (!initApplication()) { return; } // Get the content of the numor. string numorName = lexical_cast(numor); size_t size = numorName.size(); if (size < 6) { string prefix(6 - size, '0'); numorName = prefix + numorName; } // Not log when the calculation is for live process. if (!live) { LogStream logStream = log(Level::s_Info).property(scanController->logAcquisitionType) .property(scanController->logNumor) .property(scanController->logSubtitle); logStream << numorName << " in Q" << image(numor, "q") << endlog; } string numorFileName(ServerProperties::getInstance()->getNomadDataPath()); numorFileName += numorName; ifstream numorFile; numorFile.open(numorFileName.c_str()); stringstream stream; stream << numorFile.rdbuf(); // Serialize the request. proto::NumorRequest request; if (live) { request.mutable_info()->set_type(proto::NumorInfo_Type_LIVE); } else { request.mutable_info()->set_type(proto::NumorInfo_Type_LOG); } request.mutable_info()->set_propid(getProposalId()); request.mutable_info()->set_proposal(getProposalName()); request.mutable_info()->set_instrid(getInstrumentId()); request.mutable_info()->set_instrument(getInstrumentName()); request.mutable_info()->set_sampid(getSampleId()); request.mutable_info()->set_sample(getSampleName()); request.mutable_info()->set_cycle(getCycleId()); request.mutable_info()->set_imageid(getImageName()); request.mutable_info()->set_numor(lexical_cast(numor)); request.set_content(stream.str()); // Send the message. m_requester->sendBinary(request.SerializeAsString()); cout << getName() << " sent numor request " << numor << endl; // Wait for the response synchronously. // Note that responses can be processed asynchronously if the server is able to. string data; m_requester->receiveBinary(data); proto::NumorResponse response; response.ParseFromString(data); if (response.mutable_info()->type() == proto::NumorInfo_Type_LOG) { sendImageToFluentd(response); } else { setContentFromBinaryData(response.image()); } } void RemoteMatlabReq::start() { // Send the numor sendNumor(testNumor(), true); } void RemoteMatlabReq::sendImageToFluentd(const proto::NumorResponse& response) { // Encode the JSON message ptree message; message.put("id", Date().toString()); message.put("mt", "acq"); message.put("name", "numor"); message.put("type", "number"); message.put("value", response.info().numor()); message.put("output", "null"); message.put("level", "info"); message.put("propid", response.info().propid()); message.put("instrid", response.info().instrid()); message.put("sampleid", response.info().sampid()); message.put("sample", response.info().sample()); message.put("cycleid", response.info().cycle()); message.put("imagefile", response.info().imageid()); // The binary content of the image is a string and we encode it in base64 message.put("imagedata", Calculations::encodeBase64(response.image())); stringstream os; write_json(os, message, false); std::string instrumentName = response.info().instrument(); boost::to_upper(instrumentName); RestJsonHttpConnection con; try { con.openUrl("localhost", "8888"); // New environment variable. if (ServerProperties::getInstance()->getNomadSenddata() == "0") { // If 0 send data to dev database. con.setPath(string("/oracle.forward.") + instrumentName + "_DEV"); } else if (ServerProperties::getInstance()->getNomadSenddata() == "1") { // If 1 send data to prod database. con.setPath(string("/oracle.forward.") + instrumentName); } con.setData(os.str()); boost::property_tree::ptree response = con.send(false); } catch (const Exception& e) { // log to server cerr << e.printStack() << endl; } } }