__all__ = [
    "calc",
]

import copy
import json
import logging
from pathlib import Path
import pickle as pk
from typing import NoReturn, Optional, Tuple

import numpy as np

import torch
from torch import Tensor
from torch.utils.data import DataLoader

from . import _config as config
from . import dirs
from . import influence_utils
from .influence_utils import InfluenceMethod
from .influence_utils.nn_influence_utils import InfFuncTensors
from .losses import TORCH_DEVICE
from . import tracin_utils
from .types import CustomTensorDataset, LearnerParams
from . import utils

DEFAULT_R = 10
INF_RES_DIR = None

@influence_utils.log_time(res_type=InfluenceMethod.INF_FUNC)
def calc(block: utils.ClassifierBlock, tr_dl: DataLoader, te_x: Tensor,
         te_y: Tensor, ex_id: Optional[int] = None, use_precompute: bool = False):
    r"""
    :param block: Block of interest
    :param tr_dl: \p DataLoader used to train the learners
    :param te_x:
    :param te_y:
    :param ex_id:
    :param use_precompute:
    :return:
    """
    global INF_RES_DIR
    INF_RES_DIR = dirs.RES_DIR / config.DATASET.name.lower() / "inf_func" / block.name().lower()
    INF_RES_DIR.mkdir(exist_ok=True, parents=True)

    # Use the tensors to create a supervised training set. Supports optional test transform
    kw_args = {}
    if config.has_tfms():
        kw_args = {"transform": config.get_test_tfms()}
    ds = CustomTensorDataset([te_x, te_y], **kw_args)
    te_dl = DataLoader(ds, drop_last=False, shuffle=False, num_workers=utils.NUM_WORKERS,
                       batch_size=1)

    # Do not use the original training dataloader since it may require transforms or drop part of
    # the dataset.  Create temporary Dataloader that overcomes those issues
    assert isinstance(tr_dl.dataset, CustomTensorDataset), "Code supports certain dataset type"

    # Each learner will have different influence function values
    _calc_block_inf(block=block, tr_dl=tr_dl, te_dl=te_dl, ex_id=ex_id,
                    use_precompute=use_precompute)

def _calc_block_inf(block: utils.ClassifierBlock, tr_dl: DataLoader, te_dl: DataLoader,
                    ex_id: Optional[int],
                    use_precompute: bool) -> InfFuncTensors:
    r"""
    Encapsulates calculating the influence of blocks

    :param block: Block for which influence is calculated
    :param tr_dl: Train \p DataLoader
    :param te_dl: Test \p DataLoader
    :return: Tuple of the sorted estimated loss, backdoor IDs, and dataset IDs
    """
    block.eval()
    wd = config.get_learner_val(block.name(), LearnerParams.Attribute.WEIGHT_DECAY)

    bl_x, bl_y, bd_ids, ds_ids = _build_learner_tensors(block=block, train_dl=tr_dl)
    n_tr = bl_y.shape[0]
    batch_tr_dl, instance_tr_dl = _build_block_dataloaders(bl_x=bl_x, bl_y=bl_y)

    flds = [block.name(), "inf-fin"]
    prefix = "-".join(flds).lower()
    # noinspection PyTypeChecker
    filename = utils.construct_filename(prefix=prefix, out_dir=INF_RES_DIR, ex_id=ex_id,
                                        file_ext="pkl", add_ds_to_path=False)
    # if not filename.exists():
    msg = f"calculation of {InfluenceMethod.INF_FUNC.value} influence"
    logging.info(f"Starting {msg}")
    if True:
        precomputed_s_test = None
        if use_precompute:
            assert filename.exists(), "Precomputed s_test file does not exist"
            with open(filename, "rb") as f_in:
                precomputed_s_test = pk.load(f_in).s_test

        # Order of res: influences, train_inputs_collections, s_test
        res = influence_utils.compute_influences(model=block,
                                                 n_gpu=1 if torch.cuda.is_available() else 0,
                                                 device=TORCH_DEVICE,
                                                 f_loss=block.loss.calc_train_loss,
                                                 test_dl=te_dl,
                                                 batch_train_data_loader=batch_tr_dl,
                                                 instance_train_data_loader=instance_tr_dl,
                                                 weight_decay=wd,
                                                 s_test_damp=config.DAMP,
                                                 s_test_scale=config.SCALE,
                                                 s_test_num_samples=config.R_DEPTH,
                                                 s_test_iterations=config.T_REPEATS,
                                                 precomputed_s_test=precomputed_s_test)

        # Extract the result fields
        res.ds_ids, res.bd_ids = ds_ids.clone(), bd_ids.clone()
        with open(filename, "wb+") as f_out:
            pk.dump(res, f_out)
    logging.info(f"COMPLETED {msg}")

    with open(filename, "rb") as f_in:
        res = pk.load(f_in)  # type: InfFuncTensors
    flds = (
        (res.inf_base, InfluenceMethod.INF_FUNC),
        (res.inf_sim, InfluenceMethod.INF_FUNC_RENORM),
    )
    for influence_vals, method in flds:
        # Convert the estimated loss
        est_loss = -1 / n_tr * influence_vals

        est_loss_sorted, helpful = torch.sort(est_loss, dim=0, descending=True)
        tmp_bd_ids, tmp_ds_ids = res.bd_ids[helpful], res.ds_ids[helpful]

        tracin_utils.results.generate_epoch_stats(ep=None, subepoch=None, method=method,
                                                  block=block, inf_vals=est_loss_sorted,
                                                  bd_ids=tmp_bd_ids, ds_ids=tmp_ds_ids,
                                                  ex_id=ex_id, log_cutoff=True)

        if config.USE_WANDB:
            train_dl = tracin_utils.configure_train_dataloader(train_dl=tr_dl)
            tracin_utils.generate_wandb_results(block=block, inf_vals=est_loss_sorted.cpu(),
                                                ds_ids=tmp_ds_ids.cpu(), bd_ids=tmp_bd_ids.cpu(),
                                                method=method, train_dl=train_dl, ex_id=ex_id)

    return res

def _log_influence_results(block: utils.ClassifierBlock, est_loss: Tensor, helpful: Tensor,
                           ds_ids: Tensor, bl_y: Tensor) -> NoReturn:
    r"""
    :param block:
    :param est_loss: Estimated change in loss if training example is removed
    :param helpful: Training examples numbered from 0 to (# training examples - 1)
    :param ds_ids: Dataset ID numbers for the training examples used by the block
    :param bl_y: Labels for the training examples used by the block
    """
    b_name = block.name()
    influence_utils.check_duplicate_ds_ids(ds_ids=ds_ids)

    harmful = torch.flip(helpful, dims=[0])
    for i in range(2):
        if i == 0:
            name = "helpful"
        else:
            name = "harmful"
        top_ord = locals()[name][:5]
        top_ids = ds_ids[top_ord]
        logging.info(f"{b_name}: Top {name} IDs: {top_ids.tolist()}")
        logging.info(f"{b_name}: Top {name} Est. Change Loss: {est_loss[top_ord].tolist()}")
        logging.info(f"{b_name}: Top {name} Labels: {bl_y[top_ord].tolist()}")

def _build_inf_results_file(block: utils.ClassifierBlock, helpful: Tensor, influence_vals: Tensor,
                            est_loss_vals: Tensor, ds_ids: Tensor, bl_y: Tensor) -> NoReturn:
    r"""
    Constructs the influence results file

    :param block:
    :param helpful:
    :param influence_vals:
    :param est_loss_vals:
    :param ds_ids: Dataset ID numbers for the training examples used by the block
    :param bl_y: Labels for the training examples used by the block
    :return:
    """
    assert ds_ids.shape == helpful.shape, "Helpful tensor shape does not match the ID tensor"
    assert bl_y.shape == helpful.shape, "Helpful tensor shape does not match the y tensor"

    ord_ids, ord_y = ds_ids[helpful], bl_y[helpful]
    influence_utils.check_duplicate_ds_ids(ds_ids=ds_ids)

    ord_inf, ord_est_loss = influence_vals[helpful], est_loss_vals[helpful]
    inf_res = {"block_name": block.name(),
               "dataset": config.DATASET.name,
               "hvp_batch_size": config.HVP_BATCH_SIZE,
               "damp": config.DAMP,
               "scale": config.SCALE,
               "helpful-ids": ord_ids.tolist(),
               "helpful-ord-loss": ord_est_loss.tolist(),
               "helpful-y": ord_y.tolist(),
               "helpful-influence": ord_inf.tolist(),
               "test_id": config.TARG_IDX,
               "test-targ-cls": config.TARG_CLS,
               "test-pois-cls": config.POIS_CLS
               }

    res_path = _build_inf_res_filename(block)
    with open(res_path, "w+") as f_out:
        json.dump(inf_res, f_out)

