<commit_before>/*
 *  ascom.cpp
 *  PHD Guiding
 *
 *  Created by <NAME>.
 *  Copyright (c) 2006-2010 <NAME>.
 *  All rights reserved.
 *
 *  Modified by <NAME>
 *  Copyright (c) 2012-2013 <NAME>
 *  All rights reserved.
 *
 *  This source code is distributed under the following "BSD" license
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *    Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *    Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *    Neither the name of Craig Stark, Stark Labs nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "phd.h"

#ifdef GUIDE_ASCOM

#include "comdispatch.h"

#include <wx/msw/ole/oleutils.h>
#include <comdef.h>
#include <objbase.h>
#include <ole2ver.h>
#include <initguid.h>
#include <wx/textfile.h>
#include <wx/stdpaths.h>
#include <wx/stopwatch.h>

ScopeASCOM::ScopeASCOM(const wxString& choice)
{
    m_choice = choice;
    m_canPulseGuide = false;                           // will get updated in Connect()

    dispid_connected = DISPID_UNKNOWN;
    dispid_ispulseguiding = DISPID_UNKNOWN;
    dispid_isslewing = DISPID_UNKNOWN;
    dispid_pulseguide = DISPID_UNKNOWN;
    dispid_declination = DISPID_UNKNOWN;
    dispid_rightascension = DISPID_UNKNOWN;
    dispid_siderealtime = DISPID_UNKNOWN;
    dispid_sitelatitude = DISPID_UNKNOWN;
    dispid_sitelongitude = DISPID_UNKNOWN;
    dispid_slewtocoordinates = DISPID_UNKNOWN;
    dispid_raguiderate = DISPID_UNKNOWN;
    dispid_decguiderate = DISPID_UNKNOWN;
    dispid_sideofpier = DISPID_UNKNOWN;
    dispid_abortslew = DISPID_UNKNOWN;
}

ScopeASCOM::~ScopeASCOM(void)
{
}

static wxString displayName(const wxString& ascomName)
{
    if (ascomName.Find(_T("ASCOM")) != wxNOT_FOUND)
        return ascomName;
    return ascomName + _T(" (ASCOM)");
}

// map descriptive name to progid
static std::map<wxString, wxString> s_progid;

wxArrayString ScopeASCOM::EnumAscomScopes()
{
    wxArrayString list;

    try
    {
        DispatchObj profile;
        if (!profile.Create(L"ASCOM.Utilities.Profile"))
            throw ERROR_INFO("ASCOM Scope: could not instantiate ASCOM profile class ASCOM.Utilities.Profile. Is ASCOM installed?");

        Variant res;
        if (!profile.InvokeMethod(&res, L"RegisteredDevices", L"Telescope"))
            throw ERROR_INFO("ASCOM Scope: could not query registered telescope devices: " + ExcepMsg(profile.Excep()));

        DispatchClass ilist_class;
        DispatchObj ilist(res.pdispVal, &ilist_class);

        Variant vcnt;
        if (!ilist.GetProp(&vcnt, L"Count"))
            throw ERROR_INFO("ASCOM Scope: could not query registered telescopes: " + ExcepMsg(ilist.Excep()));

        unsigned int const count = vcnt.intVal;
        DispatchClass kvpair_class;

        for (unsigned int i = 0; i < count; i++)
        {
            Variant kvpres;
            if (ilist.GetProp(&kvpres, L"Item", i))
            {
                DispatchObj kvpair(kvpres.pdispVal, &kvpair_class);
                Variant vkey, vval;
                if (kvpair.GetProp(&vkey, L"Key") && kvpair.GetProp(&vval, L"Value"))
                {
                    wxString ascomName = vval.bstrVal;
                    wxString displName = displayName(ascomName);
                    wxString progid = vkey.bstrVal;
                    s_progid[displName] = progid;
                    list.Add(displName);
                }
            }
        }
    }
    catch (const wxString& msg)
    {
        POSSIBLY_UNUSED(msg);
    }

    return list;
}

bool ScopeASCOM::Create(DispatchObj& obj)
{
    try
    {
        // is there already an instance registered in the global interface table?
        IDispatch *idisp = m_gitEntry.Get();
        if (idisp)
        {
            obj.Attach(idisp, NULL);
            return true;
        }

        Debug.Write(wxString::Format("Create ASCOM Scope: choice '%s' progid %s\n", m_choice, s_progid[m_choice]));

        wxBasicString progid(s_progid[m_choice]);

        if (!obj.Create(progid))
        {
            throw ERROR_INFO("Could not establish instance of " + wxString(progid));
        }

        Debug.Write(wxString::Format("pScopeDriver = 0x%p\n", obj.IDisp()));

        // store the driver interface in the global table for access by other threads
        m_gitEntry.Register(obj);
    }
    catch (const wxString& msg)
    {
        Debug.Write(msg + "\n");
        return false;
    }

    return true;
}

bool ScopeASCOM::HasSetupDialog(void) const
{
    return true;
}

void ScopeASCOM::SetupDialog(void)
{
    DispatchObj scope;
    if (Create(scope))
    {
        Variant res;
        if (!scope.InvokeMethod(&res, L"SetupDialog"))
        {
            wxString msg(scope.Excep().bstrSource);
            if (scope.Excep().bstrDescription)
                msg += ":\n" + wxString(scope.Excep().bstrDescription);
            wxMessageBox(msg, _("Error"), wxOK | wxICON_ERROR);
        }
    }
}

bool ScopeASCOM::Connect(void)
{
    bool bError = false;

    try
    {
        Debug.Write("Connecting\n");

        if (IsConnected())
        {
            wxMessageBox("Scope already connected",_("Error"));
            throw ERROR_INFO("ASCOM Scope: Connected - Already Connected");
        }

        DispatchObj pScopeDriver;

        if (!Create(pScopeDriver))
        {<commit_msg>ASCOM scopes: better handling for ASCOM drivers that terminate outside of phd2 after PHD2 has called SetupDialog<commit_after>/*
 *  ascom.cpp
 *  PHD Guiding
 *
 *  Created by <NAME>.
 *  Copyright (c) 2006-2010 <NAME>.
 *  All rights reserved.
 *
 *  Modified by <NAME>
 *  Copyright (c) 2012-2013 <NAME>
 *  All rights reserved.
 *
 *  This source code is distributed under the following "BSD" license
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *    Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *    Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *    Neither the name of Craig Stark, Stark Labs nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "phd.h"

#ifdef GUIDE_ASCOM

#include "comdispatch.h"

#include <wx/msw/ole/oleutils.h>
#include <comdef.h>
#include <objbase.h>
#include <ole2ver.h>
#include <initguid.h>
#include <wx/textfile.h>
#include <wx/stdpaths.h>
#include <wx/stopwatch.h>

ScopeASCOM::ScopeASCOM(const wxString& choice)
{
    m_choice = choice;
    m_canPulseGuide = false;                           // will get updated in Connect()

    dispid_connected = DISPID_UNKNOWN;
    dispid_ispulseguiding = DISPID_UNKNOWN;
    dispid_isslewing = DISPID_UNKNOWN;
    dispid_pulseguide = DISPID_UNKNOWN;
    dispid_declination = DISPID_UNKNOWN;
    dispid_rightascension = DISPID_UNKNOWN;
    dispid_siderealtime = DISPID_UNKNOWN;
    dispid_sitelatitude = DISPID_UNKNOWN;
    dispid_sitelongitude = DISPID_UNKNOWN;
    dispid_slewtocoordinates = DISPID_UNKNOWN;
    dispid_raguiderate = DISPID_UNKNOWN;
    dispid_decguiderate = DISPID_UNKNOWN;
    dispid_sideofpier = DISPID_UNKNOWN;
    dispid_abortslew = DISPID_UNKNOWN;
}

ScopeASCOM::~ScopeASCOM(void)
{
}

static wxString displayName(const wxString& ascomName)
{
    if (ascomName.Find(_T("ASCOM")) != wxNOT_FOUND)
        return ascomName;
    return ascomName + _T(" (ASCOM)");
}

// map descriptive name to progid
static std::map<wxString, wxString> s_progid;

wxArrayString ScopeASCOM::EnumAscomScopes()
{
    wxArrayString list;

    try
    {
        DispatchObj profile;
        if (!profile.Create(L"ASCOM.Utilities.Profile"))
            throw ERROR_INFO("ASCOM Scope: could not instantiate ASCOM profile class ASCOM.Utilities.Profile. Is ASCOM installed?");

        Variant res;
        if (!profile.InvokeMethod(&res, L"RegisteredDevices", L"Telescope"))
            throw ERROR_INFO("ASCOM Scope: could not query registered telescope devices: " + ExcepMsg(profile.Excep()));

        DispatchClass ilist_class;
        DispatchObj ilist(res.pdispVal, &ilist_class);

        Variant vcnt;
        if (!ilist.GetProp(&vcnt, L"Count"))
            throw ERROR_INFO("ASCOM Scope: could not query registered telescopes: " + ExcepMsg(ilist.Excep()));

        unsigned int const count = vcnt.intVal;
        DispatchClass kvpair_class;

        for (unsigned int i = 0; i < count; i++)
        {
            Variant kvpres;
            if (ilist.GetProp(&kvpres, L"Item", i))
            {
                DispatchObj kvpair(kvpres.pdispVal, &kvpair_class);
                Variant vkey, vval;
                if (kvpair.GetProp(&vkey, L"Key") && kvpair.GetProp(&vval, L"Value"))
                {
                    wxString ascomName = vval.bstrVal;
                    wxString displName = displayName(ascomName);
                    wxString progid = vkey.bstrVal;
                    s_progid[displName] = progid;
                    list.Add(displName);
                }
            }
        }
    }
    catch (const wxString& msg)
    {
        POSSIBLY_UNUSED(msg);
    }

    return list;
}

bool ScopeASCOM::Create(DispatchObj& obj)
{
    try
    {
        // is there already an instance registered in the global interface table?
        IDispatch *idisp = m_gitEntry.Get();
        if (idisp)
        {
            obj.Attach(idisp, NULL);
            return true;
        }

        Debug.Write(wxString::Format("Create ASCOM Scope: choice '%s' progid %s\n", m_choice, s_progid[m_choice]));

        wxBasicString progid(s_progid[m_choice]);

        if (!obj.Create(progid))
        {
            throw ERROR_INFO("Could not establish instance of " + wxString(progid));
        }

        Debug.Write(wxString::Format("pScopeDriver = 0x%p\n", obj.IDisp()));

        // store the driver interface in the global table for access by other threads
        m_gitEntry.Register(obj);
    }
    catch (const wxString& msg)
    {
        Debug.Write(msg + "\n");
        return false;
    }

    return true;
}

bool ScopeASCOM::HasSetupDialog(void) const
{
    return true;
}

void ScopeASCOM::SetupDialog(void)
{
    DispatchObj scope;
    if (Create(scope))
    {
        Variant res;
        if (!scope.InvokeMethod(&res, L"SetupDialog"))
        {
            wxString msg(scope.Excep().bstrSource);
            if (scope.Excep().bstrDescription)
                msg += ":\n" + wxString(scope.Excep().bstrDescription);
            wxMessageBox(msg, _("Error"), wxOK | wxICON_ERROR);
        }
    }
    // destroy the COM object now as this reduces the likelhood of getting into a
    // state where the user has killed the ASCOM local server instance and PHD2 is
    // holding a reference to the defunct driver instance in the global interface
    // table
    m_gitEntry.Unregister();
}

bool ScopeASCOM::Connect(void)
{
    bool bError = false;

    try
    {
        Debug.Write("Connecting\n");

        if (IsConnected())
        {
            wxMessageBox("Scope already connected",_("Error"));
            throw ERROR_INFO("ASCOM Scope: Connected - Already Connected");
        }

        DispatchObj pScopeDriver;

        if (!Create(pScopeDriver))
        {
{'fraction_non_alphanumeric': 0.09267464185496972, 'fraction_numerical': 0.0030276177817161426, 'mean_word_length': 2.7185612300933553, 'pattern_counts': {'":': 2, '<': 31, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1823691', 'n_tokens_mistral': 4226, 'n_tokens_neox': 3992, 'n_words': 1182}