Skip to content
Snippets Groups Projects
Select Git revision
  • 72b9c3b21c4c5fb938694ace70bb6b66268bb9a4
  • main default protected
2 results

StartingView.swift

Blame
  • StartingView.swift 15.44 KiB
    //
    //  startingView.swift
    //  sensorDataCollectorApp
    //
    //  Created by Miklósi Máté on 20/01/2024.
    
    import Foundation
    import SwiftUI
    import BackgroundTasks
    import CoreData
    import CoreMotion
    import UniformTypeIdentifiers
    import MobileCoreServices
    import SensorKit
    import Combine
    import Network
    
    struct StartingView: View {
        //----------SERVER CONNECTION SETTINGS AND VARS -----------------------------------------------
        @State private var isServerSettingsSheetPresented = false
        @StateObject private var manager = ServerCommunicationManager()
        @State private var iterationCount = 0
        @State var displayText = ""
        @State var first_open = true
        @State private var shouldStartRecording = false
        
        //----------TIMER SETUP SETTINGS AND VARS -----------------------------------------------------
        @StateObject var customTimer : CustomTimer
        private let backgroundTaskIdentifier = "hu.itk.ppke.backgroundTask"
        private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
        
        //----------MEASURMENT SETUP AND VARS ---------------------------------------------------------
        @State var demostart = false
        @State var sensorDetailSheetPresented = false
        @State var showingFilePicker = false
        @State var selectedFolder: URL?
        
        @State var dataColl = false
        @StateObject var sensor = SensorCollector(activeSensorList: [])
        
        let motionManager = CMMotionManager()
        let altimeter = CMAltimeter()
        
        @State var fileLocations: [URL] = []
        let activityManager = CMMotionActivityManager()
        @State private var wifiName: String = "Not Available"
        @State var debug = true
        
        var body: some View {
            NavigationView {
                VStack {
                    Spacer()
                    //----------------------------- DEBUG VALUE DISPAY ---------------------------------
                    Text("Notification list: \(manager.timerIntervalValues.map { "\($0)" }.joined(separator: ", "))").opacity(debug ? 0 : 1)
                    Text("Next notification in: \(customTimer.timerStringValue)").opacity(debug ? 0 : 1)
                    
                    Text("\(manager.serverResponse)")
                        .padding().opacity(debug ? 0 : 1)
                    
                    //----------------------------- STARTING - STOPPING THE ITERATION CYCL ---------------
                    Button("Set next"){
                        setNextMeasurement()
                    }.opacity(debug ? 0 : 1)
                    
                    Button("Start Iterations") {
                        setNextMeasurement()
                        sensor.startAllRecording(motionManager: motionManager, altimeter: altimeter)
                        customTimer.startTimer()
                    }.padding().disabled(manager.state != "Ready").opacity(debug ? 0 : 1)
                    
    //                Button("STOP Iterations") {
    //                    customTimer.stopTimer()
    //                    sensor.stopAllRecording(motionManager: motionManager, altimeter: altimeter)
    //                    
    //                }.padding().opacity(debug ? 0 : 1)
                    
                    Text("\(manager.numberOfMeasurements - manager.timerIntervalValues.count >= 0 ? "\(manager.numberOfMeasurements - manager.timerIntervalValues.count)/\(manager.numberOfMeasurements)" : "Not initialised")")
                        .padding()
                
                        ForEach($sensor.activeSensorsList, id: \.self) { $list in
                            HStack{
                                Text(list.sensorName)
    
                                Text(String(list.sensorData.count))
                                NavigationLink(
                                    destination: SensorDetailView(
                                        x: $list.sensorData.last?.1.x ?? .constant(0.0),
                                        y: $list.sensorData.last?.1.y ?? .constant(0.0),
                                        z: $list.sensorData.last?.1.z ?? .constant(0.0), sensorName: $list.sensorName ),
                                                                        
                                        label: { Text("Details") }
                                    )
                            }
                        }.opacity(debug ? 0 : 1)
                    Button("CreateDIR"){
                        createDirectory()
                    }.padding().opacity(debug ? 0 : 1)
                    
                    Button("Select destination") {
                        showingFilePicker = true
                    }.padding()
                        .fileImporter(isPresented: $showingFilePicker, allowedContentTypes: [.folder], allowsMultipleSelection: false) { result in
                            // Handle the user-selected file or folder
                            switch result {
                            case .success(let urls):
                                // Check if any URLs were selected
                                if let url = urls.first {
                                    selectedFolder = url // Store the selected folder in selectedFolder
                                    print(url)
                                }
                            case .failure(let error):
                                print("Error selecting folder: \(error.localizedDescription)")
                            }
                            
                        }.opacity(debug ? 0 : 1)
                    
                    Button("Save All to JSON") {
                        saveAllToJson()
                    }
                    .disabled(selectedFolder == nil)
                    .opacity(debug ? 0 : 1)
                    
                    Spacer()
                    Button(action: {
                        debug.toggle() // Toggle debug mode
                    }) {
                        Text("Debug Mode")
                            .padding(5)
                            .background(
                                RoundedRectangle(cornerRadius: 5)
                                    .foregroundColor(!debug ? Color.red : Color.yellow)
                            )
                            .foregroundColor(.white)
                    }
                }
                //----------------------------- TITLE ---------------------------------
                .navigationBarTitle("Starting View", displayMode: .inline)
                //-----------------------------  DEVICE ID ---------------------------------
                .navigationBarItems(
                    leading: Button(action: {
                        sensorDetailSheetPresented = true
                    }) {
                        Text("\(manager.deviceID)")
                    }
                )
                //----------------------------- STATUS INDICATOR ---------------------------------
                .navigationBarItems(
                    trailing:
                        Button(action: {
                            isServerSettingsSheetPresented = true
                        }) {
                            Text("\(manager.state)")
                                .padding(.horizontal, 10)
                                .background(
                                    getColorForStatus(manager.state)
                                        .cornerRadius(5)
                                )
                                .foregroundColor(.white)
                        }
                )
                //----------------------------- STATUS INDICATOR SHEETS ---------------------------------
                .sheet(isPresented: $isServerSettingsSheetPresented, content: {
                    ServerConnectionView(
                        manager: manager
                    )
                })
                .sheet(isPresented: $sensorDetailSheetPresented, content: {
                    VStack{
                        ForEach($sensor.activeSensorsList, id: \.self) { $list in
                            HStack{
                                Text(list.sensorName)
                                Spacer()
                                Text(String(list.sensorData.count))
                                NavigationLink(
                                    destination: SensorDetailView(
                                        x: $list.sensorData.last?.1.x ?? .constant(0.0),
                                        y: $list.sensorData.last?.1.y ?? .constant(0.0),
                                        z: $list.sensorData.last?.1.z ?? .constant(0.0), sensorName: $list.sensorName ),
                                    
                                    label: { Text("Details") }
                                )
                            }
                        }
                    }.padding()
                })
                //---------------------------------- FIRST STARTUP ----------------------------------------
                .sheet(isPresented: $first_open, content : {
                    Button {
                        if manager.state != "Ready" {
                            manager.sendMessage(type: "init")
                        }
                        
                        if manager.state == "Ready" {
                            createDirectory()
                            first_open = false
                            demostart = true
                        }
                    } label: {
                        Text(manager.state != "Ready" ? "Get started" : manager.state)
                            .padding(.horizontal, 10)
                            .background(
                                getColorForStatus(manager.state)
                                    .cornerRadius(5)
                            )
                            .foregroundColor(.white)
                    }
                    //
                    Text(manager.serverResponse == "Basic response" || manager.serverResponse == "OK" ? "" : manager.serverResponse)
                    Text(manager.state != "Ready" ? "Click the button to connect to the server " : "Click the button to start the measurements")
                    Text("Status: \(manager.state)").padding().opacity(manager.state == "Ready" ? 0 : 1)
                    
                    HStack{
                        TextField("Enter Host", text: $manager.hostName)
                            .textFieldStyle(RoundedBorderTextFieldStyle())
                            .padding(.vertical)
                        Text(":")
                        TextField("Enter Port Number", text: $manager.portNumber)
                            .textFieldStyle(RoundedBorderTextFieldStyle())
                            .padding(.vertical)
                    }.padding().opacity(manager.state == "Ready" ? 0 : 1)
                })
                //----------------------------- TIMER REFRESH ---------------------------------
                .onReceive(timer){ _  in
                    if customTimer.isStarted{
                        customTimer.updateTimer()
                        print("Timer: \(customTimer.totalSeconds)")
                        
    //                    if customTimer.totalSeconds == 2 {
    //                        shouldStartRecording = true
    //                    }
                    }
                }
    //            .onReceive(Just(shouldStartRecording)) { _ in
    //                if shouldStartRecording {
    //                    print("Starting measurements")
    //                    sensor.startAllRecording(motionManager: motionManager, altimeter: altimeter)
    //                    shouldStartRecording = false
    //                }
    //            }
                //----------------------------- NOTIFICAION HANDELING ---------------------------------
                .alert("\(manager.numberOfMeasurements - manager.timerIntervalValues.count >= -1 ? "\(manager.numberOfMeasurements - manager.timerIntervalValues.count)/\(manager.numberOfMeasurements)" : "Not initialised") Measurement", isPresented: $customTimer.isFinished) {
                    Button(manager.timerIntervalValues.count == 0 ? "End" : "Next" ,role: .cancel){
                        customTimer.stopTimer()
    
                        sensor.stopAllRecording(motionManager: motionManager, altimeter: altimeter)
                        saveAllToJson()
                        
                        setNextMeasurement()
                        if manager.timerIntervalValues.count != 0 {
                            sensor.startAllRecording(motionManager: motionManager, altimeter: altimeter)
                            customTimer.startTimer()
                        }
                    }
                    Button("Stop", role: .destructive){
                        customTimer.stopTimer()
                        sensor.stopAllRecording(motionManager: motionManager, altimeter: altimeter)
                        saveAllToJson()
                    }
                }
                //------------------ DEMO NOTIFICATION - MEASUREMENT STARTING ----------------------
                .alert("Demo Alert - press start if you are ready to start!", isPresented: $demostart){
                    Button("Start",role: .cancel){
                        customTimer.stopTimer()
                        sensor.stopAllRecording(motionManager: motionManager, altimeter: altimeter)
                        setNextMeasurement()
                        if customTimer.totalSeconds != 0 {
                            sensor.startAllRecording(motionManager: motionManager, altimeter: altimeter)
                            customTimer.startTimer()
                        }
                    }
                    Button("Stop", role: .destructive){
                        first_open = true
                    }
                }
            }
        }
        func saveAllToJson(){
            do {
                let now = Date()
                let timeInMilliseconds = Int(now.timeIntervalSince1970 * 1000)
                
                if let selectedFolder = selectedFolder {
                    try sensor.exportAllSensorToJson(fileLocations: &fileLocations, filename_prefix: String(iterationCount) + String(timeInMilliseconds), fileLocationURL: selectedFolder, deviceID: manager.deviceID, manager: manager)
                    
                } else {
                    print("Please select a folder to save the JSON file.")
                }
            } catch {
                print("Error exporting to JSON: \(error.localizedDescription)")
            }
            //        print("File locations: \(fileLocations)")
        }
        //----------------------------- JSON LOCATION DIR CREATION ------------------------------------
        func createDirectory() {
            if manager.state == "Ready" {
                let fileManager = FileManager.default
                do {
                    let documentsDirectory = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
                    let directoryURL = documentsDirectory.appendingPathComponent(manager.deviceID)
                    if !fileManager.fileExists(atPath: directoryURL.path) {
                        try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
                        print("Directory created at: \(directoryURL)")
                        selectedFolder = directoryURL
                    } else {
                        print("Directory already exists at: \(directoryURL)")
                    }
                } catch {
                    print("Error creating directory: \(error)")
                }
            }
            
        }
        //----------------------------- STATUS INDICATOR COLOR PICKER ---------------------------------
        private func getColorForStatus(_ status: String) -> Color {
            switch status {
            case "Ready":
                return .green
            case "NoConnection":
                return .red
            default:
                return .yellow
            }
        }
        //----------------------------- SETTING UP NEXT "TIME" FOR TIMER ---------------------------------
        private func setNextMeasurement(){
            if !manager.timerIntervalValues.isEmpty {
                customTimer.seconds = manager.timerIntervalValues.removeFirst()
                customTimer.minutes = 0
            } else {
                customTimer.seconds = 00
                customTimer.minutes = 00
            }
            customTimer.refreshTimer()
        }
    }
    struct StartingView_Previews: PreviewProvider {
        static var previews: some View {
            StartingView( customTimer: CustomTimer())
            
        }
        
    }