def _build_inf_res_filename(block: utils.ClassifierBlock) -> Path:
    r""" Construct the filename for the results """
    prefix = f"inf-{block.name()}-t-id={config.TARG_IDX}"
    return utils.construct_filename(prefix, out_dir=dirs.RES_DIR, file_ext="json",
                                    add_timestamp=True)

def _build_learner_tensors(block: utils.ClassifierBlock,
                           train_dl: DataLoader) -> Tuple[Tensor, Tensor, Tensor, Tensor]:
    r"""
    Construct the X/y/IDs tensors based on any example filtering by the block
    :param block:
    :param train_dl:
    :return: Tuple of X, y, backdoor IDs, and dataset IDs tensors respectively
    """
    tmp_ds = copy.copy(train_dl.dataset)
    tmp_ds.transform = None
    cp_tr_dl = DataLoader(tmp_ds, batch_size=config.BATCH_SIZE,
                          drop_last=False, shuffle=False, num_workers=utils.NUM_WORKERS)

    verify_contents = True   # ToDo Revert to false to speed up final version

    all_x, all_y, all_bd_ids, all_ds_ids = [], [], [], []
    for batch_tensors in cp_tr_dl:
        batch = block.organize_batch(batch_tensors, process_mask=True,
                                     verify_contents=verify_contents)
        if batch.skip():
            continue

        all_x.append(batch.xs.cpu())
        all_y.append(batch.lbls.cpu())
        all_bd_ids.append(batch.bd_ids.cpu())
        all_ds_ids.append(batch.ds_ids.cpu())

    all_ds_ids, id_ordering = torch.sort(torch.cat(all_ds_ids, dim=0), dim=0)
    # Consolidate into tensors and sort by the image IDs
    n_tr = id_ordering.shape[0]  # Number of training examples
    # Strange count for number of training examples
    assert n_tr == id_ordering.numel(), "Weird size mismatch"

    filename = "_".join([block.name().lower(), "inf-func", block.start_time])
    # noinspection PyUnresolvedReferences
    np.savetxt(INF_RES_DIR / (filename + "_ord-id.csv"), all_ds_ids.numpy(), fmt='%d',
               delimiter=',')

    # Combine the subtensors and ensure the order aligns with all_ds_ids
    tr_x, tr_y = torch.cat(all_x, dim=0)[id_ordering], torch.cat(all_y, dim=0)[id_ordering]
    all_bd_ids = torch.cat(all_bd_ids, dim=0)[id_ordering]
    return tr_x, tr_y, all_bd_ids, all_ds_ids

def _build_block_dataloaders(bl_x: Tensor, bl_y: Tensor) -> Tuple[DataLoader, DataLoader]:
    r"""
    Constructs two separate dataloaders.  You may want different properties dataloader properties
    when estimating the Hessian vector product (HVP) and when estimating influence.  By specifying
    separate \p DataLoaders, those two roles are separated.

    :param bl_x: Block's X tensor
    :param bl_y: Blocks y (i.e. label) tensor
    :return: Tuple of the batch \p DataLoader (used for generating the HVP) and the instance
             \p DataLoader used when estimating influence.
    """
    # ToDo Determine whether to use transforms
    ds = CustomTensorDataset((bl_x, bl_y), transform=config.get_train_tfms())
    batch_tr_dl = DataLoader(ds, batch_size=config.HVP_BATCH_SIZE,
                             shuffle=True, drop_last=True, num_workers=utils.NUM_WORKERS)

    ds = CustomTensorDataset((bl_x, bl_y), transform=config.get_test_tfms())
    instance_tr_dl = DataLoader(ds, batch_size=1, shuffle=False, drop_last=False,
                                num_workers=utils.NUM_WORKERS)
    return batch_tr_dl, instance_tr_dl


# Data Sonification Project - LITR 0110D
import csv
from datetime import datetime
from miditime.miditime import MIDITime
from scipy import stats
import math

# instantiate the MITITime class with tempo 120 and 5sec/year
mymidi = MIDITime(120, 'data_sonfication.mid', 1, 5, 1)

# load in climate data as dictionary
climate_data = []
with open('GLB.Ts+dSST.csv') as csvfile:
    readCSV = csv.reader(csvfile, delimiter=',')
    # skip headers (first 2 rows)
    next(readCSV)
    next(readCSV)
    for row in readCSV:
        climate_data.append(
        {'days_since_epoch': mymidi.days_since_epoch(datetime.strptime(row[0], '%Y')),
         'magnitude_change': row[17]
         })

my_data_timed = [
    {'beat': mymidi.beat(d['days_since_epoch']),
     'magnitude_change': float(d['magnitude_change'])} for d in climate_data]
start_time = my_data_timed[0]['beat']
data_list = [d['magnitude_change'] for d in my_data_timed]

def mag_to_pitch_tuned(magnitude):
    """
    Consumes some magnitude value and normalizes it over the range of note
    values provided from a key.
    :param magnitude: some int or float value representing magnitude of data
    :return: a MIDI pitch represented by the normalized value
    """
    # Where does this data point sit in the domain of your data?
    #(I.E. the min magnitude is 3, the max in 5.6). In this case the optional
    #'True' means the scale is reversed, so the highest value will return the
    #lowest percentage.
    scale_pct = mymidi.linear_scale_pct(min(data_list), max(data_list), magnitude)

    # Another option: Linear scale, reverse order
    # scale_pct = mymidi.linear_scale_pct(3, 5.7, magnitude_change, True)

    # Another option: Logarithmic scale, reverse order
    # scale_pct = mymidi.log_scale_pct(3, 5.7, magnitude_change, True)

    # Pick a range of notes. This allows you to play in a key.
    c_major = ['C', 'D', 'E', 'F', 'G', 'A', 'B']

    #Find the note that matches your data point
    note = mymidi.scale_to_note(scale_pct, c_major)

    #Translate that note to a MIDI pitch
    midi_pitch = mymidi.note_to_midi_pitch(note)

    return midi_pitch

note_list = []

z_scores = stats.zscore(data_list)
exp_score = [math.ceil(math.exp(x)*4)/4 for x in z_scores]

i = 0
for d in my_data_timed:
    note_list.append([
        d['beat'] - start_time,
        mag_to_pitch_tuned(d['magnitude_change']),
        100,  # velocity
        exp_score[i]  # duration, in beats
    ])
    i += 1

# Add a track with those notes
mymidi.add_track(note_list)

# Output the .mid file
mymidi.save_midi()

#sum = sum(exp_score)
#softmax_score = [x / sum for x in exp_score]
print(exp_score)


import os

import json

import aloe

from werkzeug.datastructures import MultiDict

from nose.tools import assert_equals

import flask_login
import sqlalchemy

from app import app
from app.database import db

from app.models.university import University, UniversityPending

from ..steps import fieldname_with_language

from pprint import pprint

def university_step_data_to_post_data(step_data):

    post_data = MultiDict()
    languages = []
    for data_dict in step_data:
        language = data_dict.pop("language")
        languages.append(language)
        for k, v in data_dict.items():
            if University.is_translatable(k):
                k = k + "[" + language + "]"
            post_data.add(k, v)

    if len(languages) == 0:
        languages = ["en", "fr"]

    for l in languages:
        post_data.setdefault(fieldname_with_language("university_name", l), "")
        post_data.setdefault(fieldname_with_language("university_intro", l), "")

    post_data.setdefault("university_latlong", "")
    post_data.setdefault("university_web_address", "")

    post_data["languages"] = ",".join(languages)

    return post_data

@aloe.step(u'the user pends addition of university \"([\w\d ]*)\"')
def the_user_pends_addition_of_university(step, university_name):
    with app.test_request_context():
        aloe.world.response = aloe.world.app.post(
            '/university/create',
            data={
                fieldname_with_language("university_name", aloe.world.language):university_name,
                fieldname_with_language("university_intro", aloe.world.language):"A new university",
                "university_latlong":"0.0,0.0",
                "university_web_address": "www.some_university.com",
                'languages': [aloe.world.language]
            }
        )

@aloe.step(u'the user pends those courses to that university')
def the_user_pends_addition_of_courses_to_university(step):
    with app.app_context():
        university = University.get_single(university_id = aloe.world.university_ids[0])
        university_data = university.request_dict()
        university_data.setlist("courses[]", [str(c) for c in aloe.world.course_ids])

        pprint(university_data)
        with app.test_request_context():
            aloe.world.response = aloe.world.app.post(
                "/university/" + str(aloe.world.university_ids[0]) + "/edit",
                data=university_data
            )

