private NotificationMessage InnerPublish(
OperationContext context,
out UInt32Collection availableSequenceNumbers,
out bool moreNotifications)
{
// check session.
VerifySession(context);
// TraceState("PUBLISH");
// check if a keep alive should be sent if there is no data.
bool keepAliveIfNoData = (m_keepAliveCounter >= m_maxKeepAliveCount);
availableSequenceNumbers = new UInt32Collection();
moreNotifications = false;
if (m_lastSentMessage < m_sentMessages.Count)
{
// return the available sequence numbers.
for (int ii = 0; ii <= m_lastSentMessage && ii < m_sentMessages.Count; ii++)
{
availableSequenceNumbers.Add(m_sentMessages[ii].SequenceNumber);
}
moreNotifications = m_waitingForPublish = m_lastSentMessage < m_sentMessages.Count-1;
// TraceState("PUBLISH QUEUED MESSAGE");
return m_sentMessages[m_lastSentMessage++];
}
List<NotificationMessage> messages = new List<NotificationMessage>();
if (m_publishingEnabled)
{
DateTime start1 = DateTime.UtcNow;
// collect notifications to publish.
Queue<EventFieldList> events = new Queue<EventFieldList>();
Queue<MonitoredItemNotification> datachanges = new Queue<MonitoredItemNotification>();
Queue<DiagnosticInfo> datachangeDiagnostics = new Queue<DiagnosticInfo>();
// check for monitored items that are ready to publish.
LinkedListNode<IMonitoredItem> current = m_itemsToPublish.First;
while (current != null)
{
LinkedListNode<IMonitoredItem> next = current.Next;
IMonitoredItem monitoredItem = current.Value;
if ((monitoredItem.MonitoredItemType & MonitoredItemTypeMask.DataChange) != 0)
{
((IDataChangeMonitoredItem)monitoredItem).Publish(context, datachanges, datachangeDiagnostics);
}
else
{
((IEventMonitoredItem)monitoredItem).Publish(context, events);
}
// add back to list to check.
m_itemsToPublish.Remove(current);
m_itemsToCheck.AddLast(current);
// check there are enough notifications for a message.
if (m_maxNotificationsPerPublish > 0 && events.Count + datachanges.Count > m_maxNotificationsPerPublish)
{
// construct message.
int notificationCount;
int eventCount = events.Count;
int dataChangeCount = datachanges.Count;
NotificationMessage message = ConstructMessage(
events,
datachanges,
datachangeDiagnostics,
out notificationCount);
// add to list of messages to send.
messages.Add(message);
lock (m_diagnostics)
{
m_diagnostics.DataChangeNotificationsCount += (uint)(dataChangeCount - datachanges.Count);
m_diagnostics.EventNotificationsCount += (uint)(eventCount - events.Count);
m_diagnostics.NotificationsCount += (uint)notificationCount;
}
}
current = next;
}
// pubish the remaining notifications.
while (events.Count + datachanges.Count > 0)
{
// construct message.
int notificationCount;
int eventCount = events.Count;
int dataChangeCount = datachanges.Count;
NotificationMessage message = ConstructMessage(
events,
datachanges,
datachangeDiagnostics,
out notificationCount);
// add to list of messages to send.
messages.Add(message);
lock (m_diagnostics)
{
m_diagnostics.DataChangeNotificationsCount += (uint)(dataChangeCount - datachanges.Count);
m_diagnostics.EventNotificationsCount += (uint)(eventCount - events.Count);
m_diagnostics.NotificationsCount += (uint)notificationCount;
}
}
// check for missing notifications.
if (!keepAliveIfNoData && messages.Count == 0)
{
Utils.Trace(
(int)Utils.TraceMasks.Error,
"Oops! MonitoredItems queued but no notifications availabled.");
m_waitingForPublish = false;
return null;
}
DateTime end1 = DateTime.UtcNow;
double delta1 = ((double)(end1.Ticks-start1.Ticks))/TimeSpan.TicksPerMillisecond;
if (delta1 > 200)
{
TraceState(Utils.Format("PUBLISHING DELAY ({0}ms)", delta1));
}
}
if (messages.Count == 0)
{
// create a keep alive message.
NotificationMessage message = new NotificationMessage();
// use the sequence number for the next message.
message.SequenceNumber = (uint)m_sequenceNumber;
message.PublishTime = DateTime.UtcNow;
// return the available sequence numbers.
for (int ii = 0; ii <= m_lastSentMessage && ii < m_sentMessages.Count; ii++)
{
availableSequenceNumbers.Add(m_sentMessages[ii].SequenceNumber);
}
// TraceState("PUBLISH KEEPALIVE");
return message;
}
// have to drop unsent messages if out of queue space.
if (messages.Count > m_maxMessageCount)
{
Utils.Trace(
"WARNING: QUEUE OVERFLOW. Dropping {2} Messages. Increase MaxMessageQueueSize. SubId={0}, MaxMessageQueueSize={1}",
m_id,
m_maxMessageCount,
messages.Count - (int)m_maxMessageCount);
messages.RemoveRange(0, messages.Count - (int)m_maxMessageCount);
}
// remove old messages if queue is full.
if (m_sentMessages.Count > m_maxMessageCount - messages.Count)
{
lock (m_diagnostics)
{
m_diagnostics.UnacknowledgedMessageCount += (uint)messages.Count;
}
if (m_maxMessageCount <= messages.Count)
{
m_sentMessages.Clear();
}
else
{
m_sentMessages.RemoveRange(0, messages.Count);
}
}
// save new message
m_lastSentMessage = m_sentMessages.Count;
m_sentMessages.AddRange(messages);
// check if there are more notifications to send.
moreNotifications = m_waitingForPublish = messages.Count > 1;
// return the available sequence numbers.
for (int ii = 0; ii <= m_lastSentMessage && ii < m_sentMessages.Count; ii++)
{
availableSequenceNumbers.Add(m_sentMessages[ii].SequenceNumber);
}
// TraceState("PUBLISH NEW MESSAGE");
return m_sentMessages[m_lastSentMessage++];
}