mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-10-03 16:05:24 -04:00
Object::save() returns the error string on failure and '' on success, so `if ($manufacturer->save())` took the success branch only when the save had failed. ManufacturerId and ModelId were therefore assigned from an unsaved object and left unset whenever the save actually worked. Invert the test, capture the error string, and log it. Model.pm needs ZoneMinder::Logger imported for Error(). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
667 lines
18 KiB
Perl
667 lines
18 KiB
Perl
# ==========================================================================
|
|
#
|
|
# ZoneMinder Monitor Module, $Date$, $Revision$
|
|
# Copyright (C) 2001-2008 Philip Coombes
|
|
#
|
|
# This program 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 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program 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 for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
#
|
|
# ==========================================================================
|
|
#
|
|
# This module contains the common definitions and functions used by the rest
|
|
# of the ZoneMinder scripts
|
|
#
|
|
package ZoneMinder::Monitor;
|
|
|
|
use 5.006;
|
|
use strict;
|
|
use warnings;
|
|
use Time::HiRes qw(usleep);
|
|
|
|
require ZoneMinder::Base;
|
|
require ZoneMinder::Object;
|
|
require ZoneMinder::Storage;
|
|
require ZoneMinder::Server;
|
|
require ZoneMinder::Memory;
|
|
require ZoneMinder::Monitor_Status;
|
|
require ZoneMinder::Event_Summary;
|
|
require ZoneMinder::Zone;
|
|
require ZoneMinder::Manufacturer;
|
|
require ZoneMinder::Model;
|
|
use ZoneMinder::Logger qw(:all);
|
|
use ZoneMinder::Database qw(:all);
|
|
|
|
use parent qw(ZoneMinder::Object);
|
|
|
|
use vars qw/ $table $primary_key %fields $serial %defaults $debug/;
|
|
$debug = 0;
|
|
$table = 'Monitors';
|
|
$serial = $primary_key = 'Id';
|
|
%fields = map { $_ => $_ } qw(
|
|
Id
|
|
Name
|
|
Deleted
|
|
Notes
|
|
ServerId
|
|
StorageId
|
|
ManufacturerId
|
|
ModelId
|
|
Type
|
|
Capturing
|
|
Analysing
|
|
AnalysisSource
|
|
AnalysisImage
|
|
Recording
|
|
Decoding
|
|
JanusEnabled
|
|
JanusAudioEnabled
|
|
Janus_Profile_Override
|
|
Restream
|
|
RTSP_User
|
|
Janus_RTSP_Session_Timeout
|
|
LinkedMonitors
|
|
Triggers
|
|
EventStartCommand
|
|
EventEndCommand
|
|
ONVIF_URL
|
|
ONVIF_Username
|
|
ONVIF_Password
|
|
ONVIF_Options
|
|
ONVIF_Event_Listener
|
|
ONVIF_Alarm_Text
|
|
use_Amcrest_API
|
|
Device
|
|
Channel
|
|
Format
|
|
V4LMultiBuffer
|
|
V4LCapturesPerFrame
|
|
Protocol
|
|
Method
|
|
Host
|
|
Port
|
|
SubPath
|
|
Path
|
|
SecondPath
|
|
Options
|
|
User
|
|
Pass
|
|
Width
|
|
Height
|
|
Colours
|
|
Palette
|
|
Orientation
|
|
Deinterlacing
|
|
Decoder
|
|
DecoderHWAccelName
|
|
DecoderHWAccelDevice
|
|
SaveJPEGs
|
|
VideoWriter
|
|
OutputCodec
|
|
OutputContainer
|
|
EncoderParameters
|
|
RecordAudio
|
|
RecordingSource
|
|
RTSPDescribe
|
|
Brightness
|
|
Contrast
|
|
Hue
|
|
Colour
|
|
EventPrefix
|
|
LabelFormat
|
|
LabelX
|
|
LabelY
|
|
LabelSize
|
|
ImageBufferCount
|
|
MaxImageBufferCount
|
|
WarmupCount
|
|
PreEventCount
|
|
PostEventCount
|
|
StreamReplayBuffer
|
|
AlarmFrameCount
|
|
SectionLength
|
|
SectionLengthWarn
|
|
MinSectionLength
|
|
MotionFrameSkip
|
|
AnalysisFPSLimit
|
|
AnalysisUpdateDelay
|
|
MaxFPS
|
|
AlarmMaxFPS
|
|
FPSReportInterval
|
|
RefBlendPerc
|
|
AlarmRefBlendPerc
|
|
Controllable
|
|
ControlId
|
|
ControlDevice
|
|
ControlAddress
|
|
AutoStopTimeout
|
|
TrackMotion
|
|
TrackDelay
|
|
ReturnLocation
|
|
ReturnDelay
|
|
ModectDuringPTZ
|
|
DefaultRate
|
|
DefaultScale
|
|
DefaultCodec
|
|
SignalCheckPoints
|
|
SignalCheckColour
|
|
WebColour
|
|
Exif
|
|
Sequence
|
|
ZoneCount
|
|
Refresh
|
|
DefaultCodec
|
|
Latitude
|
|
Longitude
|
|
RTSPServer
|
|
RTSPStreamName
|
|
Importance
|
|
MQTT_Enabled
|
|
MQTT_Subscriptions
|
|
);
|
|
|
|
# These have subroutines
|
|
$fields{Manufacturer} = undef;
|
|
$fields{manufacturer} = undef;
|
|
$fields{Model} = undef;
|
|
$fields{model} = undef;
|
|
|
|
%defaults = (
|
|
Name => q`'Monitor'`,
|
|
Deleted => 0,
|
|
ServerId => 0,
|
|
StorageId => 0,
|
|
ManufacturerId => undef,
|
|
ModelId => undef,
|
|
Type => q`'Ffmpeg'`,
|
|
Capturing => q`'Always'`,
|
|
Analysing => q`'Always'`,
|
|
AnalysisSource => q`'Primary'`,
|
|
AnalysisImage => q`'FullColour'`,
|
|
Recording => q`'Always'`,
|
|
Decoding => q`'Always'`,
|
|
JanusEnabled => 0,
|
|
JanusAudioEnabled => 0,
|
|
Janus_Profile_Override => q`''`,
|
|
Restream => 0,
|
|
RTSP_User => undef,
|
|
Janus_RTSP_Session_Timeout => 0,
|
|
LinkedMonitors => undef,
|
|
Triggers => '',
|
|
EventStartCommand => '',
|
|
EventEndCommand => '',
|
|
ONVIF_URL => q`''`,
|
|
ONVIF_Username => q`''`,
|
|
ONVIF_Password => q`''`,
|
|
ONVIF_Options => q`''`,
|
|
ONVIF_Event_Listener => 0,
|
|
ONVIF_Alarm_Text => q`'MotionAlarm'`,
|
|
use_Amcrest_API => 0,
|
|
Device => '',
|
|
Channel => 0,
|
|
Format => 0,
|
|
V4LMultiBuffer => undef,
|
|
V4LCapturesPerFrame => 1,
|
|
Protocol => undef,
|
|
Method => '',
|
|
Host => undef,
|
|
Port => '',
|
|
SubPath => '',
|
|
Path => undef,
|
|
SecondPath => undef,
|
|
Options => undef,
|
|
User => undef,
|
|
Pass => undef,
|
|
Width => 0,
|
|
Height => 0,
|
|
Colours => 4,
|
|
Palette => 0,
|
|
Orientation => q`'ROTATE_0'`,
|
|
Deinterlacing => 0,
|
|
Decoder => undef,
|
|
DecoderHWAccelName => undef,
|
|
DecoderHWAccelDevice => undef,
|
|
SaveJPEGs => 3,
|
|
VideoWriter => 0,
|
|
OutputCodec => 0,
|
|
OutputContainer => undef,
|
|
EncoderParameters => '',
|
|
RecordAudio=>0,
|
|
RecordingSource => q`'Primary'`,
|
|
RTSPDescribe=>0,
|
|
Brightness => -1,
|
|
Contrast => -1,
|
|
Hue => -1,
|
|
Colour => -1,
|
|
EventPrefix => q`'Event-'`,
|
|
LabelFormat => '',
|
|
LabelX => 0,
|
|
LabelY => 0,
|
|
LabelSize => 1,
|
|
ImageBufferCount => 20,
|
|
MaxImageBufferCount => 121,
|
|
WarmupCount => 0,
|
|
PreEventCount => 5,
|
|
PostEventCount => 5,
|
|
StreamReplayBuffer => 0,
|
|
AlarmFrameCount => 1,
|
|
SectionLength => 600,
|
|
SectionLengthWarn => 1,
|
|
MinSectionLength => 10,
|
|
MotionFrameSkip => 0,
|
|
AnalysisFPSLimit => undef,
|
|
AnalysisUpdateDelay => 0,
|
|
MaxFPS => undef,
|
|
AlarmMaxFPS => undef,
|
|
FPSReportInterval => 100,
|
|
RefBlendPerc => 6,
|
|
AlarmRefBlendPerc => 6,
|
|
Controllable => 0,
|
|
ControlId => undef,
|
|
ControlDevice => undef,
|
|
ControlAddress => undef,
|
|
AutoStopTimeout => undef,
|
|
TrackMotion => 0,
|
|
TrackDelay => undef,
|
|
ReturnLocation => -1,
|
|
ReturnDelay => undef,
|
|
ModectDuringPTZ => 0,
|
|
DefaultRate => 100,
|
|
DefaultScale => 100,
|
|
DefaultCodec => q`'auto'`,
|
|
SignalCheckPoints => 0,
|
|
SignalCheckColour => q`'#0000BE'`,
|
|
WebColour => q`'#ff0000'`,
|
|
Exif => 0,
|
|
Sequence => undef,
|
|
ZoneCount => 0,
|
|
Refresh => undef,
|
|
DefaultCodec => q`'auto'`,
|
|
Latitude => undef,
|
|
Longitude => undef,
|
|
RTSPStreamName => '',
|
|
RTSPServer => 0,
|
|
Importance => q`'Normal'`,
|
|
MQTT_Enabled => 0,
|
|
MQTT_Subscriptions => q`''`,
|
|
);
|
|
|
|
use constant CAPTURING_NONE => 1;
|
|
use constant CAPTURING_ONDEMAND => 2;
|
|
use constant CAPTURING_ALWAYS => 3;
|
|
use constant ANALYSING_ALWAYS => 2;
|
|
use constant ANALYSING_NONE => 1;
|
|
|
|
sub save {
|
|
my $self = shift;
|
|
|
|
my $manufacturer = $self->Manufacturer();
|
|
my $model = $self->Model();
|
|
|
|
if ($manufacturer->Name() and !$manufacturer->Id()) {
|
|
# save() returns the error string on failure, '' on success.
|
|
if (my $error = $manufacturer->save()) {
|
|
Error('Failed saving Manufacturer '.$manufacturer->Name().": $error");
|
|
} else {
|
|
$$self{ManufacturerId} = $manufacturer->Id();
|
|
if ($model->Name()) {
|
|
$model->ManufacturerId($$self{ManufacturerId});
|
|
}
|
|
}
|
|
}
|
|
if ($model->Name() and !$model->Id()) {
|
|
if (my $error = $model->save()) {
|
|
Error('Failed saving Model '.$model->Name().": $error");
|
|
} else {
|
|
$$self{ModelId} = $model->Id();
|
|
}
|
|
}
|
|
|
|
my $error = $self->SUPER::save(@_);
|
|
return $error;
|
|
} # end sub save
|
|
|
|
sub Server {
|
|
return new ZoneMinder::Server( $_[0]{ServerId} );
|
|
} # end sub Server
|
|
|
|
sub Storage {
|
|
return new ZoneMinder::Storage( $_[0]{StorageId} );
|
|
} # end sub Storage
|
|
|
|
sub Zones {
|
|
if (! exists $_[0]{Zones}) {
|
|
$_[0]{Zones} = [ $_[0]{Id} ? ZoneMinder::Zone->find(MonitorId=>$_[0]{Id}) : () ];
|
|
}
|
|
return wantarray ? @{$_[0]{Zones}} : $_[0]{Zones};
|
|
}
|
|
|
|
sub control {
|
|
my $monitor = shift;
|
|
my $command = shift;
|
|
my $process = shift;
|
|
|
|
if ($monitor->{Type} eq 'Local') {
|
|
if (!defined $monitor->{Device} or $monitor->{Device} !~ /^\/dev\/[\w\/.\-]+$/) {
|
|
Error("Invalid device path rejected: $monitor->{Device}");
|
|
return;
|
|
}
|
|
}
|
|
|
|
# Callers hold a Monitor object for a while before acting on it: zmwatch
|
|
# fetches its whole list at the top of a pass and then walks it, so a monitor
|
|
# deleted mid-pass is still restarted from that stale list. The web ui already
|
|
# stopped its zmc, so the restart resurrects a capture daemon for a monitor
|
|
# nothing will ever ask about again - an orphan zmc that survives until the
|
|
# next zmpkg restart. Re-read Deleted from the db before starting anything.
|
|
if (($command eq 'start' or $command eq 'restart') and $$monitor{Id}) {
|
|
my $row = zmDbFetchOne('SELECT `Deleted` FROM `Monitors` WHERE `Id`=?', $$monitor{Id});
|
|
if (!$row) {
|
|
Info("Not running $command for monitor $$monitor{Id}: no longer exists");
|
|
return;
|
|
}
|
|
if ($$row{Deleted}) {
|
|
Info("Not running $command for monitor $$monitor{Id}: has been deleted");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ($command eq 'stop') {
|
|
if ($process) {
|
|
ZoneMinder::General::runCommand("zmdc.pl stop $process -m $$monitor{Id}");
|
|
} else {
|
|
if ($monitor->{Type} eq 'Local') {
|
|
ZoneMinder::General::runCommand("zmdc.pl stop zmc -d '$monitor->{Device}'");
|
|
} else {
|
|
ZoneMinder::General::runCommand('zmdc.pl stop zmc -m '.$monitor->{Id});
|
|
}
|
|
}
|
|
} elsif ($command eq 'start') {
|
|
if ( $process ) {
|
|
ZoneMinder::General::runCommand("zmdc.pl start $process -m $$monitor{Id}");
|
|
} else {
|
|
if ($monitor->{Type} eq 'Local') {
|
|
ZoneMinder::General::runCommand("zmdc.pl start zmc -d '$monitor->{Device}'");
|
|
} else {
|
|
ZoneMinder::General::runCommand('zmdc.pl start zmc -m '.$monitor->{Id});
|
|
}
|
|
} # end if
|
|
} elsif ( $command eq 'restart' ) {
|
|
if ( $process ) {
|
|
ZoneMinder::General::runCommand("zmdc.pl restart $process -m $$monitor{Id}");
|
|
} else {
|
|
if ($monitor->{Type} eq 'Local') {
|
|
ZoneMinder::General::runCommand("zmdc.pl restart zmc -d '$monitor->{Device}'");
|
|
} else {
|
|
ZoneMinder::General::runCommand('zmdc.pl restart zmc -m '.$monitor->{Id});
|
|
}
|
|
}
|
|
}
|
|
} # end sub control
|
|
|
|
sub Status {
|
|
my $self = shift;
|
|
$$self{Status} = shift if @_;
|
|
if ( ! $$self{Status} ) {
|
|
$$self{Status} = ZoneMinder::Monitor_Status->find_one(MonitorId=>$$self{Id});
|
|
}
|
|
return $$self{Status};
|
|
}
|
|
|
|
sub Event_Summary {
|
|
my $self = shift;
|
|
$$self{Event_Summary} = shift if @_;
|
|
if ( ! $$self{Event_Summary} ) {
|
|
$$self{Event_Summary} = ZoneMinder::Event_Summary->find_one(MonitorId=>$$self{Id});
|
|
}
|
|
return $$self{Event_Summary};
|
|
}
|
|
|
|
sub connect {
|
|
my $self = shift;
|
|
if (!ZoneMinder::Memory::zmMemVerify($self)) {
|
|
$self->disconnect();
|
|
return undef;
|
|
}
|
|
return !undef;
|
|
}
|
|
|
|
sub disconnect {
|
|
my $self = shift;
|
|
ZoneMinder::Logger::Debug(4, "Disconnecting");
|
|
ZoneMinder::Memory::zmMemInvalidate($self); # Close our file handle to the zmc process we are about to end
|
|
}
|
|
|
|
sub suspendMotionDetection {
|
|
my $self = shift;
|
|
return 0 if ! ZoneMinder::Memory::zmMemVerify($self);
|
|
return if $$self{Capturing} eq 'None' or $$self{Analysing} eq 'None';
|
|
my $count = 50;
|
|
while ($count and
|
|
( ZoneMinder::Memory::zmMemRead($self, 'shared_data:analysing', 1) != ANALYSING_NONE)
|
|
) {
|
|
ZoneMinder::Logger::Debug(1, 'Suspending motion detection');
|
|
ZoneMinder::Memory::zmMonitorSuspend($self);
|
|
usleep(100000);
|
|
$count -= 1;
|
|
}
|
|
if (!$count) {
|
|
ZoneMinder::Logger::Error('Unable to suspend motion detection after 5 seconds.');
|
|
ZoneMinder::Memory::zmMemInvalidate($self); # Close our file handle to the zmc process we are about to end
|
|
} else {
|
|
ZoneMinder::Logger::Debug(1, 'shared_data:analysing='.ZoneMinder::Memory::zmMemRead($self, 'shared_data:analysing', 1));
|
|
}
|
|
}
|
|
|
|
sub resumeMotionDetection {
|
|
my $self = shift;
|
|
return 0 if ! ZoneMinder::Memory::zmMemVerify($self);
|
|
return if $$self{Capturing} eq 'None' or $$self{Analysing} eq 'None';
|
|
ZoneMinder::Logger::Debug(1, "capturing: $$self{Capturing} analysing: $$self{Analysing}");
|
|
my $count = 50;
|
|
while ($count and !ZoneMinder::Memory::zmMemRead($self, 'shared_data:analysing', 1)) {
|
|
ZoneMinder::Logger::Debug(1, 'Resuming motion detection');
|
|
ZoneMinder::Memory::zmMonitorResume($self);
|
|
usleep(100000);
|
|
$count -= 1;
|
|
}
|
|
if (!$count) {
|
|
ZoneMinder::Logger::Error('Unable to resume motion detection after 5 seconds.');
|
|
ZoneMinder::Memory::zmMemInvalidate($self); # Close our file handle to the zmc process we are about to end
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
sub Control {
|
|
my $self = shift;
|
|
if (!exists $$self{Control}) {
|
|
if ($$self{ControlId}) {
|
|
require ZoneMinder::Control;
|
|
my $Control = ZoneMinder::Control->find_one(Id=>$$self{ControlId});
|
|
if ($Control) {
|
|
my $Protocol = $$Control{Protocol};
|
|
|
|
if (!$Protocol) {
|
|
Debug("No protocol set in control $$Control{Id}, trying Name $$Control{Name}");
|
|
$Protocol = $$Control{Name};
|
|
}
|
|
require Module::Load::Conditional;
|
|
if (!Module::Load::Conditional::can_load(modules => {'ZoneMinder::Control::'.$Protocol => undef})) {
|
|
Error("Monitor $$self{Id} $$self{Name} Can't load ZoneMinder::Control::$Protocol\n$Module::Load::Conditional::ERROR");
|
|
return undef;
|
|
}
|
|
$Control = $Control->clone(); # Because this object is not per monitor specific
|
|
bless $Control, 'ZoneMinder::Control::'.$Protocol;
|
|
$$Control{MonitorId} = $$self{Id};
|
|
$$Control{Monitor} = $self->clone();
|
|
$$self{Control} = $Control;
|
|
} else {
|
|
Error("Unable to load control for control $$self{ControlId} for monitor $$self{Id}");
|
|
}
|
|
} else {
|
|
Info("No ControlId set in monitor $$self{Id}")
|
|
}
|
|
}
|
|
return $$self{Control};
|
|
}
|
|
|
|
sub zmcControl {
|
|
my $self = shift;
|
|
my $mode = shift;
|
|
|
|
if (!$$self{Id}) {
|
|
Warning('Attempt to control a monitor with no Id');
|
|
return;
|
|
}
|
|
my $zmcArgs = '';
|
|
|
|
if ((!$ZoneMinder::Config{ZM_SERVER_ID}) or ( $$self{ServerId} and ($ZoneMinder::Config{ZM_SERVER_ID}==$$self{ServerId}) )) {
|
|
if ($$self{Type} eq 'Local') {
|
|
if (!defined $$self{Device} or $$self{Device} !~ /^\/dev\/[\w\/.\-]+$/) {
|
|
Error("Invalid device path rejected: $$self{Device}");
|
|
return;
|
|
}
|
|
$zmcArgs .= "-d '$$self{Device}'";
|
|
} else {
|
|
$zmcArgs .= '-m '.$self->{Id};
|
|
}
|
|
|
|
`/usr/bin/zmdc.pl $mode zmc $zmcArgs`;
|
|
} elsif ($self->ServerId()) {
|
|
my $Server = $self->Server();
|
|
|
|
my $url = $Server->UrlToApi().'/monitors/daemonControl/'.$self->{'Id'}.'/'.$mode.'/zmc.json';
|
|
if ($ZoneMinder::Config{ZM_OPT_USE_AUTH}) {
|
|
if ($ZoneMinder::Config{ZM_AUTH_RELAY} eq 'hashed') {
|
|
#$url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS);
|
|
} elsif ($ZoneMinder::Config{ZM_AUTH_RELAY} == 'plain') {
|
|
#$url .= '?user='.$_SESSION['username'];
|
|
#$url .= '?pass='.$_SESSION['password'];
|
|
} else {
|
|
Error('Multi-Server requires AUTH_RELAY be either HASH or PLAIN');
|
|
return;
|
|
}
|
|
}
|
|
Debug('sending command to '.$url);
|
|
my $ua = LWP::UserAgent->new();
|
|
$ua->ssl_opts(verify_hostname => 0, SSL_verify_mode => 0x00);
|
|
my $response = $ua->get($url);
|
|
if (!$response->is_success()) {
|
|
Error("Unable to restart remote monitor: " .$response->status_line);
|
|
}
|
|
} # end if local or remote
|
|
} # end sub zmcControl
|
|
|
|
sub ImportanceNumber {
|
|
my $self = shift;
|
|
if ($$self{Importance} eq 'Not') {
|
|
return 2;
|
|
} elsif ($$self{Importance} eq 'Less') {
|
|
return 1;
|
|
} elsif ($$self{Importance} eq 'Normal') {
|
|
return 0;
|
|
}
|
|
Warning("Weird value for Importance $$self{Importance} in monitor $$self{Id} $$self{Name}");
|
|
return 0;
|
|
}
|
|
|
|
sub Manufacturer {
|
|
my $self = shift;
|
|
$$self{Manufacturer} = shift if @_;
|
|
if (!$$self{Manufacturer}) {
|
|
$$self{Manufacturer} = new ZoneMinder::Manufacturer($$self{ManufacturerId});
|
|
}
|
|
return $$self{Manufacturer};
|
|
}
|
|
|
|
sub manufacturer {
|
|
my $self = shift;
|
|
if (@_) {
|
|
my $new = shift;
|
|
$$self{Manufacturer} = ZoneMinder::Manufacturer->find_one(Name=>$new);
|
|
if (!$$self{Manufacturer}) {
|
|
$$self{Manufacturer} = new ZoneMinder::Manufacturer();
|
|
$$self{Manufacturer}->Name($new);
|
|
}
|
|
$$self{ManufacturerId} = $$self{Manufacturer}->Id();
|
|
}
|
|
if (!$$self{Manufacturer}) {
|
|
$$self{Manufacturer} = new ZoneMinder::Manufacturer($$self{ManufacturerId});
|
|
}
|
|
return $$self{manufacturer} = $$self{Manufacturer}->Name();
|
|
}
|
|
|
|
sub Model {
|
|
my $self = shift;
|
|
if (@_) {
|
|
$$self{Model} = shift;
|
|
if (!$$self{Model}) {
|
|
$$self{Model} = new ZoneMinder::Model($$self{ModelId});
|
|
}
|
|
$$self{ModelId} = $$self{Model}->Id();
|
|
}
|
|
if (!$$self{Model}) {
|
|
$$self{Model} = new ZoneMinder::Model($$self{ModelId});
|
|
}
|
|
return $$self{Model};
|
|
}
|
|
|
|
sub model {
|
|
my $self = shift;
|
|
|
|
if (@_) {
|
|
my $new = shift;
|
|
if ((!$$self{Model}) or ($new ne $$self{Model}->Name())) {
|
|
$$self{Model} = ZoneMinder::Model->find_one(Name=>$new);
|
|
if (!$$self{Model}) {
|
|
$$self{Model} = new ZoneMinder::Model();
|
|
$$self{Model}->Name($new);
|
|
}
|
|
$$self{ModelId} = $$self{Model}->Id();
|
|
}
|
|
}
|
|
if (!$$self{Model}) {
|
|
$$self{Model} = new ZoneMinder::Model($$self{ModelId});
|
|
}
|
|
return $$self{Model}->Name();
|
|
}
|
|
|
|
sub DESTROY {
|
|
ZoneMinder::Memory::zmMemInvalidate($_[0]) if ZoneMinder::Memory::zmMemKey($_[0]);
|
|
}
|
|
|
|
1;
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
ZoneMinder::Monitor - Perl Class for Monitors
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use ZoneMinder::Monitor;
|
|
|
|
=head1 AUTHOR
|
|
|
|
Isaac Connor, E<lt>isaac@zoneminder.comE<gt>
|
|
|
|
=head1 COPYRIGHT AND LICENSE
|
|
|
|
Copyright (C) 2001-2017 ZoneMinder LLC
|
|
|
|
Licensed under the GNU General Public License v2 or later; see the COPYING
|
|
file distributed with ZoneMinder for the full text.
|
|
|
|
|
|
=cut
|