@aloe.step(u'the user approves pending changes to university \"([\w\d ]*)\"')
def the_user_approves_pending_changes_to_university(step, university_name):
    with app.test_request_context():
        university = UniversityPending.get_by_name(university_name=university_name, language=aloe.world.language)
        aloe.world.response = aloe.world.app.post(
            '/university/pending/approve',
            data={
                'data_id':university.pending_id
            }
        )

@aloe.step(u'the following university details are correct in response')
def the_following_university_details_are_returned(step):
    returned_json = json.loads(aloe.world.response.data.decode("utf-8"))["data"]

    expected_json = University.json_skeleton()
    expected_json = json.loads(step.multiline)

    for k, v in expected_json.items():
        assert_equals(v, returned_json[k])

@aloe.step(u'the following data is pending for that university')
def the_following_university_details_are_pending(step):
    with app.app_context():
        if aloe.world.last_pending_university_id is None:
            aloe.world.last_pending_university_id = UniversityPending.get_single(
                university_id=aloe.world.university_ids[0]
            )

        actual_json = aloe.world.last_pending_university_id.json()

        expected_json = University.json_skeleton()
        expected_json = json.loads(step.multiline)

        for k, v in expected_json.items():
            assert_equals(v, actual_json[k])

@aloe.step(u'the university \"([\w\d ]*)\" should exist in \"([\w\d ]*)\"')
def the_university_should_exist(step, university_name, language):
    with app.app_context():
        university = University.get_by_name(university_name=university_name, language=language)
        assert_equals(university.translations[language].university_name, university_name)

@aloe.step(u"the university \"([\w\d ]*)\" exists")
def the_university_exists(step, university_name):
    with app.app_context():
        try:
            uni = University.create({aloe.world.language: {"university_name": university_name}})
            aloe.world.university_ids.append(uni.university_id)
        except sqlalchemy.exc.IntegrityError:
            db.session.rollback() # University already in the system

@aloe.step(u"the university \"([\w\d ]*)\" is pending for addition")
def the_university_is_pending_for_addition(step, university_name):
    with app.app_context():
        try:
            aloe.world.last_pending_university_id = UniversityPending.addition(
                {aloe.world.language: {"university_name": university_name}},
            )
        except sqlalchemy.exc.IntegrityError:
            db.session.rollback() # University already in the system

@aloe.step(u"the university \"([\w\d ]*)\" is pending for edit")
def the_university_is_pending_for_edit(step, university_name):
    with app.app_context():
        try:
            university = University.get_single(university_name=university_name, language=aloe.world.language)
            aloe.world.last_pending_university_id = UniversityPending.edit(university)
        except sqlalchemy.exc.IntegrityError:
            db.session.rollback() # University already in the system

@aloe.step(u"the translation \"([\w\d ]*)\" in \"([\w\d ]*)\" of university \"([\w\d ]*)\" is pending")
def the_translation_of_university_is_pending(step, translation, language, university_name):
    with app.app_context():
        try:
            university = University.get_single(university_name=university_name, language=aloe.world.language)
            aloe.world.last_pending_university_id = UniversityPending.edit(university)
            aloe.world.last_pending_university_id.set_translations(
                {language: {"university_name": translation}}
            )
            aloe.world.last_pending_university_id.save()

        except sqlalchemy.exc.IntegrityError:
            db.session.rollback() # University already in the system

@aloe.step(u'the university \"([\w\d ]*)\" should have the following data')
def the_university_has_data(step, university_name):
    with app.test_request_context():
        university = University.get_by_name(university_name=university_name, language="en")
        expected_data = json.loads(step.multiline)
        assert_equals(university.json(), expected_data)

@aloe.step(u'the user pends the following data to it')
def the_user_pends_university_data_for_edit(step):
    with app.test_request_context():
        aloe.world.response = aloe.world.app.post(
            "/university/" + str(aloe.world.university_ids[0]) + "/edit",
            data=university_step_data_to_post_data(step.hashes)
        )

@aloe.step(u'those courses should be pending for addition')
def courses_should_be_pending_for_addition(step):
    with app.app_context():
        university = UniversityPending.get_single(university_id = aloe.world.university_ids[0])
        for course_id in aloe.world.course_ids:
            assert(university.has_course(course_id))


import board
import busio
import digitalio
import microcontroller
import sys, os
from time import sleep, monotonic_ns
import adafruit_dotstar as dotstar
import feathers2
# +--------------------------+
# | Imports for LCD control  |
# +--------------------------+
from sparkfun_serlcd import Sparkfun_SerLCD_I2C

# -----------------------+
# General debug flag     |
my_debug = False      #  |
# -----------------------+
# Other global flags     |
ctrl_c_flag = False   #  |
# -----------------------+
my_os = None  # will hold a list containing O.S. info
my_machine = None
#               like fw version, machine name.
#               Data collected through function: get_os_info()
my_cpu_id = None # collected through function get_cpu_id()

# +--------------------------------------+
# | Msg rx diagnostics                   |
# +--------------------------------------+
use_diagnosics = False
if use_diagnosics:
    diagnostics_iterations = 20 # Perform rx msg diagnostics for n times
    diagn_dict = {} # used for msg receive time diagnostics
else:
    diagn_dict = None # needs always to be defined. Used in 'globals' cmd in loop() and ck_uart()
    # See loop()
# +--------------------------------------+
# | Definition for the I2C character LCD |
# +--------------------------------------+
if board.board_id == 'unexpectedmaker_feathers2':
    i2c = board.STEMMA_I2C()
else:
    i2c = board.I2C()

# It happens when that SerLCD gets locked-up
# e.g. caused by touching with a finger the
# RX pin (4th pin fm left).
# This pin is very sensitive!
# A locked-up situation is often shown as that
# all the 8x5 segments of the LCD are 'filled with inverse pixels.
# see: https://github.com/KR0SIV/SerLCD_Reset for a reset tool.
# But this is not what I want.
# I want to be able to reset the LCD from within this script.
while True:
    try:
        lcd = Sparkfun_SerLCD_I2C(i2c)
        break
    except ValueError:
        print("The LCD is locked-up. Please connect RS with GND for a second or so.")
        sleep(10)  # wait a bit

sleep(1)

# while not i2c.try_lock():

# 0x72 found (= SparkFun serLCD ?)
"""
try:
    while True:
        print(
            "I2C addresses found:",
            [hex(device_address) for device_address in i2c.scan()],
        )
        sleep(2)

finally:  # unlock the i2c bus when ctrl-c'ing out of the loop
    i2c.unlock()
"""
# +-----------------------------------------------+
# | SparkFun LCD special command codes            |
# +-----------------------------------------------+
HIGH = 1
LOW = 0

# +-----------------------------------------------+
# | Hardware definitions for builtin BLUE LED     |
# +-----------------------------------------------+
#led_interval = 1000
led_state = HIGH  # idem. When HIGH the LED is OFF

# Make sure the 2nd LDO is turned on
feathers2.enable_LDO2(True)
# +-----------------------------------------------+
# | Hardware definitions for builtin NeoPixel LED |
# +-----------------------------------------------+
use_dotstar = False
if use_dotstar:
    # Create a DotStar instance
    dots = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.5, auto_write=True)

biLdIsOn = False # Flag for the built-in blue led

# Buffers
rx_buffer_len = 152  # was: 151
rx_buffer = bytearray(rx_buffer_len * b'\x00')

# +-----------------------------------------------+
# | Create an instance of the UART object class   |
# +-----------------------------------------------+
uart = busio.UART(board.TX, board.RX, baudrate=4800, timeout=0, receiver_buffer_size=rx_buffer_len)  # board.RX, board.TX)
#uart = board.UART()

if sys.version_info > (3,):
    long = int

nRMC = None
nGGA = None

_id = 0
_lat = 1
_latdir = 2
_lon = 3
_londir = 4
_gs = 5
_crs = 6
_alt = 7

class gps_msgs:
    def __init__(self):
        self.gps = ["",  "",  "",  "",  "", "", "", ""]

    def write(self, s):
        tp = isinstance(s,list)
        if tp == True:
            self.gps[_id] = s[_id] # ID
            self.gps[_lat] = s[_lat] # Lat
            self.gps[_latdir] = s[_latdir] # LadID N/S
            self.gps[_lon] = s[_lon] # Lon
            self.gps[_londir] = s[_londir] # LonID E/W
            self.gps[_gs] = s[_gs] # GS
            self.gps[_crs] = s[_crs] # CRS
            self.gps[_alt] = s[_alt] # Alt

    def read(self, n):
        tp = isinstance(n, type(None))
        if tp == True:
            n = 0
        if n >= 0 and n <= 7:
            return self.gps[n]
        else:
            return self.gps

    def clean(self):
            self.gps[0] = ""
            self.gps[1] = ""
            self.gps[2] = ""
            self.gps[3] = ""
            self.gps[4] = ""
            self.gps[5] = ""
            self.gps[6] = ""
            self.gps[7] = ""

