1 /*
2 *  rotator_ascom.cpp
3 *  PHD Guiding
4 *
5 *  Created by Andy Galasso
6 *  Copyright (c) 2015 Andy Galasso
7 *  All rights reserved.
8 *
9 *  This source code is distributed under the following "BSD" license
10 *  Redistribution and use in source and binary forms, with or without
11 *  modification, are permitted provided that the following conditions are met:
12 *    Redistributions of source code must retain the above copyright notice,
13 *     this list of conditions and the following disclaimer.
14 *    Redistributions in binary form must reproduce the above copyright notice,
15 *     this list of conditions and the following disclaimer in the
16 *     documentation and/or other materials provided with the distribution.
17 *    Neither the name of Craig Stark, Stark Labs nor the names of its
18 *     contributors may be used to endorse or promote products derived from
19 *     this software without specific prior written permission.
20 *
21 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 *  POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34 
35 #include "phd.h"
36 
37 #ifdef ROTATOR_ASCOM
38 
39 #include <wx/msw/ole/oleutils.h>
40 
41 struct AscomRotatorImpl
42 {
43     GITEntry m_gitEntry;
44     wxString m_choice;
45     wxString m_name;
46 
47     bool Create(DispatchObj *obj, DispatchClass *cls);
48 };
49 
RotatorAscom(const wxString & choice)50 RotatorAscom::RotatorAscom(const wxString& choice)
51     : m_impl(new AscomRotatorImpl())
52 {
53     m_impl->m_choice = m_impl->m_name = choice;
54 }
55 
~RotatorAscom(void)56 RotatorAscom::~RotatorAscom(void)
57 {
58     delete m_impl;
59 }
60 
displayName(const wxString & ascomName)61 static wxString displayName(const wxString& ascomName)
62 {
63     if (ascomName.Find(_T("ASCOM")) != wxNOT_FOUND)
64         return ascomName;
65     return ascomName + _T(" (ASCOM)");
66 }
67 
68 // map descriptive name to progid
69 static std::map<wxString, wxString> s_progid;
70 
EnumAscomRotators(void)71 wxArrayString RotatorAscom::EnumAscomRotators(void)
72 {
73     wxArrayString list;
74 
75     try
76     {
77         DispatchObj profile;
78         if (!profile.Create(L"ASCOM.Utilities.Profile"))
79             throw ERROR_INFO("ASCOM Rotator: could not instantiate ASCOM profile class ASCOM.Utilities.Profile. Is ASCOM installed?");
80 
81         Variant res;
82         if (!profile.InvokeMethod(&res, L"RegisteredDevices", L"Rotator"))
83             throw ERROR_INFO("ASCOM Rotator: could not query registered rotator devices: " + ExcepMsg(profile.Excep()));
84 
85         DispatchClass ilist_class;
86         DispatchObj ilist(res.pdispVal, &ilist_class);
87 
88         Variant vcnt;
89         if (!ilist.GetProp(&vcnt, L"Count"))
90             throw ERROR_INFO("ASCOM Rotator: could not query registered rotators: " + ExcepMsg(ilist.Excep()));
91 
92         unsigned int const count = vcnt.intVal;
93         DispatchClass kvpair_class;
94 
95         for (unsigned int i = 0; i < count; i++)
96         {
97             Variant kvpres;
98             if (ilist.GetProp(&kvpres, L"Item", i))
99             {
100                 DispatchObj kvpair(kvpres.pdispVal, &kvpair_class);
101                 Variant vkey, vval;
102                 if (kvpair.GetProp(&vkey, L"Key") && kvpair.GetProp(&vval, L"Value"))
103                 {
104                     wxString ascomName = vval.bstrVal;
105                     wxString displName = displayName(ascomName);
106                     wxString progid = vkey.bstrVal;
107                     s_progid[displName] = progid;
108                     list.Add(displName);
109                 }
110             }
111         }
112     }
113     catch (const wxString& msg)
114     {
115         POSSIBLY_UNUSED(msg);
116     }
117 
118     return list;
119 }
120 
Create(DispatchObj * obj,DispatchClass * cls)121 bool AscomRotatorImpl::Create(DispatchObj *obj, DispatchClass *cls)
122 {
123     IDispatch *idisp = m_gitEntry.Get();
124     if (idisp)
125     {
126         obj->Attach(idisp, cls);
127         return true;
128     }
129 
130     Debug.Write(wxString::Format("Create ASCOM Rotator: choice '%s' progid %s\n", m_choice, s_progid[m_choice]));
131 
132     wxBasicString progid(s_progid[m_choice]);
133 
134     if (!obj->Create(progid))
135     {
136         Debug.AddLine("ASCOM Rotator: Could not get CLSID for rotator " + m_choice);
137         return false;
138     }
139 
140     m_gitEntry.Register(*obj);
141     return true;
142 }
143 
Connect(void)144 bool RotatorAscom::Connect(void)
145 {
146     DispatchClass driver_class;
147     DispatchObj driver(&driver_class);
148 
149     // create the COM object
150     if (!m_impl->Create(&driver, &driver_class))
151     {
152         pFrame->Alert(_("Could not create ASCOM rotator object. See the debug log for more information."));
153         return true;
154     }
155 
156     struct ConnectInBg : public ConnectRotatorInBg
157     {
158         AscomRotatorImpl *rotator;
159         ConnectInBg(AscomRotatorImpl *rotator_) : rotator(rotator_) { }
160         bool Entry()
161         {
162             GITObjRef dobj(rotator->m_gitEntry);
163             // ... set the Connected property to true....
164             if (!dobj.PutProp(L"Connected", true))
165             {
166                 SetErrorMsg(ExcepMsg(dobj.Excep()));
167                 return true;
168             }
169             return false;
170         }
171     };
172     ConnectInBg bg(m_impl);
173 
174     if (bg.Run())
175     {
176         pFrame->Alert(_("ASCOM driver problem: Connect") + ":\n" + bg.GetErrorMsg());
177         return true;
178     }
179 
180     Variant vname;
181     if (driver.GetProp(&vname, L"Name"))
182     {
183         m_impl->m_name = vname.bstrVal;
184         Debug.AddLine(wxString::Format("rotator name = %s", m_impl->m_name));
185     }
186 
187     Rotator::Connect();
188 
189     return false;
190 }
191 
Disconnect(void)192 bool RotatorAscom::Disconnect(void)
193 {
194     if (!IsConnected())
195     {
196         Debug.AddLine("ASCOM rotator: attempt to disconnect when not connected");
197         return false;
198     }
199 
200     GITObjRef rot(m_impl->m_gitEntry);
201 
202     if (!rot.PutProp(L"Connected", false))
203     {
204         Debug.AddLine(ExcepMsg("ASCOM disconnect", rot.Excep()));
205     }
206 
207     Rotator::Disconnect();
208     return false;
209 }
210 
ShowPropertyDialog(void)211 void RotatorAscom::ShowPropertyDialog(void)
212 {
213     DispatchObj rot;
214 
215     if (m_impl->Create(&rot, NULL))
216     {
217         Variant res;
218         if (!rot.InvokeMethod(&res, L"SetupDialog"))
219         {
220             pFrame->Alert(ExcepMsg(rot.Excep()));
221         }
222     }
223 }
224 
Name(void) const225 wxString RotatorAscom::Name(void) const
226 {
227     return m_impl->m_name;
228 }
229 
Position(void) const230 float RotatorAscom::Position(void) const
231 {
232     GITObjRef rot(m_impl->m_gitEntry);
233 
234     Variant vRes;
235     if (!rot.GetProp(&vRes, L"Position"))
236     {
237         pFrame->Alert(ExcepMsg(_("ASCOM driver problem -- cannot get rotator position"), rot.Excep()));
238         return 0.f;
239     }
240 
241     return vRes.fltVal;
242 }
243 
244 #endif // ROTATOR_ASCOM
245