// // FrequencyOperation.swift // Logger4 // // Created by Kilian Hofmann on 15.06.17. // Copyright © 2017 Kilian Hofmann. All rights reserved. // import Cocoa class FrequencyOperation: Operation { // DOCSIS 3.0 Upstream Power Limits as per Specification March 5. 2015 let upstreamOne = ["qpsk": "62.18", "8qam": "58.21", "16qam": "58.21", "32qam": "57", "64qam": "57", "128qam": "-"] let upstreamTwo = ["qpsk": "59.18", "8qam": "55.21", "16qam": "55.21", "32qam": "54", "64qam": "54", "128qam": "-"] let upstreamThreeOrFour = ["qpsk": "56.18", "8qam": "52.21", "16qam": "52.21", "32qam": "51", "64qam": "51", "128qam": "-"] let modulationAdjust: NSDictionary = ["qpsk": "-1.18", "8qam": "-0.21", "16qam": "-0.21", "32qam" : "0", "64qam": "0", "128qam": "0.05"] // XML Parser let parser: XMLDictionaryParser = XMLDictionaryParser() // Data handling var downstream: Data? var upstream: Data? var upstreamBool: Bool = false var downstreamBool: Bool = false var dir: NSString = "" override init() { } override func main() { // Get date for folder structure let time : Date = Date() let start: Array = (NSApp.delegate as! AppDelegate).justDate.string(from: time).components(separatedBy: ".") // File manager let fileManager: FileManager = FileManager.default // Make all relevant directories if not present pthread_mutex_lock(&((NSApp.delegate as! AppDelegate).lock)) dir = NSString(format: "~/KDLog/%@.docsisplist2/%@/%@/", start[2], start[1], start[0]) do { try fileManager.createDirectory(atPath: dir.expandingTildeInPath, withIntermediateDirectories: true, attributes: nil) } catch let error as NSError{ NSLog("ERROR ON SUBDIRECTORY CREATION: \(error.localizedDescription)") } pthread_mutex_unlock(&((NSApp.delegate as! AppDelegate).lock)) // URL Requests let requestDownstream: URLRequest = URLRequest.init(url: URL.init(string: UserDefaults.standard.string(forKey: "downstream")!)!, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 4) let requestUpstream: URLRequest = URLRequest.init(url: URL.init(string: UserDefaults.standard.string(forKey: "upstream")!)!, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 4) // Tasks let taskDownstream: URLSessionDataTask = (NSApp.delegate as! AppDelegate).urlSession.dataTask(with: requestDownstream){data, response, error in pthread_mutex_lock(&(NSApp.delegate as! AppDelegate).lock) self.downstream = data self.downstreamBool = true self.handleData(date: time) pthread_mutex_unlock(&(NSApp.delegate as! AppDelegate).lock) } let taskUpstream: URLSessionDataTask = (NSApp.delegate as! AppDelegate).urlSession.dataTask(with: requestUpstream){data, response, error in pthread_mutex_lock(&(NSApp.delegate as! AppDelegate).lock) self.upstream = data self.upstreamBool = true self.handleData(date: time) pthread_mutex_unlock(&(NSApp.delegate as! AppDelegate).lock) } // Start taskUpstream.resume() taskDownstream.resume() } func handleData(date: Date) { // Both returned successfully if upstream != nil && downstream != nil { // Parse data upstreamBool = false downstreamBool = false let dictionaryUpstream: Dictionary? = parser.dictionary(with: upstream) let dictionaryDownstream: Dictionary? = parser.dictionary(with: downstream) upstream = nil downstream = nil // Tracking padding adding var filesModified: [String] = [] // Upstream is valid if dictionaryUpstream != nil{ var entry: String = "" var file: NSString = "" // Grab channels let channels: AnyObject = (dictionaryUpstream!["upstream_channel"]) as AnyObject // We have multiple channels if channels is [Dictionary] { // Simplify let channels: [Dictionary] = (channels as! [Dictionary]) // Step through channels for index in 0.. = channels[index] // We do not have an error if channel["upstream_modulation"] != nil && (channel["frequency"] as! String) != "status_error" { // Grab power rating in dBmV let powerInt: Double? = Double(channel["power_level"] as! String) // We have valid power rating if powerInt != nil { // Grab modulation data let modulation: NSObject = ((channel["upstream_modulation"]!)["modulation"])! as! NSObject // Grab power adjustment see DOCSIS 3.0 Specification for more Information) var adjust: String = "" if modulation.isKind(of: NSArray.self) { adjust = modulationAdjust[(((modulation as! NSArray)[0] as! NSDictionary)["mod_type"]!) as! String]! as! String } else{ adjust = modulationAdjust[((modulation as! NSDictionary)["mod_type"]!) as! String]! as! String } let adjustInt: Double = Double(adjust)! // Add timestamp and power to file entry entry = "\((NSApp.delegate as! AppDelegate).justTime.string(from: date));\(String(powerInt! + adjustInt));" // Add threshold to file entry switch channels.count { case 2: entry += "\(upstreamTwo[modulationAdjust.allKeys(for: adjust)[0] as! String]!)" case 3: entry += "\(upstreamThreeOrFour[modulationAdjust.allKeys(for: adjust)[0] as! String]!)" case 4: entry += "\(upstreamThreeOrFour[modulationAdjust.allKeys(for: adjust)[0] as! String]!)" default: entry += "NaN" } // Add ranging status entry += ";\(channel["ranging_status"] as! String)\n" // Add file name to path file = dir.appending("Upstream \(channel["frequency"]!).csv") as NSString // Track modification filesModified.append("Upstream \(channel["frequency"]!).csv") // Write to file logFreq(header: ";Power Level;Threshold;Ranging Status\n", file: file, entry: entry) } } } } // We have a single channel else if channels is Dictionary { // Grab channel let channel: Dictionary = channels as! Dictionary // We do not have an error if channel["upstream_modulation"] != nil && channel["frequency"] as! String != "status_error" { // Grab power rating in dBmV let powerInt: Double? = Double(channel["power_level"] as! String) // We have valid power rating if powerInt != nil { // Grab modulation data let modulation: NSObject = ((channel["upstream_modulation"]!)["modulation"])! as! NSObject // Grab power adjustment see DOCSIS 3.0 Specification for more Information) var adjust: String = "" if modulation.isKind(of: NSArray.self) { adjust = modulationAdjust[(((modulation as! NSArray)[0] as! NSDictionary)["mod_type"]!) as! String]! as! String } else{ adjust = modulationAdjust[((modulation as! NSDictionary)["mod_type"]!) as! String]! as! String } let adjustInt: Double = Double(adjust)! // Add data to entry entry = "\((NSApp.delegate as! AppDelegate).justTime.string(from: date));\(String(powerInt! + adjustInt));\(upstreamOne[modulationAdjust.allKeys(for: adjust)[0] as! String]!);\(channel["ranging_status"] as! String)\n" // Add file name to path file = dir.appending("Upstream \(channel["frequency"]!).csv") as NSString // Track modifiction filesModified.append("Upstream \(channel["frequency"]!).csv") // Write to file logFreq(header: ";Power Level;Threshold;Ranging Status\n", file: file, entry: entry) } } } } // Downstream is valid if dictionaryDownstream != nil { var entry: String = "" var file: NSString = "" // Grab channels let channels: AnyObject = (dictionaryDownstream!["downstream_channel"]) as AnyObject // We have multiple channels if channels is [Dictionary] { // Simplify let channels: [Dictionary] = channels as! [Dictionary] // Step through channels for index in 0.. = channels[index] // We do not have an error if channel["frequency"] as! String != "status_error" { // Add data to entry entry = "\((NSApp.delegate as! AppDelegate).justTime.string(from: date));\(channel["power_level"]!);\(channel["snr"]!)\n" // Add file name to path file = dir.appending("Downstream \(channel["frequency"]!).csv") as NSString // Tracking modification filesModified.append("Downstream \(channel["frequency"]!).csv") // Write to file logFreq(header: ";Power;SNR\n", file: file, entry: entry) } } } // We have a single channel else if channels is Dictionary { // Grab channel let channel: Dictionary = channels as! Dictionary // We do not have an error if channel["frequency"] as! String != "status_error" { // Add data to entry entry = "\((NSApp.delegate as! AppDelegate).justTime.string(from: date));\(channel["power_level"]!);\(channel["snr"]!)\n" // Add file name to path file = dir.appending("Downstream \(channel["frequency"]!).csv") as NSString // Tracking modification filesModified.append("Downstream \(channel["frequency"]!).csv") // Write to file logFreq(header: ";Power;SNR\n", file: file, entry: entry) } } // Update if not modified let fileManager = FileManager.default // Get all filenames in directory let enumerator:FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: dir.expandingTildeInPath)! // Step through files for element in enumerator { // Check if modified if !filesModified.contains(element as! String) && (element as! String).contains(".csv") { logDummie(dir: dir, element: element as! String, date: date) } } } // Update if both nil (no connection to modem possible) else if (upstreamBool && downstreamBool) { upstreamBool = false downstreamBool = false let fileManager = FileManager.default // Get all filenames in directory let enumerator:FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: dir.expandingTildeInPath)! // Step through files for element in enumerator { logDummie(dir: dir, element: element as! String, date: date) } } } } func logFreq(header: String, file: NSString, entry: String) { let fileManager = FileManager.default if !fileManager.fileExists(atPath: file.expandingTildeInPath) { do { try header.write(toFile: file.expandingTildeInPath, atomically: true, encoding: .utf8) } catch { NSLog("WRITE TO FILE \(file.expandingTildeInPath) FAILED\n"); return } } let fileH: FileHandle? = FileHandle(forUpdatingAtPath: file.expandingTildeInPath) fileH?.seekToEndOfFile() let data: Data = (entry as NSString).data(using: String.Encoding.utf8.rawValue)! fileH?.write(data) fileH?.closeFile() } func logDummie(dir: NSString, element: String, date: Date) { // Get file let file: FileHandle? = FileHandle(forUpdatingAtPath: dir.expandingTildeInPath.appending(element)) if file != nil { // Write appropriate null entry to file file?.seekToEndOfFile(); var data: Data?; if element.contains("Upstream") { data = ("\((NSApp.delegate as! AppDelegate).justTime.string(from: date));-;-;-\n" as NSString).data(using: String.Encoding.utf8.rawValue) } else { data = ("\((NSApp.delegate as! AppDelegate).justTime.string(from: date));-;-\n" as NSString).data(using: String.Encoding.utf8.rawValue) } file?.write(data!) file?.closeFile() } } }