encoding = 'utf-8'
lcd_maxrows = 4
lcd_rowlen = 20
lp_cnt = 0
max_lp_cnt = 99
startup = -1
loop_time = 0
t_elapsed = 0
msg_nr = 0

# next four defs copied from:
# I:\pico\paul_projects\pico\circuitpython\msfs2020_gps_rx_picolipo\2021-09-03_16h49_ver
ac_none = 0
ac_stopped = 1
ac_taxying = 2
ac_flying = 4

am_last_stat = ac_none
am_stat = ac_stopped # am_stat = airplane movement status
am_stat_dict = {0:"stopped", 2:"taxying", 4: "flying"}
lac_Stopped = True
lac_IsTaxying = False
lacStopMsgShown = False
lacTaxyMsgShown = False
acStopInitMonot = 0
acStopInterval = 6000 # mSec

# Classes
my_msgs = gps_msgs()

# +--------------------------------------+
# | Definitions for all LEDs             |
# +--------------------------------------+
led_colors_dict = {
    'green' : 0,
    'red' : 1,
    'blue' : 2,
    'white' : 3,
    'off' : 4 }

lcd_bl_colors = ["black", "red", "orange", "yellow", "green", "blue", "indigo", "violet", "grey", "white"]

brill = 50

colorN = [(0,  0,     0),     # black is off
  (brill,       0,     0),     # bright red
  (0xFF8C00,    0,     0),     # orange
  (brill,       brill, 0),     # bright yellow
  (0,           brill, 0),     # bright green
  (0,           0,     brill), # bright blue
  (0x4B0082,    0,     0),     # indigo
  (0xA020F0,    0,     0),     # violet
  (0x808080,    0,     0),     # grey
  (brill,       brill, brill)] # white

# Create a colour wheel index int
color_index = 0
lcd_color_index = 0
lcd_clr_chg_cnt = 0

"""
   get_os_info() -> Bool
        @brief
        Get and extract the name and version of the CircuitPython firmware installed,
        e.g. 'release='6.3.0' and 'version='6.3.0 on 2021-06-01'.
        Get also the name of the 'machine', e.g.: FeatherS2 with ESP32S2'
    Parameters: None
    Return: Bool

"""
def get_os_info():
    global my_os, my_debug, my_machine
    my_fw_itms = ("sysname", "nodename", "release", "version", "machine")
    n = os.uname()
    if n:
        my_os = n
        le = len(my_os)
        my_machine = my_os[le-1]
        print("get_os_info(): my_machine=", my_machine)
        if my_debug:
            for i in range(le):  # sysname, nodename, release, version and machine
            # e.g.: (sysname='esp32s2', nodename='esp32s2', release='6.3.0',
            # version='6.3.0 on 2021-06-01', machine='FeatherS2 with ESP32S2')
                print("{}: \'{}\'.".format(my_fw_itms[i], my_os[i]), end='\n')
        return True
    else:
        print("get_os_info(): n: {}, type(n): {}".format(n, type(n)), end='\n')
        return False

"""
   get_cpu_id() -> Bool
        @brief
        Gets the unique id of the cpu
    Parameters: None
    Return: Bool

"""
def get_cpu_id():
    global my_cpu_id, my_debug
    TAG = "get_cpu_id(): "
    lRetval = True  # assume positive result
    n1 = None
    n2 = None
    try:
        n1 = microcontroller.cpu.uid
        n2 = n1[:-2]
        if my_debug:
            print(TAG+"cpu uid: {}".format(n2), end='\n')
            print(TAG+"list(n2)=", list(n2))
    except AttributeError as e:
        # This happenen when trying this with a CircuitPython v8.0.0-alpha.1 on a Unexpected Maker FeatherS2
        print("get_cpu_id(): while trying to get \"microcontroller.cpu.uid\" occurred error:", e)
        return False
    if my_debug:
        print(TAG+"received cpu uid: {}".format(n1), end='\n')

    my_cpu_id_str = s = ""
    res = 0
    mult = 0
    le_uid2 = len(n2)
    # See: https://github.com/adafruit/circuitpython/issues/462,
    # especially the post by user 'Sommersoft' on 2018-01-13.
    if le_uid2 > 0:
        # added by @PaulskPt on 2022-07-04
        cpu_uid_lst = list(n2) # e.g.:  [199, 253, 26, 1, 163, 224]
        p = 0
        s = ""
        if my_debug:
            print(TAG+"cpu uid contains {} digits.".format(le_uid2), end='\n')
        i = le_uid2
        for c in cpu_uid_lst:
            if i == 4: mult = 10000
            elif i == 3: mult = 1000
            elif i == 2: mult = 10
            elif i == 1: mult = 1
            elif i == 0: mult = 0
            n2 = ord(chr(c))
            if my_debug:
                print(TAG+"the value of digit {} is: {:3d}.   ".format(i, n2), end='')
            res += n2 * mult
            if my_debug:
                print(TAG+"ord(0x{:02x}): {:3d}, i: {}, mult: {:5d}, result of calculation: {}".format(n2, n2, i, mult, res), end='\n')
            i -= 1
            s += chr(n2)+" "

        if my_debug:
            print("", end='\n')
            print(TAG+"final res = {} ".format(res), end='\n')
        s_res = str(res)
        if my_debug:
            print(TAG+"get_cpu_id(): unique cpu id: \'{}\'".format(s_res), end='\n')
        my_cpu_id = s_res

        if isinstance(n2, str):
            le = len(n2)
            p = 0
            s = ""
            if le > 0:
                if my_debug:
                    print(TAG+"cpu uid contains {} digits.".format(le), end='\n')
                i = le
                for c in n:
                    if i == 4: mult = 10000
                    elif i == 3: mult = 1000
                    elif i == 2: mult = 10
                    elif i == 1: mult = 1
                    elif i == 0: mult = 0
                    n2 = ord(c)
                    if my_debug:
                        print(TAG+"the value of digit {} is: {:2d}.   ".format(i, n2), end='')
                    res += n2 * mult
                    if my_debug:
                        print(TAG+"ord(0x{:02x}): {:2d}, i: {}, mult: {:5d}, result of calculation: {}".format(n2, n2, i, mult, res), end='\n')
                    i -= 1
                    s += chr(n2)+" "
                if my_debug:
                    print("", end='\n')
                    print(TAG+"final res = {} ".format(res), end='\n')
                s_res = str(res)
                if my_debug:
                    print(TAG+"get_cpu_id(): unique cpu id: \'{}\'".format(s_res), end='\n')
                my_cpu_id = s_res
            else:
                lRetval = False
        else:
            lRetVal = False
    else:
        lRetval = False
    return lRetval

"""
   my_board() -> None
        @brief
        Function prints global variables my_os and my_cpu_id
        if global my_debug is True and if either of these variables contain values
        Called by setup()
    Parameters: None
    Return: None
"""
def my_board():
    global my_os, my_cpu_id
    #if my_debug:
    #if my_os:
    print("OS: {}.".format(my_os), end='\n')
    #if my_cpu_id:
    print("CPU ID: {}.".format(my_cpu_id), end='\n')

"""
   lcd_deflt_clr() -> None
        @brief
        Sets LCD default background color
    Parameters: None
    Return: None

"""
def lcd_dflt_clr():
    global colorN
    lcd.set_backlight(colorN[2][0])  # set backlight to orange

"""
   chg_lcd_bg_clr() -> None
        @brief
        Sets LCD background to color upon value of global lcd_color_index
    Parameters: None
    Return: None

"""
def chg_lcd_bg_clr():
    global colorN, lcd_bl_colors, lcd_color_index
    lcd.clear()
    lcd.write(lcd_bl_colors[lcd_color_index])
    if colorN[lcd_color_index][0] > 255:
        lcd.set_backlight(colorN[lcd_color_index][0])
    else:
        lcd.set_backlight_rgb(colorN[lcd_color_index][0], colorN[lcd_color_index][1], colorN[lcd_color_index][2])
    lcd_color_index += 1
    if lcd_color_index >= len(colorN):
        lcd_color_index = 0

