DOCSIS-Toolkit/Logger4/FrequencyOperation.swift

276 lines
15 KiB
Swift

//
// 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<String> = (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<String,AnyObject>] {
// Simplify
let channels: [Dictionary<String,AnyObject>] = (channels as! [Dictionary<String,AnyObject>])
// Step through channels
for index in 0..<channels.count {
// Grab channel
let channel: Dictionary<String,AnyObject> = 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<String,AnyObject> {
// Grab channel
let channel: Dictionary<String,AnyObject> = channels as! Dictionary<String,AnyObject>
// 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<String,AnyObject>] {
// Simplify
let channels: [Dictionary<String, AnyObject>] = channels as! [Dictionary<String, AnyObject>]
// Step through channels
for index in 0..<channels.count {
// Grab channel
let channel: Dictionary<String,AnyObject> = 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<String,AnyObject> {
// Grab channel
let channel: Dictionary<String,AnyObject> = channels as! Dictionary<String,AnyObject>
// 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()
}
}
}