diff --git a/ndncon/NCStatisticsWindowController.mm b/ndncon/NCStatisticsWindowController.mm index 96120f0..1fa5595 100755 --- a/ndncon/NCStatisticsWindowController.mm +++ b/ndncon/NCStatisticsWindowController.mm @@ -16,6 +16,7 @@ #import "NCStreamingController.h" #import "NSDictionary+NCAdditions.h" #import "NSObject+NCAdditions.h" +#import "NSArray+NCAdditions.h" #define INTEREST_AVERAGE_SIZE_BYTES 150 #define STAT_UPDATE_RATE 10 // per second @@ -30,6 +31,9 @@ @interface NCStatisticsStreamEntryValueTransformer : NSValueTransformer //****************************************************************************** @interface NCStatisticsWindowController () +{ + StatisticsStorage::StatRepo _prevStat; +} @property (nonatomic) NSString *username; @property (nonatomic) NSString *hubPrefix; @@ -43,6 +47,8 @@ @interface NCStatisticsWindowController () @property (nonatomic) NSTimer *statUpdateTimer; @property (nonatomic) NSString *activeThread; @property (weak) IBOutlet NSTextField *outRateEstimationLabel; +@property (nonatomic) unsigned int refreshRate; +@property (nonatomic) NSDictionary *rateMeters; @property (nonatomic) double nBytesPerSec, interestFrequency, segmentsFrequency; @property (nonatomic) double rttEstimation; @@ -87,6 +93,9 @@ -(id)init if (self) { + for (auto it:StatisticsStorage::IndicatorKeywords) + _prevStat[it.first] = 0.; + self.activeStreamPrefix = nil; self.streamPrefixLock = [[NSLock alloc] init]; self.chartsView.alphaValue = 0; @@ -307,6 +316,13 @@ -(void)onFetchedStreamsChanged:(NSNotification*)notification -(void)startStatUpdate:(NSTimeInterval)refreshRate { __weak NCStatisticsWindowController *weakSelf = self; + self.refreshRate = refreshRate; + self.rateMeters = @{ + @"incomingRate" : [[NSMutableArray alloc] initCircularArrayWithSize:refreshRate], + @"irate" : [[NSMutableArray alloc] initCircularArrayWithSize:refreshRate], + @"srate" : [[NSMutableArray alloc] initCircularArrayWithSize:refreshRate], + @"rtxRate" : [[NSMutableArray alloc] initCircularArrayWithSize:refreshRate] + }; self.statUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:(1./refreshRate) repeats:YES fireBlock:^(NSTimer *timer) { @@ -336,9 +352,12 @@ -(void)queryStatisticsForStream:(NSString*)aStreamPrefix std::string threadName = libHandle->getStreamThread(streamPrefix); self.activeThread = [NSString ncStringFromCString:threadName.c_str()]; - self.nBytesPerSec = stat[Indicator::InBitrateKbps]; - self.interestFrequency = stat[Indicator::InterestRate]; - self.segmentsFrequency = stat[Indicator::InRateSegments]; + [self.rateMeters[@"incomingRate"] push:@((stat[Indicator::RawBytesReceived] - _prevStat[Indicator::RawBytesReceived]))]; + self.nBytesPerSec = [self.rateMeters[@"incomingRate"] average]*self.refreshRate*8/1000; + [self.rateMeters[@"irate"] push:@((stat[Indicator::InterestsSentNum] - _prevStat[Indicator::InterestsSentNum]))]; + self.interestFrequency = [self.rateMeters[@"irate"] average]*(double)self.refreshRate; + [self.rateMeters[@"srate"] push:@((stat[Indicator::SegmentsReceivedNum] - _prevStat[Indicator::SegmentsReceivedNum]))]; + self.segmentsFrequency = [self.rateMeters[@"srate"] average]*(double)self.refreshRate; self.rttEstimation = stat[Indicator::RttEstimation]; self.jitterEstimationMs = stat[Indicator::BufferEstimatedSize]; self.jitterPlayableMs = stat[Indicator::BufferPlayableSize]; @@ -372,7 +391,7 @@ -(void)queryStatisticsForStream:(NSString*)aStreamPrefix self.avgSegNumKey = stat[Indicator::SegmentsKeyAvgNum]; self.avgSegNumParity = stat[Indicator::SegmentsDeltaParityAvgNum]; self.avgSegNumParityKey = stat[Indicator::SegmentsKeyParityAvgNum]; - self.rtxFreq = stat[Indicator::RtxFrequency]; + self.rtxFreq = [self.rateMeters[@"rtxRate"] average]*(double)self.refreshRate; self.nRtx = stat[Indicator::RtxNum]; self.nRebuffer = stat[Indicator::RebufferingsNum]; self.nRequested = stat[Indicator::RequestedNum]; @@ -386,7 +405,10 @@ -(void)queryStatisticsForStream:(NSString*)aStreamPrefix double allSkipped = self.nSkippedIncomplete+self.nSkippedInvalidGop+self.nSkippedNoKey; self.playbackEfficiency = round(allSkipped/(double)self.nPlayed*10000)/100; - self.outRateEstimationLabel.stringValue = [[NSNumber numberWithDouble:(INTEREST_AVERAGE_SIZE_BYTES*stat[Indicator::InterestRate]*8/1000.)] stringValue]; + self.outRateEstimationLabel.stringValue = [[NSNumber numberWithDouble:(INTEREST_AVERAGE_SIZE_BYTES*self.interestFrequency*8/1000.)] stringValue]; + + _prevStat = stat.getIndicators(); + } } diff --git a/ndncon/NSArray+NCAdditions.h b/ndncon/NSArray+NCAdditions.h index 9a1cfda..25267dc 100755 --- a/ndncon/NSArray+NCAdditions.h +++ b/ndncon/NSArray+NCAdditions.h @@ -26,3 +26,15 @@ -(NSDictionary*)threadWithName:(NSString*)threadName; @end + +@interface NSMutableArray (NCCircularArray) + +@property (nonatomic) unsigned int circularBufferSize; +@property (nonatomic) unsigned int currentIndex; + +-(instancetype)initCircularArrayWithSize:(unsigned int)size; + +-(void)push:(id)object; +-(float)average; + +@end diff --git a/ndncon/NSArray+NCAdditions.m b/ndncon/NSArray+NCAdditions.m index 490326c..64c8b87 100755 --- a/ndncon/NSArray+NCAdditions.m +++ b/ndncon/NSArray+NCAdditions.m @@ -6,6 +6,19 @@ // Copyright 2013-2015 Regents of the University of California. // +// from http://stackoverflow.com/questions/8733104/objective-c-property-instance-variable-in-category +#define CATEGORY_PROPERTY_GET(type, property) - (type) property { return objc_getAssociatedObject(self, @selector(property)); } +#define CATEGORY_PROPERTY_SET(type, property, setter) - (void) setter (type) property { objc_setAssociatedObject(self, @selector(property), property, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } +#define CATEGORY_PROPERTY_GET_SET(type, property, setter) CATEGORY_PROPERTY_GET(type, property) CATEGORY_PROPERTY_SET(type, property, setter) + +#define CATEGORY_PROPERTY_GET_NSNUMBER_PRIMITIVE(type, property, valueSelector) - (type) property { return [objc_getAssociatedObject(self, @selector(property)) valueSelector]; } +#define CATEGORY_PROPERTY_SET_NSNUMBER_PRIMITIVE(type, property, setter, numberSelector) - (void) setter (type) property { objc_setAssociatedObject(self, @selector(property), [NSNumber numberSelector: property], OBJC_ASSOCIATION_RETAIN_NONATOMIC); } + +#define CATEGORY_PROPERTY_GET_UINT(property) CATEGORY_PROPERTY_GET_NSNUMBER_PRIMITIVE(unsigned int, property, unsignedIntValue) +#define CATEGORY_PROPERTY_SET_UINT(property, setter) CATEGORY_PROPERTY_SET_NSNUMBER_PRIMITIVE(unsigned int, property, setter, numberWithUnsignedInt) +#define CATEGORY_PROPERTY_GET_SET_UINT(property, setter) CATEGORY_PROPERTY_GET_UINT(property) CATEGORY_PROPERTY_SET_UINT(property, setter) + +#import #import "NSArray+NCAdditions.h" #import "NSDictionary+NCAdditions.h" @@ -97,3 +110,35 @@ -(NSDictionary *)threadWithName:(NSString *)threadName } @end + +@implementation NSMutableArray (NCCircularArray) + +CATEGORY_PROPERTY_GET_SET_UINT(circularBufferSize, setCircularBufferSize:) +CATEGORY_PROPERTY_GET_SET_UINT(currentIndex, setCurrentIndex:) + +-(instancetype)initCircularArrayWithSize:(unsigned int)size +{ + self = [self init]; + self.circularBufferSize = size; + self.currentIndex = 0; + + return self; +} + +-(void)push:(id)object +{ + if (self.currentIndex == self.circularBufferSize) + self.currentIndex = 0; + + [self insertObject:object atIndex:self.currentIndex++]; + + if (self.count > self.circularBufferSize) + [self removeObjectAtIndex:self.currentIndex]; +} + +-(float)average +{ + return [[self valueForKeyPath:@"@avg.floatValue"] floatValue]; +} + +@end