"""
    lcd_chr_test(void) -> None
        @brief
        This function sends a block of characters to the lcd
        to test what graphic representation the lcd will show.
        The low-end and high-end of the range of character values
        has to be manually put in line 355 or accept what is there as default
        THIS FUNCTION IS CALLED FROM setup() BUT NOT USED IN THIS MOMENT (2021-10-11)

        Parameters: None

        Return: None
"""
def lcd_chr_test():
    col = row = 0
    spc = chr(0x20)
    lcd.clear()
    for _ in range(0x20,0xfe):   # start with ascii <spc>
        if _ < 0x80 or _ > 0x9f:
            c1 = chr(_)
            lcd.set_cursor(col, row)
            lcd._put_char(_)
            #lcd.write(spc)
            print("hex value: {}, graphic: {} ".format(hex(_), c1), end='\n')
            col += 1
            if col >= 18:
                col = 0
                row += 1
                if row > 3:
                    row = 0
                    sleep(5)
                    lcd.clear()
    sleep(5)

"""
    lcd_clean_fm(fm_row, to_row) -> None
        @brief
        This function clears the lcd from row number indicated by parameter fm_row,
        until and including row number indicated by parameter to_row.

        Parameters: int fm_row, int to_row

        Return: None
"""
def lcd_clean_fm(fm_row, to_row = (lcd_maxrows-1)):
    global lcd_maxrows
    if fm_row >=0 and fm_row <= 3:
        if isinstance(to_row, type(None)):
            to_row = lcd_maxrows -1
        while True:
            lcd.set_cursor(0, fm_row)
            lcd.write(" "*20)
            fm_row +=1
            if fm_row > lcd_maxrows-1:
                break

"""
    setup(void) -> None
        @brief
        This function is called by main().
        It sets starting parameters for the lcd:
        - no system messages (to the lcd);
        - no cursor;
        - (if cursor) do not blink the cursor.
        It also resets the uart input buffer

        Parameters: None

        Return: None
"""
def setup():
    global lcd, uart, degreesChar

    # lcd.backlight();
    lcd.system_messages(False)
    # 150 for system with CP2101N (5Volt)
    #  50 for system with CP2221a
    lcd.set_contrast(150)  # Set to 50 for 5 Volt. Set lcd contrast to default value (was: 120). Value 50 worked fine with UM FeatherS2
    lcd_dflt_clr()  # set default lcd backlight color to orange
    lcd.cursor(0)  # do not show cursor (use 2 for show cursor)
    lcd.blink(0)   # do not blink cursor
    lcd.clear()
    lcd.set_cursor(0, 0)   # ATTENTION: the SerLCD module set_cursor(col,row)

    if use_dotstar and led_state == LOW: # Switch off the RGB LED
        led_toggle()
        #dotstar_led_off()

    #lcd_chr_test()  # print all the characters in the lcd rom

    if uart:
        uart.reset_input_buffer()  # Clear the rx buffer

    sleep(1)  # <--------------- DELAY ---------------

    get_os_info()  # Collect O.S. info. Put in global variable my_os and (partly) in my_machine

    get_cpu_id()   # Collect cpu id info. Put in global variable my_cpu_id

"""
    loop(void) -> boolean
        @brief
        This functions is the backbone of this script. It is called by main().
        It makes calls to various important functions in this script.
        It also will stop execution if the user pressed the Ctrl-C key combo (keyboard interrupt).

        Parameters: None

        Return: boolean
"""
def loop():
    global startup, am_stat, lp_cnt, biLdIsOn, lcd, msg_nr, ctrl_c_flag, diagn_dict

    TAG = "loop(): "
    lRetval = True  # assume positive
    chrs_rcvd = 0
    ac_stopped_cnt = 0
    ac_flying_cnt = 0
    lstop = False
    lSplitOK = False
    lcd_cleared = False
    lcd.clear()
    lcd.set_cursor(0, 0)
    lcd.write("MSFS 2020      ")
    lcd.set_cursor(0, 1)
    lcd.write("GPRMC/GPGGA data RX")
    lcd.set_cursor(0, 2)
    lcd.write("Platform ")
    lcd.set_cursor(0, 3)
    if my_machine:
        print(TAG+"my_machine= \"{}\"".format(my_machine))
        n1 = my_machine.find("ESP32S")
        n2 = my_machine.find("with")
        if n1 > 0 and n2 >=0:
            s = my_machine[:n2]+my_machine[n2:n2+1]+" "+my_machine[n2+5:]
        else:
            s = my_machine[:19]  # Not more than 20 characters
        lcd.write(s)
        #print(TAG+"my_machine (cut)= \"{}\"".format(s))
    else:
        lcd.write(sys.platform)
    sleep(5)
    lcd_clean_fm(2) # clean lcd rows 2 and 3
    print()
    print("MSFS2020 GPS GPRMC data reception decoder sketch by Paulsk (mailto: ct7agr@live.com.pt). ")
    print("\nNumber of loops in this run: {}".format(max_lp_cnt))
    chrs_rcvd = 0
    print("........................", end="\n")
    lcd.set_cursor(0, 3)
    lcd.write("About to receive...")
    while True:
        try:
            lp_cnt += 1
            # lcd.clear()
            print("\nStart of loop {}".format(lp_cnt), end="\n")

            if use_dotstar and led_state == LOW: # Switch off the dotstar RGB LED
                led_toggle()

            if startup == -1:
                uart.reset_input_buffer()

            wait_cnt = 0
            chrs_rcvd = ck_uart()
            print(TAG+"characters rcvd: ", chrs_rcvd)
            if chrs_rcvd > 0:
                lSplitOK = split_types()
                print(TAG+"split_types() result = {}".format(lSplitOK))
                ac_status()
                if am_stat == ac_stopped:
                    ac_stopped_cnt += 1
                    print(TAG+"ac_stopped_cnt=", ac_stopped_cnt)
                    if ac_stopped_cnt >= 5:
                        #if not lacStopMsgShown and not lacTaxyMsgShown:
                        lcd.set_cursor(0, 3)
                        lcd.write("About to receive...")
                elif am_stat == ac_flying:
                    ac_flying_cnt += 1
                    if ac_flying_cnt >= 5:
                        ac_stopped_cnt = 0 # reset when we sure are flying and no incidently gs = 0
                    if ac_flying_cnt > 1000:
                        ac_flying_cnt = 0  # reset
                if lSplitOK:
                    #ac_status()
                    if am_stat == ac_flying: # are we flying?
                        msg_nr += 1
                        print(TAG+"handling msg nr: {:02d}".format(msg_nr))
                        if use_diagnosics:
                            if msg_nr in diagn_dict:
                                diagn_dict[msg_nr][1] = 1 if lResult else 0
                            else:
                                print(TAG+"msg_nr {} not found in diagn_dict".format(msg_nr))
                        lcd_pr_msgs()
                        chrs_rcvd = 0
                        #sleep(0.1)
                        if msg_nr >= max_lp_cnt:
                            pass
                    if lstop == True:
                        if biLdIsOn:
                            #led.value = 0
                            feathers2.led_set(0)
                            biLdIsOn = False
                    else:
                        if startup == -1:
                            print("Waiting for serial com line to become available...")
                            startup = 0
                    print("End of loop {:2d}".format(lp_cnt), end="\n")
                    print("........................", end="\n")
                    if msg_nr >= max_lp_cnt:
                        msg_nr = 0
                        lp_cnt = 0
            else:
                pass
            if lstop == True:
                break
            if use_diagnosics:
                # Prepare and print to REPL a rx msgs diagnostics report
                if len(diagn_dict) >= diagnostics_iterations:
                    avg_time = 0
                    avg_split = 0
                    v = s = 0
                    le = len(diagn_dict)
                    for k, v in sorted(diagn_dict.items()):
                        if isinstance(diagn_dict[k], dict):
                            le2 = len(diagn_dict[k])
                            if le2 == 1:
                                v = diagn_dict[k][0]
                            if le2 == 2:
                                v = diagn_dict[k][0]
                                s = diagn_dict[k][1] # split result
                                avg_split += s
                        if isinstance(diagn_dict[k], float):
                            v = diagn_dict[k]
                        avg_time += v
                        print("msg nr: {:2d}, wait time for msg: {:7.4f}. Split result: {}".format(k, v, s))
                    print("Average wait time for msg: {:>5.2f}. Average split result: {:>5.2f}".format(avg_time/le, float(avg_split/le)))
                    diagn_dict = {} # cleanup the diagnostics dict
            #lcd_cleared = False
        except KeyboardInterrupt:
            ctrl_c_flag = True
            print("\'Ctrl-C\' pressed. Going to quit...")
            lcd.clear()
            lcd.set_cursor(1,2)
            lcd.write("\'Ctrl-C\' pressed.")
            lcd.set_cursor(1,2)
            lcd.write("Going to quit...")
            sleep(5)
            lRetval = False
            break

    #sleep(0.5)
    return lRetval

