277 lines
9.2 KiB
C++
277 lines
9.2 KiB
C++
#include "camera.h"
|
|
|
|
std::shared_ptr<spdlog::logger> Camera::log = nullptr;
|
|
QHash<QString, Camera*> 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<STRU_PRESET_CONFIG> Camera::presets() {
|
|
unsigned int count = 255;
|
|
std::vector<STRU_PRESET_CONFIG> 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<STRU_NDSWN_RULE> 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<int>(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<Camera*>(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<STRU_NDSWN_TEMPERATURE_UPLOAD> 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<STRU_NDSWN_TEMPERATURE_UPLOAD>, items));
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Camera::onTemperatureDataReady(int presetNo, QVector<STRU_NDSWN_TEMPERATURE_UPLOAD> 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<qint32>(reinterpret_cast<const uchar*>(buffer.constData())) == bufferSize && buffer.at(512) == 'q') return InfraredType::NEW_STATE_GRID;
|
|
|
|
return InfraredType::UNKNOWN;
|
|
}
|