#include "camera.h" std::shared_ptr Camera::log = nullptr; QHash Camera::instances; QMutex Camera::mutex; Camera* Camera::of(CameraInfo cameraInfo) { return of(cameraInfo.getCameraType(), cameraInfo.getIP(), cameraInfo.getPort(), cameraInfo.getUsername(), cameraInfo.getPassword()); } Camera* Camera::of(CameraType cameraType, QString ip, unsigned int port, QString username, QString password) { QString key = cameraKey(cameraType, ip); QMutexLocker locker(&mutex); if (instances.contains(key)) { return instances[key]; } Camera* camera = new Camera(cameraType, ip, port, username, password); instances[key] = camera; return camera; } Camera::Camera(CameraType cameraType, QString ip, unsigned int port, QString username, QString password) { this->cameraType = cameraType; this->ip = ip; this->port = port; this->username = username; this->password = password; } bool Camera::login() { if (connected) return true; hk_net_dev_init(&ptr); connected = hk_net_dev_login(ptr, 0, ip.toUtf8().constData(), port, nullptr, nullptr, username.toUtf8().constData(), password.toUtf8().constData()); if (!connected) { log->error("{} |> 无法登录设备", toString()); hk_net_dev_uninit(&ptr); ptr = nullptr; return false; } log->info("{} |> 设备登录", toString()); return connected; } void Camera::logout() { log->info("{} |> 设备登出", toString()); if (connected) { hk_net_dev_logout(ptr); hk_net_dev_uninit(&ptr); ptr = nullptr; connected = false; } QString key = cameraKey(cameraType, ip); QMutexLocker locker(&mutex); delete instances.take(key); } void Camera::startRealtimeTemperatureCallback() { hk_net_dev_stop_realtime_temperature_v20(ptr); hk_net_dev_set_realtime_temperature_callback_v20(ptr, Camera::temperatureCallback, this); hk_net_dev_start_realtime_temperature_v20(ptr); log->info("{} |> 开启温度回调", toString()); this->realtiming = true; } void Camera::stopRealtimeTemperatureCallback() { hk_net_dev_stop_realtime_temperature_v20(ptr); log->info("{} |> 停止温度回调", toString()); this->realtiming = false; } void Camera::attach(struct mg_connection *conn) { this->conn = conn; } void Camera::unattach() { this->conn = NULL; } std::vector Camera::presets() { unsigned int count = 255; std::vector presets(count); hk_net_dev_ptz_get_preset_list(ptr, presets.data(), &count); presets.erase(std::remove_if(presets.begin(), presets.end(), [](const STRU_PRESET_CONFIG& preset) { return !(preset.ui_enable == 1 || preset.ui_especial == 1);}), presets.end()); return presets; } bool Camera::addPreset(QJsonObject packet) { QJsonObject preset = packet["preset"].toObject(); int presetNo = preset["no"].toInt(); float radiance = preset["radiance"].toInt() / 100.0f; float distance = preset["distance"].toInt() / 100.0f;; QJsonArray anas = preset["anas"].toArray(); bool ok = hk_net_dev_ptz_set_preset(ptr, presetNo); if (ok) { if (anas.isEmpty()) { ok = hk_net_dev_set_preset_temperature_rules_v20(ptr, presetNo, NULL, 0); } else { std::vector rules(anas.size()); for (int i = 0; i < anas.size(); ++i) { QJsonValue ana = anas.at(i); QJsonArray coords = ana["coords"].toArray(); auto& rule = rules[i]; rule.i_rule_type = ana["shape"].toInt(); rule.i_id = ana["index"].toInt(); rule.f_emiss = radiance; rule.f_dist = distance; rule.pt_start.fX = coords.at(0)["x"].toDouble(); rule.pt_start.fY = coords.at(0)["y"].toDouble(); if (rule.i_rule_type != 0) { rule.pt_end.fX = coords.at(1)["x"].toDouble(); rule.pt_end.fY = coords.at(1)["y"].toDouble(); } } ok = hk_net_dev_set_preset_temperature_rules_v20(ptr, presetNo, rules.data(), static_cast(rules.size())); } } log->info("{} |> 保存预置位: {}, {}", toString(), presetNo, ok); return ok; } QString Camera::getViCameraIP() { char ip[16] = {0}; bool ok = hk_net_dev_get_association_dev_info(ptr, ip); return ok ? QString::fromUtf8(ip) : QString(); } CaptureData Camera::capture(bool append) { CaptureData result; bool ok = false; QTemporaryFile file(QDir::tempPath() + "/camera_XXXXXXXX.jpg"); if (file.open()) { QString filename = file.fileName(); file.close(); QByteArray f = filename.toUtf8(); if (cameraType == CameraType::IR) { ok = hk_net_dev_capture_ir_jpg(ptr, f.data()); if (ok && append) { QByteArray date = QDateTime::currentDateTime().toString("yyyyMMddHHmmss").toUtf8(); ok = hk_net_dev_append_temperature_to_national_grid_image(ptr, date.data(), f.data()); } } else { ok = hk_net_dev_capture_vi_jpg(ptr, f.data()); } if (ok) { QFile image(filename); if (image.open(QIODevice::ReadOnly)) { QByteArray buffer = image.readAll(); result.image = buffer; image.close(); if (result.image.isEmpty()) { ok = false; } if (ok) { if (cameraType == CameraType::IR) { InfraredType infraredType = getInfraredType(buffer); ok = infraredType != InfraredType::UNKNOWN; } } } else { ok = false; } } } log->info("{} |> 采集{}, {}", toString(), cameraType == CameraType::IR ? "红外" : "可将光", ok); result.ok = ok; return result; } bool Camera::temperatureCallback(int, int presetNo, STRU_NDSWN_TEMPERATURE_UPLOAD* data, unsigned int len, void* user) { if (user) { Camera* camera = static_cast(user); qint64 now = QDateTime::currentMSecsSinceEpoch(); camera->heartbeat = now; // 保活回调 // 发送心跳 if (now - camera->lastTickAt >= 30000) { camera->broadcast(PING); camera->lastTickAt = now; } // 不在预置位上 if (presetNo == 0) { camera->stayIn = 0; camera->broadcast(PRESET_NO_ZERO); return true; } if (camera->stayIn != presetNo) { camera->log->info("{} |> 预置点切换: {}->{}", camera->toString(), camera->stayIn, presetNo); camera->stayIn = presetNo; camera->stayInAt = now; } if (now - camera->stayInAt >= 15000 && data && len > 0) { QVector items; items.resize(len); memcpy(items.data(), data, len * sizeof(STRU_NDSWN_TEMPERATURE_UPLOAD)); QMetaObject::invokeMethod(camera, "onTemperatureDataReady", Qt::QueuedConnection, Q_ARG(int, presetNo),Q_ARG(QVector, items)); } } return true; } void Camera::onTemperatureDataReady(int presetNo, QVector items) { QJsonArray shapes; for (const auto& item : items) { QJsonObject max({{"value", item.f_max}, {"x", item.st_max_pt.fX}, {"y", item.st_max_pt.fY}}); QJsonObject min({{"value", item.f_min}, {"x", item.st_min_pt.fX}, {"y", item.st_min_pt.fY}}); QJsonObject avg({{"value", item.f_avg}, {"x", QJsonValue::Null}, {"y", QJsonValue::Null}}); QJsonObject shape({{"ruleType", item.i_rule_type}, {"id", item.i_id}, {"max", max}, {"min", min}, {"avg", avg}}); shapes.append(shape); } broadcast(QJsonDocument(QJsonObject{{"event", "temperature"},{"payload", QJsonObject{{"presetNo", presetNo}, {"shapes", shapes}}}}).toJson(QJsonDocument::Compact)); } QString Camera::cameraKey(CameraType cameraType, QString ip) { return QString("%1_%2").arg(cameraType == CameraType::IR ? "IR" : "VI").arg(ip); } void Camera::broadcast(QByteArray byteArray) { QtConcurrent::run([this, byteArray]()-> void { if (conn) { mg_ws_send(conn, byteArray.constData(), byteArray.length(), WEBSOCKET_OP_TEXT); } }); } InfraredType Camera::getInfraredType(QByteArray buffer) { static constexpr char HeiKa[] = "\x93\x5c\x80\x39\xf6\xac\x4c\x4b\xbc\xb0\xb2\x50\xf7\xe1\xcd\x55"; static constexpr char StateGrid[] = "\x37\x66\x07\x1a\x12\x3a\x4c\x9f\xa9\x5d\x21\xd2\xda\x7d\x26\xbc"; const int bufferSize = buffer.size(); if (bufferSize < 16) return InfraredType::UNKNOWN; const char* const tail = buffer.constData() + bufferSize - 16; if (std::memcmp(tail, HeiKa, 16) == 0) return InfraredType::HEIKA; if (std::memcmp(tail, StateGrid, 16) == 0) return InfraredType::STATE_GRID; if (bufferSize < 512) return InfraredType::UNKNOWN; if (qFromLittleEndian(reinterpret_cast(buffer.constData())) == bufferSize && buffer.at(512) == 'q') return InfraredType::NEW_STATE_GRID; return InfraredType::UNKNOWN; }