"""
    ck_uart(void) -> int (nr_bytes)
        @brief
        This functions reads the incoming data via the uart.
        The received data is put into rx_buffer.
        If data received this function will return the number of bytes received.
        The function will loop if the value(s) of the received data is zero.
        Parameters: None

        Return: int
"""
def ck_uart():
    global rx_buffer, msg_nr, loop_time, diagn_dict, nRMC, nGGA, my_debug
    TAG = 'ck_uart(): '
    nr_bytes = i = 0
    tot_nr_bytes = 0
    delay_ms = 0.2
    rx_buffer_s = b = ""
    n1 = n2 = n3 = n4 = 0
    if use_diagnosics:
        rx_wait_start = monotonic_ns()
    while True:
        #nr_bytes = uart.readinto(rx_buffer)
        rx_buffer = uart.read(rx_buffer_len)
        if rx_buffer is None:
            sleep(0.2)
            continue
        nr_bytes = len(rx_buffer)
        if my_debug:
            print(TAG+"nr of bytes= ", nr_bytes)
            print(TAG+"type(rx_buffer)=", type(rx_buffer))
            print(TAG+"rcvd data: {}".format(rx_buffer),end="\n")
        #for i in range(5):
        #    sleep(delay_ms)
        loop_time = monotonic_ns()
        if nr_bytes is None:
            if my_debug:
                print(TAG+"nr_bytes is None")
            sleep(delay_ms)
            continue
        if not nr_bytes:
            if my_debug:
                print(TAG+"nr_bytes=", nr_bytes)
            sleep(delay_ms)
            continue
        if nr_bytes > 1:
            tot_nr_bytes += nr_bytes
            print(TAG+"tot_nr_bytes=", tot_nr_bytes)
            rx_buffer_s += rx_buffer.decode(encoding) # if receiving a 2nd part: add it.
            print(TAG+"rx_buffer_s=", rx_buffer_s)
            # Check for '*' in tail. If present, the message is very probably complete.
            tail = rx_buffer_s[-15:]
            print(TAG+"tail=", tail)
            n0 = tail.find('*') # find '*' in tail 10 characters. If not found, loop
            if my_debug:
                print(TAG+"* found in tail of msg at: ", n0)
            if n0 < 0: # no '*' in tail
                sleep(delay_ms)
                continue
            if n1 == 0:
                n1 = rx_buffer_s.find('$GPRMC')
                if n1 < 0:
                    sleep(delay_ms)
                    continue
                else:
                    nRMC = n1
                    print(TAG+"$GPRMC msg received")
            if n2 == 0:
                n2 = rx_buffer_s.find('$GPGGA')
                if n2 < 0:
                    sleep(delay_ms)
                    continue
                else:
                    nGGA = n2
                    print(TAG+"$GPGGA msg received")
                n3 = int(tot_nr_bytes*0.75) # get 3/4 of length of characters received
                if my_debug:
                    print(TAG+"n0 = {}, n1 = {}, n2= {}, n3 ={}".format(n0, n1, n2, n3))
                if n1 > n3: # check if $GPRMC occurs at more than 3/4 of the characters received
                    sleep(delay_ms)
                    continue
                else:
                    # print(TAG+"returning with {} nr of bytes".format(nr_bytes))
                    if use_diagnosics:
                        rx_wait_stop = monotonic_ns()
                        rx_wait_duration = float((rx_wait_stop - rx_wait_start) / 1000000000) # convert nSec to mSec
                        diagn_dict[msg_nr+1] = {0: rx_wait_duration, 1: -1} # add a key/value pair for diagnostics
                        print(TAG+"it took {:6.2f} seconds for a complete msg ($GPRMC & $GPGGA) to be received".format(rx_wait_duration))
                    rx_buffer = rx_buffer_s.encode(encoding)
                    if my_debug:
                        print(TAG+"rx_bufffer returned=", rx_buffer)
                    return tot_nr_bytes
        elif nr_bytes == 1:
            if rx_buffer[0] == b'\x00':
                sleep(delay_ms)
                i += 1
                if i % 1000 == 0:
                    print("Waiting for uart line to become ready")
        else:
            empty_buffer()
            sleep(delay_ms)
            continue
    #return tot_nr_bytes

"""
   find_all(c) -> dict
   dict consists of an integer as key
   and an integer as value

"""
def find_all(c):
    #global rx_buffer
    f_dict = {}
    if c is None:
        return 0
    if type(c) is int:
        c2 = chr(c)
    elif type(c) is str:
        c2 = c
    rx_buffer_s = rx_buffer.decode()
    if my_debug:
        print("find_all(): rx_buffer=", rx_buffer)
    le = len(rx_buffer_s)
    if le > 0:
        ret = rx_buffer_s.count(c2)
        o_cnt = 0
        for i in range(le):
            if rx_buffer_s[i] == c2:
                f_dict[o_cnt] = i
                o_cnt += 1
    return f_dict

"""
    split_types(void) -> boolean
        @brief
        This functions searches the rx_buffer various times to find certain patterns
        that are tipical for the GPRMC and GPGGA GPS datagrams.
        If found, the data will be saved in the my_msgs class
        Parameters: None

        Return: boolean

"""
def split_types():
    global rx_buffer, my_msgs, nRMC, nGGA
    TAG = "split_types(): "
    lResult = True
    sRMC = sGGA = None
    rmc_s = gga_s = ""
    nr_RMC_items = nr_GGA_items = 0
    le_b4 = le_aft = 0
    nRMC_end = nGGA_end = 0
    lGPRMC_go = lGPGGA_go = False
    msg_lst = []
    rmc_lst = []
    gga_lst = []
    rmc_msg_lst = []
    gga_msg_lst = []
    le_dict = 0

    if my_debug:
        print(TAG+"entry...contents rx_buffer=", rx_buffer)
    t_rx_buffer = rx_buffer.decode()
    le_t_rx = len(t_rx_buffer)

    f_dict = find_all(10)
    if f_dict:
        le_dict = len(f_dict)
        if my_debug:
            print(TAG+"f_dict= {}, length dict = {}".format(f_dict, le_dict))

    if le_dict < 2:
        print(TAG+"exiting... f_dict length < 2")
        return False

    if nRMC is None: # in ck_uart() already has been done a find for $GPRMC and if found set nRMC
        nRMC = t_rx_buffer.find('$GPRMC')
    if nGGA is None: # idem for $GPGGA and nGGA
        nGGA = t_rx_buffer.find('$GPGGA')

    if le_dict >= 1 and (nRMC < 0 or nRMC > f_dict[le_dict-1]):
        print(TAG+"exiting.. nRMC = {}, f_dict[le_dict-1] = {}".format(nRMC, f_dict[le_dict-1]))
        return False  # $GPRMC not in first or not in first two msgs

    if nRMC >= 0:
        rmc_s = t_rx_buffer[nRMC:nRMC+6]
    if nGGA >= 0:
        gga_s = t_rx_buffer[nGGA:nGGA+6]

    if my_debug:
        print(TAG+"nRMC=", nRMC)
        print(TAG+"nGGA=", nGGA)

    if nRMC >= 0 and nGGA >= 0:
        if nRMC < nGGA:
            if nRMC >= 0:
                if le_dict == 1 or le_dict == 2:
                    sRMC_end = f_dict[0]+1
                    sRMC = t_rx_buffer[nRMC:f_dict[0]+1] # copy $GPRMC...\r\n'
                    if my_debug:
                        print(TAG+"sRMC = t_rx_buffer[{}:{}]".format(nRMC, sRMC_end))
                if le_dict == 3:
                    sRMC = t_rx_buffer[nRMC:f_dict[1]+1] # idem
            if nGGA > 0:
                if le_dict == 2:
                    if nGGA > f_dict[1]:
                        nGGA_end = le_t_rx
                    else:
                        nGGA_end = f_dict[1]+1
                    sGGA = t_rx_buffer[nGGA:nGGA_end] # copy $GPGGA...\r\n'
                    if my_debug:
                        print(TAG+"sGGA = t_rx_buffer[{}:{}]".format(nGGA, nGGA_end))
                if le_dict == 3:
                    sGGA = t_rx_buffer[nGGA:f_dict[2]+1] # idem
        elif nGGA < nRMC:
            if nGGA >= 0:
                if le_dict == 1 or le_dict == 2:
                    nGGA_end = f_dict[0]+1
                    sGGA = t_rx_buffer[nGGA:nGGA_end] # copy $GPGGA...\r\n'
                    if my_debug:
                        print(TAG+"sGGA = t_rx_buffer[{}:{}]".format(nGGA, nGGA_end))
                if le_dict == 3:
                    sGGA = t_rx_buffer[nGGA:f_dict[1]+1] # idem
            if nRMC > 0:
                if le_dict == 2:
                    if nRMC > f_dict[1]:
                        nRMC_end = le_t_rx
                    else:
                        nRMC_end = f_dict[1]+1
                    sRMC = t_rx_buffer[nRMC:nRMC_end] # copy $GPRMC...\r\n'
                    if my_debug:
                        print(TAG+"sRMC = t_rx_buffer[{}:{}]".format(nRMC, nRMC_end))
                if le_dict == 3:
                    sRMC = t_rx_buffer[nRMC:f_dict[2]+1] # idem

    if my_debug:
        print(TAG+"nRMC= ", nRMC)
        print(TAG+"rmc_s=", rmc_s)
        print(TAG+"sRMC=", sRMC)

        print(TAG+"nGGA= ", nGGA)
        print(TAG+"gga_s=", gga_s)
        print(TAG+"sGGA=", sGGA)

    lIsStr = isinstance(sRMC,str)
    if lIsStr:
        rmc_msg_lst = sRMC.split(",")
        nr_RMC_items = len(rmc_msg_lst)
        if nr_RMC_items == 12:
            lGPRMC_go = True

    lIsStr = isinstance(sGGA,str)
    if lIsStr:
        nGGA_ID = sGGA.find("$")
        if nGGA_ID > 0:
            pass
        else:
            gga_msg_lst = sGGA.split(",")
            nr_GGA_items = len(gga_msg_lst)
            if nr_GGA_items == 15:
                t_alt = float(gga_msg_lst[9])
                p = isinstance(t_alt,float)
                if p == True:
                    #                                  alt
                    t_alt = round(int(float(gga_msg_lst[9])) * 3.2808)
                else:
                    t_alt = 0
                lGPGGA_go = True

        if lGPRMC_go == True:
            #                      id             lat             latdir          lon           londir            gs        track true
            rmc_lst = [rmc_msg_lst[0], rmc_msg_lst[3], rmc_msg_lst[4], rmc_msg_lst[5], rmc_msg_lst[6], rmc_msg_lst[7], rmc_msg_lst[8]]
            if lGPGGA_go == True:
                rmc_lst.append(str(t_alt))
            else:
                rmc_lst.append("0")
            my_msgs.write(rmc_lst)
            if not my_debug:
                print(TAG+"rmc_lst + t_alt=", rmc_lst)
        if my_debug:
            print(TAG+"cross-check: my_msgs class data contents: {}".format(my_msgs.read(9)), end="\n")

    empty_buffer() # not emptying the buffer here causes an error in lcd_pr_msgs() !!!
    if lGPRMC_go == False and lGPGGA_go == False:
        lResult = False

    return lResult

