# ISC License
#
# Copyright (c) 2016, Autonomous Vehicle Systems Lab, University of Colorado at Boulder
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
#   Integrated Unit Test Script
#   Purpose:  Run the external pulsed torque disturance dynEffector
#   Author:  Hanspeter Schaub
#   Creation Date:  March 26, 2017
#
import numpy as np
import pytest
from Basilisk.simulation import ExtPulsedTorque
from Basilisk.utilities import SimulationBaseClass
from Basilisk.utilities import macros
from Basilisk.utilities import unitTestSupport  # general support file with common unit test functions
# uncomment this line is this test is to be skipped in the global unit test run, adjust message as needed
# @pytest.mark.skipif(conditionstring)
# uncomment this line if this test has an expected failure, adjust message as needed
# @pytest.mark.xfail(True)
[docs]
@pytest.mark.parametrize("offCount", [
      (3)
     ,(0)
])
# provide a unique test method name, starting with test_
def test_module(show_plots, offCount):
    """Module Unit Test"""
    # each test method requires a single assert method to be called
    [testResults, testMessage] = run(
            show_plots, offCount)
    assert testResults < 1, testMessage 
def run(show_plots, offCount):
    testFailCount = 0                       # zero unit test result counter
    testMessages = []                       # create empty array to store test log messages
    unitTaskName = "unitTask"
    unitProcessName = "testProcess"
    scSim = SimulationBaseClass.SimBaseClass()
    #
    #  create the dynamics simulation process
    #
    dynProcess = scSim.CreateNewProcess(unitProcessName)
    # create the dynamics task and specify the integration update time
    dynProcess.addTask(scSim.CreateNewTask(unitTaskName, macros.sec2nano(0.1)))
    testObject = ExtPulsedTorque.ExtPulsedTorque()
    testObject.ModelTag = "externalPulsedTorque"
    # These don't do anything. They are here to confirm they don't do anything
    testObject.readInputMessages()
    testObject.writeOutputMessages(0)
    testObject.pulsedTorqueExternalPntB_B = [[-1], [1],[ -1]]
    testObject.countOnPulse = 1
    testObject.countOff = offCount
    scSim.AddModelToTask(unitTaskName, testObject)
    #
    #   Setup data logging
    #
    testObjectLog = testObject.logger("torqueExternalPntB_B")
    scSim.AddModelToTask(unitTaskName, testObjectLog)
    #
    #   initialize the simulation
    #
    scSim.InitializeSimulation()
    #
    #   run the simulation
    #
    DT = 0.1
    testProcessRate = macros.sec2nano(DT)
    for tStop in range(1, 11):
        scSim.ConfigureStopTime(macros.sec2nano(tStop*DT))
        scSim.ExecuteSimulation()
        testObject.computeForceTorque(scSim.TotalSim.CurrentNanos, testProcessRate)
        scSim.TotalSim.SingleStepProcesses()
    # log the data
    dataTorque = testObjectLog.torqueExternalPntB_B[1:,:]
    np.set_printoptions(precision=16)
    #
    #   set true position information
    #
    if (offCount == 3):
        trueTorque_B = [
                  [0.0, 0.0, 0.0]
                , [-1.0, 1.0, -1.0]
                , [1.0, -1.0, 1.0]
                , [0.0, 0.0, 0.0]
                , [0.0, 0.0, 0.0]
                , [0.0, 0.0, 0.0]
                , [-1.0, 1.0, -1.0]
                , [1.0, -1.0, 1.0]
                , [0.0, 0.0, 0.0]
                , [0.0, 0.0, 0.0]
                , [0.0, 0.0, 0.0]
        ]
    if (offCount == 0):
        trueTorque_B = [
                  [0.0, 0.0, 0.0]
                , [-1.0, 1.0, -1.0]
                , [1.0, -1.0, 1.0]
                , [-1.0, 1.0, -1.0]
                , [1.0, -1.0, 1.0]
                , [-1.0, 1.0, -1.0]
                , [1.0, -1.0, 1.0]
                , [-1.0, 1.0, -1.0]
                , [1.0, -1.0, 1.0]
                , [-1.0, 1.0, -1.0]
                , [1.0, -1.0, 1.0]
        ]
    # compare the module results to the truth values
    accuracy = 1e-12
    if (len(trueTorque_B) != len(dataTorque)):
        testFailCount += 1
        testMessages.append("FAILED:  ExtPulsedTorque failed torque unit test (unequal array sizes)\n")
    else:
        for i in range(0,len(trueTorque_B)):
            # check a vector values
            if not unitTestSupport.isArrayEqual(dataTorque[i],trueTorque_B[i],3,accuracy):
                testFailCount += 1
                testMessages.append("FAILED:  ExtPulsedTorque failed torque unit test at t=" + str(dataTorque[i,0]*macros.NANO2SEC) + "sec\n")
    #   print out success message if no error were found
    if testFailCount == 0:
        print("PASSED ")
    # each test method requires a single assert method to be called
    # this check below just makes sure no sub-test failures were found
    return [testFailCount, ''.join(testMessages)]
#
# This statement below ensures that the unit test scrip can be run as a
# stand-along python script
#
if __name__ == "__main__":
    test_module(False,       # show_plots
                3            # offCount
               )