try to handle nightly debug port resets
[auerswald-callnotifier.git] / src / callnotifier / CallMonitor.php
1 <?php
2 namespace callnotifier;
3
4 /**
5  * Watches EDSS1 messages for calls. Keeps an internal call state
6  * and notifies loggers of incoming and finished calls.
7  *
8  * Notifications:
9  * - startingCall
10  * - finishedCall
11  */
12 class CallMonitor
13 {
14     protected $currentCalls = array();
15
16     /**
17      * Array of objects that are able to load details for a call
18      *
19      * @var array
20      */
21     protected $detaillers = array();
22
23     public function __construct($config, $log)
24     {
25         $this->config = $config;
26         $this->log = $log;
27     }
28
29     public function addDetailler(CallMonitor_Detailler $detailler)
30     {
31         $this->detaillers[] = $detailler;
32     }
33
34     public function handle(EDSS1_Message $msg)
35     {
36         $callId = $msg->callRef;
37         if (!array_key_exists($callId, $this->currentCalls)) {
38             $this->handleNew($callId, $msg);
39         } else {
40             $this->handleExisting($callId, $msg);
41         }
42     }
43
44     protected function handleNew($callId, EDSS1_Message $msg)
45     {
46         if ($msg->type != EDSS1_Message::SETUP) {
47             return;
48         }
49         $this->currentCalls[$callId] = new CallMonitor_Call();
50         $this->handleSetup($callId, $msg);
51     }
52
53
54     protected function handleSetup($callId, EDSS1_Message $msg)
55     {
56         $call = $this->currentCalls[$callId];
57         $call->start = time();
58         if ($msg->tei == 127) {
59             $call->type = CallMonitor_Call::INCOMING;
60         } else {
61             $call->type = CallMonitor_Call::OUTGOING;
62         }
63
64         $this->handleParams($msg, $call, $callId);
65     }
66
67
68     protected function handleExisting($callId, EDSS1_Message $msg)
69     {
70         $call = $this->currentCalls[$callId];
71
72         switch ($msg->type) {
73         case EDSS1_Message::INFORMATION:
74             $this->handleParams($msg, $call, $callId);
75             break;
76         case EDSS1_Message::ALERTING:
77             if ($call->type == CallMonitor_Call::OUTGOING) {
78                 /**
79                  * There may be two alerts: One from the telephone to the
80                  * switchboard, and one from the switchboard to the target.
81                  *
82                  * The alert from the switchboard to the target call is
83                  * sent first, so we can remove the call from the telephone
84                  * to the switchboard.
85                  */
86                 $bFound = false;
87                 foreach ($this->currentCalls as $otherCallId => $otherCall) {
88                     if ($otherCallId != $callId && $otherCall->to == $call->to) {
89                         $bFound = true;
90                         break;
91                     }
92                 }
93                 if ($bFound) {
94                     unset($this->currentCalls[$otherCallId]);
95                 }
96             }
97             $this->loadCallDetails($call);
98             $this->log->log('startingCall', array('call' => $call));
99             break;
100
101         case EDSS1_Message::RELEASE:
102         case EDSS1_Message::RELEASE_COMPLETE:
103             $call->end = time();
104             //we need to load details here because they might not have been
105             //loaded yet, e.g. for calls to MSNs that have no phones.
106             $this->loadCallDetails($call);
107             $this->log->log('finishedCall', array('call' => $call));
108             unset($this->currentCalls[$callId]);
109             break;
110         }
111     }
112
113     protected function handleParams($msg, $call, $callId)
114     {
115         foreach ($msg->parameters as $param) {
116             switch ($param->type) {
117             case EDSS1_Parameter::CALLING_PARTY_NUMBER:
118                 $call->from = $this->getFullNumber(
119                     $param->number, $param->numberType
120                 );
121                 break;
122             case EDSS1_Parameter::CALLED_PARTY_NUMBER:
123                 $call->to = $this->getFullNumber(
124                     $param->number, $param->numberType
125                 );
126                 if ($call->type == CallMonitor_Call::INCOMING
127                     && $param->numberType != EDSS1_Parameter_Names::NUMBER_SUBSCRIBER
128                 ) {
129                     //only keep incoming calls that arrive at the switchboard,
130                     // not the ones from the switchboard to the telephones
131                     unset($this->currentCalls[$callId]);
132                 }
133                 break;
134             case EDSS1_Parameter::KEYPAD:
135                 if ($call->to === null) {
136                     $call->to = $param->data;
137                 }
138             }
139         }
140     }
141
142
143     protected function getFullNumber($number, $type)
144     {
145         if ($type == EDSS1_Parameter_Names::NUMBER_NATIONAL) {
146             return '0' . $number;
147         } else if ($type == EDSS1_Parameter_Names::NUMBER_INTERNATIONAL) {
148             return '+' . $number;
149         }
150         return $number;
151     }
152
153     /**
154      * Load details for a call, e.g. the name of the calling person
155      * or the area
156      *
157      * @return void
158      */
159     protected function loadCallDetails($call)
160     {
161         foreach ($this->detaillers as $detailler) {
162             $detailler->loadCallDetails($call);
163         }
164     }
165 }
166
167 ?>