"""
    led_BI_toggle(void) -> void
        @brief
        This functions toggles the builtin blue LED
        THIS FUNCTION IS USED FOR THE MOMENT (2021-10-11)

        Parameters: None

        Return: None
"""
def led_BI_toggle():
    global led, biLdIsOn

    if biLdIsOn:
        #led.value = 0
        feathers2.led_set(0)
        biLdIsOn = False
    else:
        #led.value = 1
        feathers2.led_set(1)
        biLdIsOn = True

"""
    led_toggle(void) -> void
        @brief
        This functions toggles the builtin neopixel LED and the external 3-color LED
        THIS FUNCTION IS NOT BEING USED FOR THE MOMENT (2021-10-11)

        Parameters: None

        Return: None
"""
def led_toggle():
    global led_state, HIGH, LOW, color_c_arr, color_index
    # uses global variable led_state
    led_chrs_rcvd = 0 # Before this was a global defined variable.
    brightness = 0.1
    if led_state == HIGH:
        led_state = LOW  # a=!a
    elif led_state == LOW:
        led_state = HIGH  # a=!a

    if led_state == LOW:
        # Get the R,G,B values of the next colour
        color_index = 0 # black ?
        r,g,b = feathers2.dotstar_color_wheel( color_index )
        # Set the colour on the dotstar
        dots[0] = ( r, g, b, brightness)  # was 0.5
        #pixels[0] = color_c_arr[led_colors_dict['off']]  # Set the color of the builtin LED to black
        #pixels.show()
        #blink_led3(led_colors_dict['off'])  # Switch the external 3-color LED off
    else:
        # Get the R,G,B values of the next colour
        r,g,b = feathers2.dotstar_color_wheel( color_index )
        # Set the colour on the dotstar
        dots[0] = ( r, g, b, brightness)  # was 0.5
        # Increase the wheel index
        color_index += 2
        # If the index == 255, loop it
        if color_index == 255:
            color_index = 0
        # Invert the internal LED state every half colour cycle
        feathers2.led_blink()
        # Sleep for 15ms so the colour cycle isn't too fast
        sleep(0.015)
        #pixels[0] = color_c_arr[led_colors_dict['green']]  # Set the color of the builtin LED to GREEN
        #pixels.show()
        #blink_led3(led_colors_dict['green']) # Switch the external 3-color LED GREEN

""" dotstar_led_off(void) -> void
    Switches the built-in dotstar led off
"""
def dotstar_led_off():
    global led_state, color_index
    brightness = 0.1
    color_index = 0 # black ?
    r,g,b = feathers2.dotstar_color_wheel( color_index )
    # Set the colour on the dotstar
    dotstar[0] = ( r, g, b, brightness)  # was 0.5
    led_state = HIGH

def ck_gs():
    t_gs = my_msgs.read(_gs)
    if t_gs is not None:
        if my_debug:
            print("groundspeed={}".format(t_gs))
        return float(t_gs)
    return 0.0

