/
StateManager.cs
252 lines (210 loc) · 11.2 KB
/
StateManager.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Text;
using System.Threading;
using System.Reflection;
using System.Xml;
using LothianProductions.Data;
using LothianProductions.Util;
using LothianProductions.Util.Settings;
using LothianProductions.VoIP.Monitor;
using LothianProductions.VoIP.Monitor.Impl;
using LothianProductions.VoIP.State;
namespace LothianProductions.VoIP {
public delegate void StateUpdateHandler( IDeviceMonitor monitor, StateUpdateEventArgs e );
public class StateManager {
protected static readonly StateManager mInstance = new StateManager();
protected Dictionary<Call, CallRecord> mCalls = new Dictionary<Call, CallRecord>();
protected Dictionary<Object, Dictionary<String, PropertyBehaviour>> mStateProperties = new Dictionary<Object, Dictionary<String, PropertyBehaviour>>();
public static StateManager Instance() {
return mInstance;
}
public event StateUpdateHandler StateUpdate;
protected Dictionary<DeviceMonitorControl, Thread> mDeviceMonitorControls = new Dictionary<DeviceMonitorControl, Thread>();
protected StateManager() {
// Initialize device monitors, have to give each its own thread.
ReloadDeviceStateMonitors();
}
public void ReloadDeviceStateMonitors() {
lock (mDeviceMonitorControls) {
// Stop currently running threads.
foreach( DeviceMonitorControl control in mDeviceMonitorControls.Keys )
mDeviceMonitorControls[ control ].Abort();
mDeviceMonitorControls.Clear();
NameValueCollection config = (NameValueCollection) ConfigurationManager.GetSection( "hvoipm/deviceStateMonitors" );
if( config == null )
throw new MissingAppSettingsException( "The section \"hvoipm/deviceStateMonitors\" was not present in the application configuration." );
foreach( String key in config.Keys ) {
Type type = Type.GetType( config[ key ], true );
IDeviceMonitor monitor = (IDeviceMonitor) type.InvokeMember(
"",
BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.Instance,
null,
null,
new String[] { key }
);
DeviceMonitorControl control = new DeviceMonitorControl( monitor );
Thread thread = new Thread( new ThreadStart( control.Run ) );
mDeviceMonitorControls.Add( control, thread );
thread.Start();
}
Logger.Instance().Log( "Loaded " + mDeviceMonitorControls.Count + " device state monitor(s)" );
}
}
public ICollection<DeviceMonitorControl> DeviceMonitorControls {
get{ return mDeviceMonitorControls.Keys; }
}
public void DeviceUpdated( IDeviceMonitor monitor, IList<DevicePropertyChange> deviceChanges,
IList<LinePropertyChange> lineChanges, IList<CallPropertyChange> callChanges ) {
// FIXME this is very inefficient
List<PropertyChange> changes = new List<PropertyChange>();
foreach( PropertyChange change in deviceChanges )
changes.Add( change );
foreach( PropertyChange change in lineChanges )
changes.Add( change );
foreach( PropertyChange change in callChanges )
changes.Add( change );
foreach( PropertyChange change in changes )
// Note that we log changing to or from any given logging criteria
if( LookupPropertyChangeBehaviour( change.Underlying, change.Property, change.ChangedTo ).Log ||
LookupPropertyChangeBehaviour( change.Underlying, change.Property, change.ChangedFrom ).Log )
Logger.Instance().Log( change.Underlying.GetType().Name + " property " + change.Property + " has changed from " + change.ChangedFrom + " to " + change.ChangedTo );
// Logging happens here:
foreach( CallPropertyChange change in callChanges ) {
if( change.Property == DeviceMonitor.PROPERTY_CALL_ACTIVITY ) {
if ( change.ChangedTo == Activity.Connected.ToString() ) {
CallRecord call = new CallRecord( monitor.GetDeviceState(), GetLine( change.Call ), change.Call, DateTime.Now, new DateTime() );
// Sanity check in case somehow call is already there
if ( mCalls.ContainsKey( change.Call ) ) {
mCalls.Remove( change.Call );
Logger.Instance().Log("Call #" + change.Call.Name + " had to be removed from the call list - did the previous call fail?");
}
mCalls.Add( change.Call, call );
} else if ( change.ChangedFrom == Activity.Connected.ToString() ) {
CallRecord call = mCalls[change.Call];
call.EndTime = DateTime.Now;
CallLogger.Instance().Log(call);
mCalls.Remove( change.Call );
}
}
if (change.Call.Activity != Activity.IdleDisconnected) {
if ( mCalls.ContainsKey( change.Call ) ) {
CallRecord call = mCalls[change.Call];
call.Call = change.Call;
call.Line = GetLine(change.Call);
mCalls[change.Call] = call;
}
}
}
if( StateUpdate != null )
StateUpdate( monitor, new StateUpdateEventArgs( deviceChanges, lineChanges, callChanges ) );
}
public PropertyChangeBehaviour LookupPropertyChangeBehaviour( Object state, String property, String criteria ) {
if( LookupPropertyBehaviour( state, property ).PropertyChangeBehaviours.ContainsKey( criteria ) )
return LookupPropertyBehaviour( state, property ).PropertyChangeBehaviours[ criteria ];
if( LookupPropertyBehaviour( state, property ).PropertyChangeBehaviours.ContainsKey( "" ) )
return LookupPropertyBehaviour( state, property ).PropertyChangeBehaviours[ "" ];
throw new ConfigurationErrorsException( "Could not find behaviours suitable for criteria \"" + criteria + "\" for property \"" + property + "\" in application configuration." );
}
public PropertyBehaviour LookupPropertyBehaviour( Object state, String property ) {
// Behaviour not set yet.
if( ! mStateProperties.ContainsKey( state ) )
mStateProperties.Add( state, new Dictionary<String, PropertyBehaviour>() );
Dictionary<String, PropertyBehaviour> propertyBehaviours = mStateProperties[ state ];
if( ! propertyBehaviours.ContainsKey( property ) )
propertyBehaviours.Add( property, GetPropertyBehaviourFromXml( state.GetType().Name, property ) );
return propertyBehaviours[ property ];
}
protected PropertyBehaviour GetPropertyBehaviourFromXml( String stateType, String property ) {
XmlNode node = (XmlNode) ConfigurationManager.GetSection( "hvoipm/behaviours" );
if( node == null )
throw new ConfigurationErrorsException( "Could not find behaviours section in application configuration." );
XmlNode propertyNode = node.SelectSingleNode( "property[@stateType='" + stateType + "' and @property='" + property + "']" );
if( propertyNode == null )
throw new ConfigurationErrorsException( "Could not find behaviour description for " + stateType + "." + property + "\" in application configuration." );
Dictionary<String, PropertyChangeBehaviour> changeBehaviours = new Dictionary<String, PropertyChangeBehaviour>();
PropertyBehaviour behaviour = new PropertyBehaviour(
propertyNode.Attributes[ "label" ].Value,
changeBehaviours
);
foreach( XmlNode behaviourNode in propertyNode.ChildNodes )
// Just add a single behaviour entry if it's a catch-all criteria
// or if there's only one.
if( behaviourNode.Attributes[ "warningCriteria" ] == null || ! behaviourNode.Attributes[ "warningCriteria" ].Value.Contains( "," ) )
changeBehaviours.Add(
( behaviourNode.Attributes[ "warningCriteria" ] == null ? "" : behaviourNode.Attributes[ "warningCriteria" ].Value ),
new PropertyChangeBehaviour(
Boolean.Parse( behaviourNode.Attributes[ "showBubble" ].Value ),
behaviourNode.Attributes[ "bubbleText" ].Value,
Boolean.Parse( behaviourNode.Attributes[ "systemTrayWarning" ].Value ),
Boolean.Parse( behaviourNode.Attributes[ "showApplication" ].Value ),
behaviourNode.Attributes[ "externalProcess" ].Value,
( behaviourNode.Attributes[ "warningCriteria" ] == null ? "" : behaviourNode.Attributes[ "warningCriteria" ].Value ),
Boolean.Parse( behaviourNode.Attributes[ "log" ].Value )
)
);
else
foreach( String criteria in behaviourNode.Attributes[ "warningCriteria" ].Value.Split( ',' ) )
changeBehaviours.Add(
criteria,
new PropertyChangeBehaviour(
Boolean.Parse( behaviourNode.Attributes[ "showBubble" ].Value ),
behaviourNode.Attributes[ "bubbleText" ].Value,
Boolean.Parse( behaviourNode.Attributes[ "systemTrayWarning" ].Value ),
Boolean.Parse( behaviourNode.Attributes[ "showApplication" ].Value ),
behaviourNode.Attributes[ "externalProcess" ].Value,
criteria,
Boolean.Parse( behaviourNode.Attributes[ "log" ].Value )
)
);
return behaviour;
}
// Helper functions for linking states.
public IDeviceMonitor GetMonitor( Device deviceState ) {
foreach( DeviceMonitorControl control in DeviceMonitorControls )
if( control.DeviceMonitor.GetDeviceState() == deviceState )
return control.DeviceMonitor;
throw new DomainObjectNotFoundException( "Couldn't find a monitor owning the specified device." );
}
public IDeviceMonitor GetMonitor( Line lineState ) {
foreach( DeviceMonitorControl control in DeviceMonitorControls )
foreach( Line line in control.DeviceMonitor.GetDeviceState().Lines )
if( line == lineState )
return control.DeviceMonitor;
throw new DomainObjectNotFoundException( "Couldn't find a monitor owning the specified line." );
}
public IDeviceMonitor GetMonitor( Call callState ) {
foreach( DeviceMonitorControl control in DeviceMonitorControls )
foreach( Line line in control.DeviceMonitor.GetDeviceState().Lines )
foreach( Call call in line.Calls )
if( call == callState )
return control.DeviceMonitor;
throw new DomainObjectNotFoundException( "Couldn't find a monitor owning the specified call." );
}
public Device GetDevice( Line lineState ) {
foreach( DeviceMonitorControl control in DeviceMonitorControls )
foreach( Line line in control.DeviceMonitor.GetDeviceState().Lines )
if( line == lineState )
return control.DeviceMonitor.GetDeviceState();
throw new DomainObjectNotFoundException( "Couldn't find a device owning the specified line." );
}
public Device GetDevice( Call callState ) {
foreach( DeviceMonitorControl control in DeviceMonitorControls )
foreach( Line line in control.DeviceMonitor.GetDeviceState().Lines )
foreach( Call call in line.Calls )
if( call == callState )
return control.DeviceMonitor.GetDeviceState();
throw new DomainObjectNotFoundException( "Couldn't find a device owning the specified call." );
}
public Line GetLine( Call callState ) {
foreach( DeviceMonitorControl control in DeviceMonitorControls )
foreach( Line line in control.DeviceMonitor.GetDeviceState().Lines )
foreach( Call call in line.Calls )
if( call == callState )
return line;
throw new DomainObjectNotFoundException( "Couldn't find a line owning the specified call." );
}
}
}