File indexing completed on 2024-04-21 14:45:02
0001 /* 0002 SPDX-FileCopyrightText: 2018 Jasem Mutlaq <mutlaqja@ikarustech.com> 0003 0004 Media Channel 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "media.h" 0010 #include "commands.h" 0011 #include "skymapcomposite.h" 0012 #include "fitsviewer/fitsview.h" 0013 #include "fitsviewer/fitsdata.h" 0014 #include "indi/indilistener.h" 0015 #include "hips/hipsfinder.h" 0016 #include "kstarsdata.h" 0017 #include "ekos/auxiliary/darklibrary.h" 0018 #include "ekos/guide/guide.h" 0019 #include "ekos/align/align.h" 0020 #include "kspaths.h" 0021 #include "Options.h" 0022 0023 #include "ekos_debug.h" 0024 #include "kstars.h" 0025 #include "version.h" 0026 0027 #include <QtConcurrent> 0028 #include <KFormat> 0029 0030 namespace EkosLive 0031 { 0032 0033 /////////////////////////////////////////////////////////////////////////////////////////// 0034 /// 0035 /////////////////////////////////////////////////////////////////////////////////////////// 0036 Media::Media(Ekos::Manager * manager, QVector<QSharedPointer<NodeManager>> &nodeManagers): 0037 m_Manager(manager), m_NodeManagers(nodeManagers) 0038 { 0039 for (auto &nodeManager : m_NodeManagers) 0040 { 0041 connect(nodeManager->media(), &Node::connected, this, &Media::onConnected); 0042 connect(nodeManager->media(), &Node::disconnected, this, &Media::onDisconnected); 0043 connect(nodeManager->media(), &Node::onTextReceived, this, &Media::onTextReceived); 0044 connect(nodeManager->media(), &Node::onBinaryReceived, this, &Media::onBinaryReceived); 0045 } 0046 0047 connect(this, &Media::newMetadata, this, &Media::uploadMetadata); 0048 connect(this, &Media::newImage, this, [this](const QByteArray & image) 0049 { 0050 uploadImage(image); 0051 m_TemporaryView.clear(); 0052 }); 0053 } 0054 0055 /////////////////////////////////////////////////////////////////////////////////////////// 0056 /// 0057 /////////////////////////////////////////////////////////////////////////////////////////// 0058 bool Media::isConnected() const 0059 { 0060 return std::any_of(m_NodeManagers.begin(), m_NodeManagers.end(), [](auto & nodeManager) 0061 { 0062 return nodeManager->media()->isConnected(); 0063 }); 0064 } 0065 0066 /////////////////////////////////////////////////////////////////////////////////////////// 0067 /// 0068 /////////////////////////////////////////////////////////////////////////////////////////// 0069 void Media::onConnected() 0070 { 0071 auto node = qobject_cast<Node*>(sender()); 0072 if (!node) 0073 return; 0074 0075 qCInfo(KSTARS_EKOS) << "Connected to Media Websocket server at" << node->url().toDisplayString(); 0076 0077 emit connected(); 0078 } 0079 0080 /////////////////////////////////////////////////////////////////////////////////////////// 0081 /// 0082 /////////////////////////////////////////////////////////////////////////////////////////// 0083 void Media::onDisconnected() 0084 { 0085 auto node = qobject_cast<Node*>(sender()); 0086 if (!node) 0087 return; 0088 0089 qCInfo(KSTARS_EKOS) << "Disconnected from Message Websocket server at" << node->url().toDisplayString(); 0090 0091 if (isConnected() == false) 0092 { 0093 m_sendBlobs = true; 0094 0095 for (const QString &oneFile : temporaryFiles) 0096 QFile::remove(oneFile); 0097 temporaryFiles.clear(); 0098 0099 emit disconnected(); 0100 } 0101 } 0102 0103 /////////////////////////////////////////////////////////////////////////////////////////// 0104 /// 0105 /////////////////////////////////////////////////////////////////////////////////////////// 0106 void Media::onTextReceived(const QString &message) 0107 { 0108 qCInfo(KSTARS_EKOS) << "Media Text Websocket Message" << message; 0109 QJsonParseError error; 0110 auto serverMessage = QJsonDocument::fromJson(message.toLatin1(), &error); 0111 if (error.error != QJsonParseError::NoError) 0112 { 0113 qCWarning(KSTARS_EKOS) << "Ekos Live Parsing Error" << error.errorString(); 0114 return; 0115 } 0116 0117 const QJsonObject msgObj = serverMessage.object(); 0118 const QString command = msgObj["type"].toString(); 0119 const QJsonObject payload = msgObj["payload"].toObject(); 0120 0121 if (command == commands[ALIGN_SET_FILE_EXTENSION]) 0122 extension = payload["ext"].toString(); 0123 else if (command == commands[SET_BLOBS]) 0124 m_sendBlobs = msgObj["payload"].toBool(); 0125 // Get a list of object based on criteria 0126 else if (command == commands[ASTRO_GET_OBJECTS_IMAGE]) 0127 { 0128 int level = payload["level"].toInt(5); 0129 double zoom = payload["zoom"].toInt(20000); 0130 0131 // Object Names 0132 QVariantList objectNames = payload["names"].toArray().toVariantList(); 0133 0134 for (auto &oneName : objectNames) 0135 { 0136 const QString name = oneName.toString(); 0137 SkyObject *oneObject = KStarsData::Instance()->skyComposite()->findByName(name, false); 0138 if (oneObject) 0139 { 0140 QImage centerImage(HIPS_TILE_WIDTH, HIPS_TILE_HEIGHT, QImage::Format_ARGB32_Premultiplied); 0141 double fov_w = 0, fov_h = 0; 0142 0143 if (oneObject->type() == SkyObject::MOON || oneObject->type() == SkyObject::PLANET) 0144 { 0145 QProcess xplanetProcess; 0146 const QString output = KSPaths::writableLocation(QStandardPaths::TempLocation) + QDir::separator() + "xplanet.jpg"; 0147 xplanetProcess.start(Options::xplanetPath(), QStringList() 0148 << "--num_times" << "1" 0149 << "--geometry" << QString("%1x%2").arg(HIPS_TILE_WIDTH).arg(HIPS_TILE_HEIGHT) 0150 << "--body" << name.toLower() 0151 << "--output" << output); 0152 xplanetProcess.waitForFinished(5000); 0153 centerImage.load(output); 0154 } 0155 else 0156 HIPSFinder::Instance()->render(oneObject, level, zoom, ¢erImage, fov_w, fov_h); 0157 0158 if (!centerImage.isNull()) 0159 { 0160 // Use seed from name, level, and zoom so that it is unique 0161 // even if regenerated again. 0162 auto seed = QString("%1%2%3").arg(QString::number(level), QString::number(zoom), name); 0163 QString uuid = "hips_" + QCryptographicHash::hash(seed.toLatin1(), QCryptographicHash::Md5).toHex(); 0164 // Send everything as strings 0165 QJsonObject metadata = 0166 { 0167 {"uuid", uuid}, 0168 {"name", name}, 0169 {"zoom", zoom}, 0170 {"resolution", QString("%1x%2").arg(HIPS_TILE_WIDTH).arg(HIPS_TILE_HEIGHT)}, 0171 {"bin", "1x1"}, 0172 {"fov_w", QString::number(fov_w)}, 0173 {"fov_h", QString::number(fov_h)}, 0174 {"ext", "jpg"} 0175 }; 0176 0177 QByteArray jpegData; 0178 QBuffer buffer(&jpegData); 0179 buffer.open(QIODevice::WriteOnly); 0180 0181 // First METADATA_PACKET bytes of the binary data is always allocated 0182 // to the metadata, the rest to the image data. 0183 QByteArray meta = QJsonDocument(metadata).toJson(QJsonDocument::Compact); 0184 meta = meta.leftJustified(METADATA_PACKET, 0); 0185 buffer.write(meta); 0186 centerImage.save(&buffer, "jpg", 90); 0187 buffer.close(); 0188 0189 emit newImage(jpegData); 0190 } 0191 } 0192 } 0193 } 0194 else if (command == commands[ASTRO_GET_SKYPOINT_IMAGE]) 0195 { 0196 int level = payload["level"].toInt(5); 0197 double zoom = payload["zoom"].toInt(20000); 0198 double ra = payload["ra"].toDouble(0); 0199 double de = payload["de"].toDouble(0); 0200 double width = payload["width"].toDouble(512); 0201 double height = payload["height"].toDouble(512); 0202 0203 QImage centerImage(width, height, QImage::Format_ARGB32_Premultiplied); 0204 SkyPoint coords(ra, de); 0205 SkyPoint J2000Coord(coords.ra(), coords.dec()); 0206 J2000Coord.catalogueCoord(KStars::Instance()->data()->ut().djd()); 0207 coords.setRA0(J2000Coord.ra()); 0208 coords.setDec0(J2000Coord.dec()); 0209 coords.EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); 0210 0211 volatile auto jnowRAString = coords.ra().toHMSString(); 0212 volatile auto jnowDEString = coords.dec().toDMSString(); 0213 volatile auto j2000RAString = coords.ra0().toHMSString(); 0214 volatile auto j2000DEString = coords.dec0().toDMSString(); 0215 0216 0217 double fov_w = 0, fov_h = 0; 0218 HIPSFinder::Instance()->render(&coords, level, zoom, ¢erImage, fov_w, fov_h); 0219 0220 if (!centerImage.isNull()) 0221 { 0222 // Use seed from name, level, and zoom so that it is unique 0223 // even if regenerated again. 0224 // Send everything as strings 0225 QJsonObject metadata = 0226 { 0227 {"uuid", "skypoint_hips"}, 0228 {"name", "skypoint_hips"}, 0229 {"zoom", zoom}, 0230 {"resolution", QString("%1x%2").arg(width).arg(height)}, 0231 {"bin", "1x1"}, 0232 {"fov_w", QString::number(fov_w)}, 0233 {"fov_h", QString::number(fov_h)}, 0234 {"ext", "jpg"} 0235 }; 0236 0237 QByteArray jpegData; 0238 QBuffer buffer(&jpegData); 0239 buffer.open(QIODevice::WriteOnly); 0240 0241 // First METADATA_PACKET bytes of the binary data is always allocated 0242 // to the metadata, the rest to the image data. 0243 QByteArray meta = QJsonDocument(metadata).toJson(QJsonDocument::Compact); 0244 meta = meta.leftJustified(METADATA_PACKET, 0); 0245 buffer.write(meta); 0246 centerImage.save(&buffer, "jpg", 95); 0247 buffer.close(); 0248 0249 emit newImage(jpegData); 0250 } 0251 } 0252 } 0253 0254 /////////////////////////////////////////////////////////////////////////////////////////// 0255 /// 0256 /////////////////////////////////////////////////////////////////////////////////////////// 0257 void Media::onBinaryReceived(const QByteArray &message) 0258 { 0259 // Sometimes this is triggered even though it's a text message 0260 Ekos::Align * align = m_Manager->alignModule(); 0261 if (align) 0262 { 0263 QString metadataString = message.left(METADATA_PACKET); 0264 QJsonDocument metadataDocument = QJsonDocument::fromJson(metadataString.toLatin1()); 0265 QJsonObject metadataJSON = metadataDocument.object(); 0266 QString extension = metadataJSON.value("ext").toString(); 0267 align->loadAndSlew(message.mid(METADATA_PACKET), extension); 0268 } 0269 } 0270 0271 /////////////////////////////////////////////////////////////////////////////////////////// 0272 /// 0273 /////////////////////////////////////////////////////////////////////////////////////////// 0274 void Media::sendDarkLibraryData(const QSharedPointer<FITSData> &data) 0275 { 0276 sendData(data, "+D"); 0277 }; 0278 0279 /////////////////////////////////////////////////////////////////////////////////////////// 0280 /// 0281 /////////////////////////////////////////////////////////////////////////////////////////// 0282 void Media::sendData(const QSharedPointer<FITSData> &data, const QString &uuid) 0283 { 0284 if (Options::ekosLiveImageTransfer() == false || m_sendBlobs == false) 0285 return; 0286 0287 m_UUID = uuid; 0288 0289 m_TemporaryView.reset(new FITSView()); 0290 m_TemporaryView->loadData(data); 0291 QtConcurrent::run(this, &Media::upload, m_TemporaryView); 0292 } 0293 0294 /////////////////////////////////////////////////////////////////////////////////////////// 0295 /// 0296 /////////////////////////////////////////////////////////////////////////////////////////// 0297 void Media::sendFile(const QString &filename, const QString &uuid) 0298 { 0299 if (Options::ekosLiveImageTransfer() == false || m_sendBlobs == false) 0300 return; 0301 0302 m_UUID = uuid; 0303 0304 QSharedPointer<FITSView> previewImage(new FITSView()); 0305 connect(previewImage.get(), &FITSView::loaded, this, [this, previewImage]() 0306 { 0307 QtConcurrent::run(this, &Media::upload, previewImage); 0308 }); 0309 previewImage->loadFile(filename); 0310 } 0311 0312 /////////////////////////////////////////////////////////////////////////////////////////// 0313 /// 0314 /////////////////////////////////////////////////////////////////////////////////////////// 0315 void Media::sendView(const QSharedPointer<FITSView> &view, const QString &uuid) 0316 { 0317 if (Options::ekosLiveImageTransfer() == false || m_sendBlobs == false) 0318 return; 0319 0320 m_UUID = uuid; 0321 0322 upload(view); 0323 } 0324 0325 /////////////////////////////////////////////////////////////////////////////////////////// 0326 /// 0327 /////////////////////////////////////////////////////////////////////////////////////////// 0328 void Media::upload(const QSharedPointer<FITSView> &view) 0329 { 0330 const QString ext = "jpg"; 0331 QByteArray jpegData; 0332 QBuffer buffer(&jpegData); 0333 buffer.open(QIODevice::WriteOnly); 0334 0335 const QSharedPointer<FITSData> imageData = view->imageData(); 0336 QString resolution = QString("%1x%2").arg(imageData->width()).arg(imageData->height()); 0337 QString sizeBytes = KFormat().formatByteSize(imageData->size()); 0338 QVariant xbin(1), ybin(1), exposure(0), focal_length(0), gain(0), pixel_size(0), aperture(0); 0339 imageData->getRecordValue("XBINNING", xbin); 0340 imageData->getRecordValue("YBINNING", ybin); 0341 imageData->getRecordValue("EXPTIME", exposure); 0342 imageData->getRecordValue("GAIN", gain); 0343 imageData->getRecordValue("PIXSIZE1", pixel_size); 0344 imageData->getRecordValue("FOCALLEN", focal_length); 0345 imageData->getRecordValue("APTDIA", aperture); 0346 0347 auto stretchParameters = view->getStretchParams(); 0348 0349 // Account for binning 0350 const double binned_pixel = pixel_size.toDouble() * xbin.toInt(); 0351 // Send everything as strings 0352 QJsonObject metadata = 0353 { 0354 {"resolution", resolution}, 0355 {"size", sizeBytes}, 0356 {"channels", imageData->channels()}, 0357 {"mean", imageData->getAverageMean()}, 0358 {"median", imageData->getAverageMedian()}, 0359 {"stddev", imageData->getAverageStdDev()}, 0360 {"min", imageData->getMin()}, 0361 {"max", imageData->getMax()}, 0362 {"bin", QString("%1x%2").arg(xbin.toString(), ybin.toString())}, 0363 {"bpp", QString::number(imageData->bpp())}, 0364 {"uuid", m_UUID}, 0365 {"exposure", exposure.toString()}, 0366 {"focal_length", focal_length.toString()}, 0367 {"aperture", aperture.toString()}, 0368 {"gain", gain.toString()}, 0369 {"pixel_size", QString::number(binned_pixel, 'f', 4)}, 0370 {"shadows", stretchParameters.grey_red.shadows}, 0371 {"midtones", stretchParameters.grey_red.midtones}, 0372 {"highlights", stretchParameters.grey_red.highlights}, 0373 {"hasWCS", imageData->hasWCS()}, 0374 {"hfr", imageData->getHFR()}, 0375 {"ext", ext} 0376 }; 0377 0378 // First METADATA_PACKET bytes of the binary data is always allocated 0379 // to the metadata 0380 // the rest to the image data. 0381 QByteArray meta = QJsonDocument(metadata).toJson(QJsonDocument::Compact); 0382 meta = meta.leftJustified(METADATA_PACKET, 0); 0383 buffer.write(meta); 0384 0385 auto fastImage = (!Options::ekosLiveHighBandwidth() || m_UUID[0] == "+"); 0386 auto scaleWidth = fastImage ? HB_IMAGE_WIDTH / 2 : HB_IMAGE_WIDTH; 0387 0388 // For low bandwidth images 0389 // Except for dark frames +D 0390 QPixmap scaledImage = view->getDisplayPixmap().width() > scaleWidth ? 0391 view->getDisplayPixmap().scaledToWidth(scaleWidth, fastImage ? Qt::FastTransformation : Qt::SmoothTransformation) : 0392 view->getDisplayPixmap(); 0393 scaledImage.save(&buffer, ext.toLatin1().constData(), HB_IMAGE_QUALITY); 0394 0395 buffer.close(); 0396 0397 emit newImage(jpegData); 0398 } 0399 0400 /////////////////////////////////////////////////////////////////////////////////////////// 0401 /// 0402 /////////////////////////////////////////////////////////////////////////////////////////// 0403 void Media::sendUpdatedFrame(const QSharedPointer<FITSView> &view) 0404 { 0405 QString ext = "jpg"; 0406 QByteArray jpegData; 0407 QBuffer buffer(&jpegData); 0408 buffer.open(QIODevice::WriteOnly); 0409 0410 const QSharedPointer<FITSData> imageData = view->imageData(); 0411 0412 if (!imageData) 0413 return; 0414 0415 const int32_t width = imageData->width(); 0416 const int32_t height = imageData->height(); 0417 QString resolution = QString("%1x%2").arg(width).arg(height); 0418 QString sizeBytes = KFormat().formatByteSize(imageData->size()); 0419 QVariant xbin(1), ybin(1), exposure(0), focal_length(0), gain(0), pixel_size(0), aperture(0); 0420 imageData->getRecordValue("XBINNING", xbin); 0421 imageData->getRecordValue("YBINNING", ybin); 0422 imageData->getRecordValue("EXPTIME", exposure); 0423 imageData->getRecordValue("GAIN", gain); 0424 imageData->getRecordValue("PIXSIZE1", pixel_size); 0425 imageData->getRecordValue("FOCALLEN", focal_length); 0426 imageData->getRecordValue("APTDIA", aperture); 0427 0428 // Account for binning 0429 const double binned_pixel = pixel_size.toDouble() * xbin.toInt(); 0430 // Send everything as strings 0431 QJsonObject metadata = 0432 { 0433 {"resolution", resolution}, 0434 {"size", sizeBytes}, 0435 {"channels", imageData->channels()}, 0436 {"mean", imageData->getAverageMean()}, 0437 {"median", imageData->getAverageMedian()}, 0438 {"stddev", imageData->getAverageStdDev()}, 0439 {"bin", QString("%1x%2").arg(xbin.toString()).arg(ybin.toString())}, 0440 {"bpp", QString::number(imageData->bpp())}, 0441 {"uuid", "+A"}, 0442 {"exposure", exposure.toString()}, 0443 {"focal_length", focal_length.toString()}, 0444 {"aperture", aperture.toString()}, 0445 {"gain", gain.toString()}, 0446 {"pixel_size", QString::number(binned_pixel, 'f', 4)}, 0447 {"ext", ext} 0448 }; 0449 0450 // First METADATA_PACKET bytes of the binary data is always allocated 0451 // to the metadata 0452 // the rest to the image data. 0453 QByteArray meta = QJsonDocument(metadata).toJson(QJsonDocument::Compact); 0454 meta = meta.leftJustified(METADATA_PACKET, 0); 0455 buffer.write(meta); 0456 0457 // For low bandwidth images 0458 QPixmap scaledImage; 0459 // Align images 0460 if (correctionVector.isNull() == false) 0461 { 0462 scaledImage = view->getDisplayPixmap(); 0463 const double currentZoom = view->getCurrentZoom(); 0464 const double normalizedZoom = currentZoom / 100; 0465 // If zoom level is not 100%, then scale. 0466 if (fabs(normalizedZoom - 1) > 0.001) 0467 scaledImage = view->getDisplayPixmap().scaledToWidth(view->zoomedWidth()); 0468 else 0469 scaledImage = view->getDisplayPixmap(); 0470 // as we factor in the zoom level, we adjust center and length accordingly 0471 QPointF center = 0.5 * correctionVector.p1() * normalizedZoom + 0.5 * correctionVector.p2() * normalizedZoom; 0472 uint32_t length = qMax(correctionVector.length() / normalizedZoom, 100 / normalizedZoom); 0473 0474 QRect boundingRectable; 0475 boundingRectable.setSize(QSize(length * 2, length * 2)); 0476 QPoint topLeft = (center - QPointF(length, length)).toPoint(); 0477 boundingRectable.moveTo(topLeft); 0478 boundingRectable = boundingRectable.intersected(scaledImage.rect()); 0479 0480 emit newBoundingRect(boundingRectable, scaledImage.size(), currentZoom); 0481 0482 scaledImage = scaledImage.copy(boundingRectable); 0483 } 0484 else 0485 { 0486 scaledImage = view->getDisplayPixmap().width() > HB_IMAGE_WIDTH / 2 ? 0487 view->getDisplayPixmap().scaledToWidth(HB_IMAGE_WIDTH / 2, Qt::FastTransformation) : 0488 view->getDisplayPixmap(); 0489 emit newBoundingRect(QRect(), QSize(), 100); 0490 } 0491 0492 scaledImage.save(&buffer, ext.toLatin1().constData(), HB_IMAGE_QUALITY); 0493 buffer.close(); 0494 emit newImage(jpegData); 0495 } 0496 0497 /////////////////////////////////////////////////////////////////////////////////////////// 0498 /// 0499 /////////////////////////////////////////////////////////////////////////////////////////// 0500 void Media::sendVideoFrame(const QSharedPointer<QImage> &frame) 0501 { 0502 if (Options::ekosLiveImageTransfer() == false || m_sendBlobs == false || !frame) 0503 return; 0504 0505 int32_t width = Options::ekosLiveHighBandwidth() ? HB_VIDEO_WIDTH : HB_VIDEO_WIDTH / 2; 0506 QByteArray image; 0507 QBuffer buffer(&image); 0508 buffer.open(QIODevice::WriteOnly); 0509 0510 QImage videoImage = (frame->width() > width) ? frame->scaledToWidth(width) : *frame; 0511 0512 QString resolution = QString("%1x%2").arg(videoImage.width()).arg(videoImage.height()); 0513 0514 // First METADATA_PACKET bytes of the binary data is always allocated 0515 // to the metadata 0516 // the rest to the image data. 0517 QJsonObject metadata = 0518 { 0519 {"resolution", resolution}, 0520 {"ext", "jpg"} 0521 }; 0522 QByteArray meta = QJsonDocument(metadata).toJson(QJsonDocument::Compact); 0523 meta = meta.leftJustified(METADATA_PACKET, 0); 0524 buffer.write(meta); 0525 0526 QImageWriter writer; 0527 writer.setDevice(&buffer); 0528 writer.setFormat("JPG"); 0529 writer.setCompression(6); 0530 writer.write(videoImage); 0531 buffer.close(); 0532 0533 for (auto &nodeManager : m_NodeManagers) 0534 { 0535 nodeManager->media()->sendBinaryMessage(image); 0536 } 0537 } 0538 0539 /////////////////////////////////////////////////////////////////////////////////////////// 0540 /// 0541 /////////////////////////////////////////////////////////////////////////////////////////// 0542 void Media::registerCameras() 0543 { 0544 static const QRegularExpression re("[-{}]"); 0545 for(auto &oneDevice : INDIListener::devices()) 0546 { 0547 auto camera = oneDevice->getCamera(); 0548 if (camera) 0549 { 0550 camera->disconnect(this); 0551 connect(camera, &ISD::Camera::newVideoFrame, this, &Media::sendVideoFrame); 0552 connect(camera, &ISD::Camera::newView, this, [this](const QSharedPointer<FITSView> &view) 0553 { 0554 QString uuid = QUuid::createUuid().toString(); 0555 uuid = uuid.remove(re); 0556 sendView(view, uuid); 0557 }); 0558 } 0559 } 0560 } 0561 0562 void Media::resetPolarView() 0563 { 0564 this->correctionVector = QLineF(); 0565 m_Manager->alignModule()->zoomAlignView(); 0566 } 0567 0568 void Media::uploadMetadata(const QByteArray &metadata) 0569 { 0570 for (auto &nodeManager : m_NodeManagers) 0571 { 0572 nodeManager->media()->sendTextMessage(metadata); 0573 } 0574 } 0575 0576 void Media::uploadImage(const QByteArray &image) 0577 { 0578 for (auto &nodeManager : m_NodeManagers) 0579 { 0580 nodeManager->media()->sendBinaryMessage(image); 0581 } 0582 } 0583 0584 void Media::processNewBLOB(IBLOB * bp) 0585 { 0586 Q_UNUSED(bp) 0587 } 0588 0589 void Media::sendModuleFrame(const QSharedPointer<FITSView> &view) 0590 { 0591 if (Options::ekosLiveImageTransfer() == false || m_sendBlobs == false) 0592 return; 0593 0594 if (qobject_cast<Ekos::Align*>(sender()) == m_Manager->alignModule()) 0595 sendView(view, "+A"); 0596 else if (qobject_cast<Ekos::Focus*>(sender()) == m_Manager->focusModule()) 0597 sendView(view, "+F"); 0598 else if (qobject_cast<Ekos::Guide*>(sender()) == m_Manager->guideModule()) 0599 sendView(view, "+G"); 0600 else if (qobject_cast<Ekos::DarkLibrary*>(sender()) == Ekos::DarkLibrary::Instance()) 0601 sendView(view, "+D"); 0602 } 0603 }