"""
    ac_status(void) -> boolean
        @brief
        This function chcks if the aircraft is moving or not. When not moving it will write
        a message to REPL and to the LCD

        Parameters: None

        Return: boolean
"""
# Function copied from: I:\pico\paul_projects\pico\circuitpython\msfs2020_gps_rx_picolipo\2021-09-03_16h49_ver
def ac_status():
    global lacStopMsgShown, lacTaxyMsgShown, acStopInitMonot, acStopInterval, am_stat, am_stat_dict, am_last_stat
    TAG = "ac_status(): "
    s = "Airplane is stopped or parked"
    t = "Airplane is taxying"
    f = "Airplane is flying"
    #gs = 5
    t_elapsed = 0
    lelapsed = False
    t_gs = my_msgs.read(_gs)
    le = len(t_gs)
    if my_debug:
        print(TAG,"value of gs = {}, len(gs) = {}".format(t_gs, le), end='\n')
    if le == 0: # check for t_gs == "". This happened sometimes!
        v_gs = 0
    else:
        v_gs = round(int(float(t_gs)))
    if v_gs <= 0.2:
        am_stat = ac_stopped
        lacTaxyMsgShown = False
    elif v_gs < 30:
        am_stat = ac_taxying
        lacStopMsgShown = False
    else:
        am_stat = ac_flying
        #lacStopMsgShown = False
        #lacTaxyMsgShown = False
        acStopInitMonot = 0
    am_last_stat = am_stat
    if my_debug:
        print(TAG,"value of v_gs = {}".format(v_gs), end='\n')
        print(TAG,"am_stat =", am_stat_dict[am_stat], end='\n')

    if am_stat == ac_stopped:
        currMonot = monotonic_ns()
        if acStopInitMonot == 0:
            acStopInitMonot = currMonot

        t_elapsed = (((currMonot - acStopInitMonot) + 500000)// 1000000)
        if my_debug:
            print(TAG,"t_elapsed:{}. acStopInterval: {}".format(t_elapsed, acStopInterval), end='\n')

        if t_elapsed >= acStopInterval:
            lelapsed = True

        if my_debug:
            print(TAG,"lelapsed:{}. lacStopMsgShown: {}".format(lelapsed, lacStopMsgShown), end='\n')
        if lelapsed:
            if lacStopMsgShown == False:
                lcd.clear()  # It takes about ten seconds before this message is shown after aircraft is stopped
                lcd.set_cursor(0,1)
                lcd.write(s)
                lacStopMsgShown = True
            print(s, end = '\n') # Alway print to REPL (it does almost immediately)
    if am_stat == ac_taxying:
        if not lacTaxyMsgShown:
            lcd.clear()  # It takes about ten seconds before this message is shown after aircraft is stopped
            lcd.set_cursor(0,1)
            lcd.write(t)
            lacTaxyMsgShown = True

def empty_buffer():
    global rx_buffer, rx_buffer_len
    rx_buffer = bytearray(rx_buffer_len * b'\x00')

def lcd_pr_msgs():
    global startup, loop_time, t_elapsed, msg_nr, my_msgs, lcd_maxrows, lacStopMsgShown, lacTaxyMsgShown, lac_Stopped
    TAG = "lcd_pr_msgs(): "
    dp = 0
    msg_itm = 0
    lcd_vpos = 0
    s = s0 = ""

    lat   =  1
    latID =  2
    lon   =  3
    lonID =  4
    gs    =  5
    crs   =  6
    alt   =  7

    degs = chr(0xdf)
    if startup == -1 or lacStopMsgShown or lacTaxyMsgShown:
        lacStopMsgShown = False
        lacTaxyMsgShown = False
        lcd.clear()
    lcd.set_cursor(18, 0)
    lcd.write("{:0>2d}".format(msg_nr))
    lcd_vpos = 0
    itms_lst = [lat, lon, gs, crs]
    led_BI_toggle()
    for msg_itm in itms_lst:
        if msg_itm == lat:
            lat_v = my_msgs.read(lat)
            dp = lat_v.find(".")
            if dp >= 0:
                if dp == 4:
                    s0 = "{: >2s}{}{:0>2s}\'{:0>2s}.{:0>2s}\"".format(lat_v[:2], degs, lat_v[2:4], lat_v[5:7],lat_v[7:])
                elif dp == 3:
                    s0 = "{: >2s}{}{:0>2s}\'{:0>2s}.{:0>2s}\" ".format(lat_v[:1], degs, lat_v[1:3], lat_v[4:6],lat_v[6:])
                else:
                    s0 = "             " # 13 spaces
            s = my_msgs.read(latID) + "    " + s0
        if msg_itm == lon:
            lon_v = my_msgs.read(lon)
            dp = lon_v.find(".")
            if dp >= 0:
                if dp == 5:
                    s0 = "{: >2s}{}{:0>2s}\'{:0>2s}.{:0>2s}\"".format(lon_v[:3], degs, lon_v[3:5], lon_v[6:8], lon_v[8:])
                elif dp == 4:
                    s0 = "{: >2s}{}{:0>2s}\'{:0>2s}.{:0>2s}\" ".format(lon_v[:2], degs, lon_v[2:4], lon_v[5:7], lon_v[7:])
                else:
                    s0 = "                " # 16 spaces
            s = my_msgs.read(lonID) + "   " + s0
        if msg_itm == gs:
            #gs0 = my_msgs.read(gs)
            s = "GS  {: >3d} ALT {: >5d} FT".format(round(int(float(my_msgs.read(gs)))),
                round(int(float(my_msgs.read(alt)))))
        if msg_itm == crs:
            #crs0 = my_msgs.read(crs)
            s = "CRS {:0>3d} DEGS     ".format(round(int(float(my_msgs.read(crs)))))
        lcd.set_cursor(0, lcd_vpos)

        le = len(s)
        for _ in range(le):
            lcd._put_char(ord(s[_]))
        #lcd.write(s)
        lcd_vpos += 1
    t_elapsed = (((monotonic_ns() - loop_time) + 500000)// 1000000)
    print(TAG+"Duration rx -> lcd: {} mSecs".format(t_elapsed), end="\n")

    my_msgs.clean()

    lcd.set_cursor(19, 3)

    lcd_vpos += 1
    if lcd_vpos >= lcd_maxrows:
        lcd_vpos = 0

    led_BI_toggle()

def main():
    global my_debug, ctrl_c_flag
    lResult = True
    cnt = 0

    setup()
    my_board()

    while True:
        lcd.clear()
        sleep(2)
        lcd.set_cursor(0, 0)
        lcd.write("FSUIPC7 GPS RX ")
        lcd.set_cursor(0, 1)
        lcd.write("for MSFS2020   ")
        sleep(2)
        lcd.set_cursor(0, 1)
        lcd.write("via serial     ")
        sleep(2)
        lcd.clear()

        if cnt == 0:
            lResult = loop()
            cnt += 1
            if lResult == False:
                if ctrl_c_flag:  # Ctrl-c has been pressed
                    break
                if my_debug == True:
                    print("loop(): setup returned with: \"{}\" in line nr: {}".format(lResult))
            break

    cnt = 0
    while True:
        cnt += 1
        if cnt >= 100:
            cnt = 0
            led_BI_toggle()

if __name__ == '__main__':
    main()


import requests
import json
import unicodedata
from bs4 import BeautifulSoup
import os
from dotenv import load_dotenv
import time

def find_env_file(folder):
    for filename in os.listdir(folder):
        if filename.endswith(".env"):
            return os.path.join(folder, filename)
    return None

def normalize_text(text):
    normalized_text = unicodedata.normalize('NFKD', text)
    return normalized_text.replace('“', "'").replace('”', "'")

def scrape_quotes(url, output_file):
    load_dotenv(find_env_file(os.getcwd()))
    page_num = 1

    while True:
        response = requests.get(url)
        content = response.text
        soup = BeautifulSoup(content, 'html.parser')

        target_script = None
        for script in soup.find_all('script'):
            if 'text' in script.text:
                target_script = script.text
                break

        if target_script is None:
            break

        start_index = target_script.find('[')
        end_index = target_script.find('];') + 1
        quotes_data = target_script[start_index:end_index]

        try:
            quotes = json.loads(quotes_data)
            for quote in quotes:
                quote['text'] = normalize_text(quote['text'])

        except json.JSONDecodeError as e:
            print("JSON parse invalid:", e)

        with open(output_file, "a", encoding='utf-8') as file:
            for quote in quotes:
                quote_data = {
                    "text": quote['text'],
                    "by": quote['author']['name'],
                    "tags": quote['tags']
                }
                json.dump(quote_data, file, ensure_ascii=False, indent=2)
                file.write("\n")

        next_page = soup.find('li', class_='next')
        if not next_page:
            break
        url = next_page.find('a')['href']
        url = f'http://quotes.toscrape.com{url}'
        page_num += 1
        time.sleep(2)

    print(f"Quotes from {page_num} pages have been saved in the output file.")

load_dotenv(find_env_file(os.getcwd()))
input_url = os.getenv("INPUT_URL")
output_file = os.getenv("OUTPUT_FILE")


import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread("imagens/hospital2.jpg")
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

corners = cv.goodFeaturesToTrack(gray, 10, 0.05, 0.25)

for item in corners:
    x, y = item[0]
    cv.circle(img, (x, y), 4, (0, 0, 255), -1)

fig = plt.figure(figsize=(10,8))
plt.imshow(img)
plt.show()


import os
import tensorflow as tf
import tensorflow_hub as hub
from tfhub_styletransfer_wrapper.imgFnc import load_image, show_images, save_to_gif, save_image

class StyleHub:
    def __init__(self, cpu_or_gpu='CPU'):
        os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
        if cpu_or_gpu == 'cpu' or cpu_or_gpu == 'CPU':
            os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
        elif cpu_or_gpu == 'gpu' or cpu_or_gpu == 'GPU':
            pass
        self.hub_module = hub.load('https://tfhub.dev/google/magenta/arbitrary-image-stylization-v1-256/2')
        self.content_image = []
        self.style_image = []

    def load_content(self, content_image_filename, output_size):
        self.content_image = tf.constant(load_image(content_image_filename, (output_size, output_size)))

    def load_style(self, style_image_filename, style_size):
        self.style_image = load_image(style_image_filename, (style_size, style_size))
        self.style_image = tf.constant(tf.nn.avg_pool(self.style_image, ksize=[3, 3], strides=[1, 1], padding='SAME'))

    def evaluate(self, plot_onoff=False):
        stylized_image = self.hub_module(self.content_image, self.style_image)[0]
        if plot_onoff:
            show_images([self.content_image, self.style_image, stylized_image],
                        ('Content Image', 'Style Image', 'Stylized Image'))

        return stylized_image

    def evaluate_recursively(self, n, plot_onoff=False, export_to_gif=False):
        filenames = []
        stylized_image = self.hub_module(self.content_image, self.style_image)[0]
        if export_to_gif:
            filenames.append("test_image/_0.jpg")
            save_image(stylized_image, filenames[0])
        for i in range(n - 1):
            stylized_image = self.hub_module(stylized_image, self.style_image)[0]
            if export_to_gif:
                filenames.append(f"test_image/_{i + 1}.jpg")
                save_image(stylized_image, filenames[i + 1])
        if export_to_gif:
            save_to_gif(filenames, "test_image/out.gif")
        if plot_onoff:
            show_images([self.content_image, self.style_image, stylized_image],
                        ("Content Image", "Style Image", f"Stylized Image after {n} applications"))


import logging
from celery import shared_task

logger = logging.getLogger('error')

@shared_task
def error_logger(err, exc_info=None):
    if exc_info:
        exc_type, exc_obj, exc_tb = exc_info
        logger.error(f'{exc_tb.tb_frame.f_code.co_filename} {exc_tb.tb_lineno} {str(err)}')
    else:
        logger.error(f'{str(err)}')