1 //-----------------------------------------------------------------------------
2 //
3 // SerialControllerImpl.cpp
4 //
5 // WinRT Implementation of the cross-platform serial port
6 //
7 // Copyright (c) 2015 Microsoft Corporation
8 // All rights reserved.
9 //
10 // SOFTWARE NOTICE AND LICENSE
11 //
12 // This file is part of OpenZWave.
13 //
14 // OpenZWave is free software: you can redistribute it and/or modify
15 // it under the terms of the GNU Lesser General Public License as published
16 // by the Free Software Foundation, either version 3 of the License,
17 // or (at your option) any later version.
18 //
19 // OpenZWave is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU Lesser General Public License for more details.
23 //
24 // You should have received a copy of the GNU Lesser General Public License
25 // along with OpenZWave. If not, see <http://www.gnu.org/licenses/>.
26 //
27 //-----------------------------------------------------------------------------
28
29 #include "Defs.h"
30 #include "SerialControllerImpl.h"
31
32 #include "platform/Log.h"
33 #include <winstring.h>
34 #include <ppltasks.h>
35
36 using namespace OpenZWave;
37 using namespace Windows::Devices::SerialCommunication;
38 using namespace Windows::Devices::Enumeration;
39 using namespace Windows::Storage::Streams;
40 using namespace Windows::Foundation;
41 using namespace Concurrency;
42 using namespace Platform;
43
44 //-----------------------------------------------------------------------------
45 // <SerialControllerImpl::SerialControllerImpl>
46 // Constructor
47 //-----------------------------------------------------------------------------
SerialControllerImpl(SerialController * _owner)48 SerialControllerImpl::SerialControllerImpl( SerialController* _owner)
49 : m_owner( _owner )
50 {
51 }
52
53 //-----------------------------------------------------------------------------
54 // <SerialControllerImpl::~SerialControllerImpl>
55 // Destructor
56 //-----------------------------------------------------------------------------
~SerialControllerImpl()57 SerialControllerImpl::~SerialControllerImpl()
58 {
59 Close();
60 }
61
62 //-----------------------------------------------------------------------------
63 // <SerialControllerImpl::Close>
64 // Close the serial port
65 //-----------------------------------------------------------------------------
Close()66 void SerialControllerImpl::Close()
67 {
68 // cancel read task
69 m_readTaskCancelationTokenSource.cancel();
70 }
71
72 //-----------------------------------------------------------------------------
73 // <SerialControllerImpl::Open>
74 // Open the serial port
75 //-----------------------------------------------------------------------------
Open()76 bool SerialControllerImpl::Open()
77 {
78 Log::Write(LogLevel_Info, "Trying to open serial port %s", m_owner->m_serialControllerName.c_str());
79
80 try
81 {
82 auto selector = SerialDevice::GetDeviceSelectorFromUsbVidPid(0x10C4, 0xEA60);
83
84 return create_task(DeviceInformation::FindAllAsync(selector))
85 .then([this](DeviceInformationCollection ^ devices) -> IAsyncOperation<SerialDevice ^> ^
86 {
87 wstring ourId(m_owner->m_serialControllerName.begin(), m_owner->m_serialControllerName.end());
88 for (auto iterator = devices->First(); iterator->HasCurrent; iterator->MoveNext())
89 {
90 wstring currentId = iterator->Current->Id->Data();
91 if (currentId.find(ourId) != wstring::npos)
92 {
93 return SerialDevice::FromIdAsync(iterator->Current->Id);
94 }
95 }
96 return create_async([]() -> SerialDevice ^ { return nullptr; });
97
98 }).then([this](SerialDevice ^ device) -> bool
99 {
100 if (device == nullptr)
101 {
102 return false;
103 }
104 m_serialDevice = device;
105
106 m_serialDevice->BaudRate = m_owner->m_baud;
107 m_serialDevice->DataBits = 8;
108 switch (m_owner->m_stopBits)
109 {
110 case SerialController::StopBits::StopBits_One:
111 {
112 m_serialDevice->StopBits = SerialStopBitCount::One;
113 break;
114 }
115 case SerialController::StopBits::StopBits_OneAndAHalf:
116 {
117 m_serialDevice->StopBits = SerialStopBitCount::OnePointFive;
118 break;
119 }
120 case SerialController::StopBits::StopBits_Two:
121 {
122 m_serialDevice->StopBits = SerialStopBitCount::Two;
123 break;
124 }
125 }
126
127 switch (m_owner->m_parity)
128 {
129 case SerialController::Parity::Parity_Even:
130 {
131 m_serialDevice->Parity = SerialParity::Even;
132 break;
133 }
134 case SerialController::Parity::Parity_Mark:
135 {
136 m_serialDevice->Parity = SerialParity::Mark;
137 break;
138 }
139 case SerialController::Parity::Parity_None:
140 {
141 m_serialDevice->Parity = SerialParity::None;
142 break;
143 }
144 case SerialController::Parity::Parity_Odd:
145 {
146 m_serialDevice->Parity = SerialParity::Odd;
147 break;
148 }
149 case SerialController::Parity::Parity_Space:
150 {
151 m_serialDevice->Parity = SerialParity::Space;
152 break;
153 }
154 }
155
156 Windows::Foundation::TimeSpan timespan;
157 timespan.Duration = 1;
158 m_serialDevice->ReadTimeout = timespan;
159
160 StartReadTask();
161
162 return true;
163 }).get();
164 }
165 catch (...)
166 {
167 return false;
168 }
169 }
170
171 //-----------------------------------------------------------------------------
172 // <SerialControllerImpl::StartReadTask>
173 // Start a background task which reads available data and passes it along to SerialController
174 //-----------------------------------------------------------------------------
StartReadTask()175 void SerialControllerImpl::StartReadTask()
176 {
177 // Read serial data on background task
178 cancellation_token token = m_readTaskCancelationTokenSource.get_token();
179
180 create_task([token, this]()
181 {
182 uint32 readBufferLength = 512;
183 Buffer ^ buffer = ref new Buffer(readBufferLength);
184
185 for (;;)
186 {
187 try
188 {
189 create_task(m_serialDevice->InputStream->ReadAsync(buffer, readBufferLength, InputStreamOptions::None))
190 .then([&, this](IBuffer ^ outBuffer)
191 {
192 auto reader = DataReader::FromBuffer(outBuffer);
193 auto bytesRead = reader->UnconsumedBufferLength;
194
195 std::vector<uint8> byteVector(bytesRead);
196 if (!byteVector.empty())
197 {
198 reader->ReadBytes(::Platform::ArrayReference<uint8>(byteVector.data(), bytesRead));
199 m_owner->Put(byteVector.data(), bytesRead);
200 }
201 }).wait();
202 }
203 catch (Platform::Exception^ ex)
204 {
205 if (ex->HResult == HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED))
206 {
207 m_owner->Close();
208 }
209 }
210
211 if (token.is_canceled())
212 {
213 cancel_current_task();
214 }
215 }
216 }, token);
217 }
218
219 //-----------------------------------------------------------------------------
220 // <SerialControllerImpl::Write>
221 // Send data to the serial port
222 //-----------------------------------------------------------------------------
Write(uint8 * _buffer,uint32 _length)223 uint32 SerialControllerImpl::Write
224 (
225 uint8* _buffer,
226 uint32 _length
227 )
228 {
229 uint32 retVal = 0;
230
231 if (m_serialDevice == nullptr)
232 {
233 //Error
234 Log::Write(LogLevel_Error, "ERROR: Serial port must be opened before writing\n");
235 return 0;
236 }
237
238 DataWriter ^ writer = ref new DataWriter();
239 writer->WriteBytes(ref new Platform::Array<uint8>(_buffer, _length));
240 try
241 {
242 auto writeTask = create_task(m_serialDevice->OutputStream->WriteAsync(writer->DetachBuffer()));
243
244 // since the consumer of this function expects this to be synchronous, just wait here.
245 retVal = writeTask.get();
246 }
247 catch (Platform::Exception^ )
248 {
249 //ignore - return 0
250 retVal = 0;
251 }
252
253 return retVal;
254 }
255