• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mcallegari / qlcplus / 13633248611

03 Mar 2025 02:31PM UTC coverage: 31.871% (+0.4%) from 31.5%
13633248611

push

github

web-flow
actions: add chrpath to profile

14689 of 46089 relevant lines covered (31.87%)

26426.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.65
/plugins/enttecwing/src/playbackwing.cpp
1
/*
2
  Q Light Controller
3
  playbackwing.cpp
4

5
  Copyright (c) Heikki Junnila
6

7
  Licensed under the Apache License, Version 2.0 (the "License");
8
  you may not use this file except in compliance with the License.
9
  You may obtain a copy of the License at
10

11
      http://www.apache.org/licenses/LICENSE-2.0.txt
12

13
  Unless required by applicable law or agreed to in writing, software
14
  distributed under the License is distributed on an "AS IS" BASIS,
15
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
  See the License for the specific language governing permissions and
17
  limitations under the License.
18
*/
19

20
#include <QHostAddress>
21
#include <QMessageBox>
22
#include <QByteArray>
23
#include <QUdpSocket>
24
#include <QString>
25
#include <QDebug>
26

27
#include "playbackwing.h"
28

29

30
/****************************************************************************
31
 * Playback wing specifics
32
 ****************************************************************************/
33
/*
34
The ENTTEC Playback wing produces packets that contain
35
WING_PLAYBACK_BUTTON_SIZE bytes of button data. Each bit in the button bytes
36
signifies the state of one button. Thus, each byte contains 8 buttons, and
37
they are in reversed order, with some of them (27, 28, 29, 22, 23, 24, 25, 26)
38
mixed up in a very weird way:
39

40
WING_PLAYBACK_BYTE_BUTTON +
41
                04  03  02  01  00
42
----------------------------------
43
bit 0 : buttons 07, 15, 28, 31, 39
44
bit 1 : buttons 06, 14, 27, 30, 38
45
bit 2 : buttons 05, 13, 21, 26, 37
46
bit 3 : buttons 04, 12, 20, 25, 36
47
bit 4 : buttons 03, 11, 19, 24, 35
48
bit 5 : buttons 02, 10, 18, 23, 34
49
bit 6 : buttons 01, 09, 17, 22, 33
50
bit 7 : buttons 00, 08, 16, 29, 32
51

52
As it can be seen from the table above, the byte order is also reversed:
53

54
WING_PLAYBACK_BYTE_BUTTON + 0: Buttons 32 - 39 (8 buttons)
55
WING_PLAYBACK_BYTE_BUTTON + 1: Buttons 24 - 31 (8 buttons)
56
WING_PLAYBACK_BYTE_BUTTON + 2: Buttons 16 - 23 (8 buttons)
57
WING_PLAYBACK_BYTE_BUTTON + 3: Buttons 08 - 15 (8 buttons)
58
WING_PLAYBACK_BYTE_BUTTON + 4: Buttons 00 - 07 (8 buttons)
59

60
The Playback Wing contains also WING_PLAYBACK_SLIDER_SIZE bytes of slider data,
61
where each byte contains an 8bit char value signifying the slider value. Slider
62
bytes are not reversed:
63

64
WING_PLAYBACK_BYTE_SLIDER + 0: Slider 01 (0-255)
65
WING_PLAYBACK_BYTE_SLIDER + 1: Slider 02 (0-255)
66
WING_PLAYBACK_BYTE_SLIDER + 2: Slider 03 (0-255)
67
WING_PLAYBACK_BYTE_SLIDER + 3: Slider 04 (0-255)
68
WING_PLAYBACK_BYTE_SLIDER + 4: Slider 05 (0-255)
69
WING_PLAYBACK_BYTE_SLIDER + 5: Slider 06 (0-255)
70
WING_PLAYBACK_BYTE_SLIDER + 6: Slider 07 (0-255)
71
WING_PLAYBACK_BYTE_SLIDER + 7: Slider 08 (0-255)
72
WING_PLAYBACK_BYTE_SLIDER + 8: Slider 09 (0-255)
73
WING_PLAYBACK_BYTE_SLIDER + 9: Slider 10 (0-255)
74
*/
75

76
#define WING_PLAYBACK_BYTE_BUTTON 7 /* Bytes 7-11 are for buttons */
77
#define WING_PLAYBACK_BUTTON_SIZE 5 /* 5 bytes of button states */
78

79
#define WING_PLAYBACK_BYTE_SLIDER 15 /* Bytes 15-25 are for sliders */
80
#define WING_PLAYBACK_SLIDER_SIZE 10 /* 10 slider values in all */
81

82
#define WING_PLAYBACK_PACKET_SIZE (WING_PLAYBACK_BYTE_SLIDER + WING_PLAYBACK_SLIDER_SIZE) /* 25, total packet size */
83

84
#define WING_PLAYBACK_BYTE_EXTRA_BUTTONS 6
85
#define WING_PLAYBACK_BIT_PAGEUP   (1 << 7)
86
#define WING_PLAYBACK_BIT_PAGEDOWN (1 << 6)
87
#define WING_PLAYBACK_BIT_BACK     (1 << 5)
88
#define WING_PLAYBACK_BIT_GO       (1 << 4)
89

90
/** Should constitute up to 50 channels */
91
#define WING_PLAYBACK_CHANNEL_COUNT 8 * WING_PLAYBACK_BUTTON_SIZE \
92
                                        + WING_PLAYBACK_SLIDER_SIZE
93

94
/** number of extra buttons (go,back, pageup, pagedown) */
95
#define WING_PLAYBACK_EXTRA_BUTTONS_COUNT 4
96
#define WING_PLAYBACK_BUTTON_GO 50
97
#define WING_PLAYBACK_BUTTON_BACK 51
98
#define WING_PLAYBACK_BUTTON_PAGEDOWN 52
99
#define WING_PLAYBACK_BUTTON_PAGEUP 53
100

101

102
#define WING_PLAYBACK_INPUT_VERSION 1
103
#define WING_PLAYBACK_INPUT_BYTE_VERSION 4
104
#define WING_PLAYBACK_INPUT_BYTE_PAGE 37
105

106
/****************************************************************************
107
 * Initialization
108
 ****************************************************************************/
109

110
PlaybackWing::PlaybackWing(QObject* parent, const QHostAddress& address,
1✔
111
                           const QByteArray& data)
1✔
112
    : Wing(parent, address, data)
1✔
113
{
114
    m_values = QByteArray((WING_PLAYBACK_CHANNEL_COUNT) + (WING_PLAYBACK_EXTRA_BUTTONS_COUNT), 0);
1✔
115

116
    /* Playback wing keys seem to be in a somewhat weird order */
117
    m_channelMap[0] = 7 + WING_PLAYBACK_SLIDER_SIZE;
1✔
118
    m_channelMap[1] = 6 + WING_PLAYBACK_SLIDER_SIZE;
1✔
119
    m_channelMap[2] = 5 + WING_PLAYBACK_SLIDER_SIZE;
1✔
120
    m_channelMap[3] = 4 + WING_PLAYBACK_SLIDER_SIZE;
1✔
121
    m_channelMap[4] = 3 + WING_PLAYBACK_SLIDER_SIZE;
1✔
122
    m_channelMap[5] = 2 + WING_PLAYBACK_SLIDER_SIZE;
1✔
123
    m_channelMap[6] = 1 + WING_PLAYBACK_SLIDER_SIZE;
1✔
124
    m_channelMap[7] = 0 + WING_PLAYBACK_SLIDER_SIZE;
1✔
125

126
    m_channelMap[8] = 15 + WING_PLAYBACK_SLIDER_SIZE;
1✔
127
    m_channelMap[9] = 14 + WING_PLAYBACK_SLIDER_SIZE;
1✔
128
    m_channelMap[10] = 13 + WING_PLAYBACK_SLIDER_SIZE;
1✔
129
    m_channelMap[11] = 12 + WING_PLAYBACK_SLIDER_SIZE;
1✔
130
    m_channelMap[12] = 11 + WING_PLAYBACK_SLIDER_SIZE;
1✔
131
    m_channelMap[13] = 10 + WING_PLAYBACK_SLIDER_SIZE;
1✔
132
    m_channelMap[14] = 9 + WING_PLAYBACK_SLIDER_SIZE;
1✔
133
    m_channelMap[15] = 8 + WING_PLAYBACK_SLIDER_SIZE;
1✔
134

135
    /* Weird order here */
136
    m_channelMap[16] = 28 + WING_PLAYBACK_SLIDER_SIZE;
1✔
137
    m_channelMap[17] = 27 + WING_PLAYBACK_SLIDER_SIZE;
1✔
138
    m_channelMap[18] = 21 + WING_PLAYBACK_SLIDER_SIZE;
1✔
139
    m_channelMap[19] = 20 + WING_PLAYBACK_SLIDER_SIZE;
1✔
140
    m_channelMap[20] = 19 + WING_PLAYBACK_SLIDER_SIZE;
1✔
141
    m_channelMap[21] = 18 + WING_PLAYBACK_SLIDER_SIZE;
1✔
142
    m_channelMap[22] = 17 + WING_PLAYBACK_SLIDER_SIZE;
1✔
143
    m_channelMap[23] = 16 + WING_PLAYBACK_SLIDER_SIZE;
1✔
144

145
    /* Weird order also here */
146
    m_channelMap[24] = 31 + WING_PLAYBACK_SLIDER_SIZE;
1✔
147
    m_channelMap[25] = 30 + WING_PLAYBACK_SLIDER_SIZE;
1✔
148
    m_channelMap[26] = 26 + WING_PLAYBACK_SLIDER_SIZE;
1✔
149
    m_channelMap[27] = 25 + WING_PLAYBACK_SLIDER_SIZE;
1✔
150
    m_channelMap[28] = 24 + WING_PLAYBACK_SLIDER_SIZE;
1✔
151
    m_channelMap[29] = 23 + WING_PLAYBACK_SLIDER_SIZE;
1✔
152
    m_channelMap[30] = 22 + WING_PLAYBACK_SLIDER_SIZE;
1✔
153
    m_channelMap[31] = 29 + WING_PLAYBACK_SLIDER_SIZE;
1✔
154

155
    m_channelMap[32] = 39 + WING_PLAYBACK_SLIDER_SIZE;
1✔
156
    m_channelMap[33] = 38 + WING_PLAYBACK_SLIDER_SIZE;
1✔
157
    m_channelMap[34] = 37 + WING_PLAYBACK_SLIDER_SIZE;
1✔
158
    m_channelMap[35] = 36 + WING_PLAYBACK_SLIDER_SIZE;
1✔
159
    m_channelMap[36] = 35 + WING_PLAYBACK_SLIDER_SIZE;
1✔
160
    m_channelMap[37] = 34 + WING_PLAYBACK_SLIDER_SIZE;
1✔
161
    m_channelMap[38] = 33 + WING_PLAYBACK_SLIDER_SIZE;
1✔
162
    m_channelMap[39] = 32 + WING_PLAYBACK_SLIDER_SIZE;
1✔
163

164
    m_needSync = true;
1✔
165

166
    /* Take initial values from the first received datagram packet.
167
       The plugin hasn't yet connected to valueChanged() signal, so this
168
       won't cause any input events. */
169
    parseData(data);
1✔
170
    sendPageData();
1✔
171
}
1✔
172

173
PlaybackWing::~PlaybackWing()
2✔
174
{
175
}
2✔
176

177
/****************************************************************************
178
 * Wing data
179
 ****************************************************************************/
180

181
QString PlaybackWing::name() const
3✔
182
{
183
    QString name("Playback");
3✔
184
    name += QString(" ") + tr("at") + QString(" ");
9✔
185
    name += m_address.toString();
6✔
186

187
    return name;
3✔
188
}
×
189

190
/****************************************************************************
191
 * Input data
192
 ****************************************************************************/
193

194
void PlaybackWing::parseData(const QByteArray& data)
76✔
195
{
196
    if (data.size() < WING_PLAYBACK_PACKET_SIZE)
76✔
197
    {
198
        qWarning() << Q_FUNC_INFO << "Expected at least" << WING_PLAYBACK_PACKET_SIZE
2✔
199
                   << "bytes for buttons but got only" << data.size();
1✔
200
        return;
1✔
201
    }
202

203
    /* Check if page buttons were pressed and act accordingly */
204
    applyExtraButtons(data);
75✔
205

206
    int size = WING_PLAYBACK_BYTE_BUTTON + WING_PLAYBACK_BUTTON_SIZE;
207

208
    /* Read the state of each button */
209
    for (int byte = size - 1; byte >= WING_PLAYBACK_BYTE_BUTTON; byte--)
450✔
210
    {
211
        /* Each byte has 8 button values as binary bits */
212
        for (int bit = 7; bit >= 0; bit--)
3,375✔
213
        {
214
            uchar value;
215

216
            /* Calculate the key number, which is 10-49, since
217
               sliders are mapped to 0-9. */
218
            int key = (size - byte - 1) * 8;
3,000✔
219
            key += bit;
3,000✔
220

221
            /* 0 = button down, 1 = button up */
222
            if ((data[byte] & (1 << bit)) == 0)
3,000✔
223
                value = UCHAR_MAX;
224
            else
225
                value = 0;
226

227
            /* Get the correct channel number for each key. */
228
            setCacheValue(m_channelMap[key], value);
3,000✔
229
        }
230
    }
231

232
    //size = WING_PLAYBACK_BYTE_SLIDER + WING_PLAYBACK_SLIDER_SIZE;
233

234
    /* Read the state of each slider. Each value takes all 8 bits. */
235
    for (int slider = 0; slider < WING_PLAYBACK_SLIDER_SIZE; slider++)
825✔
236
    {
237
        if (m_needSync)
750✔
238
        {
239
            // store value diffs to sync qlcplus widgets and wing slider
240
            if (!m_feedbackDiffs.contains(page()))
10✔
241
                m_feedbackDiffs.insert(page(), QVector<int>(WING_PLAYBACK_SLIDER_SIZE, 0));
1✔
242

243
            m_feedbackDiffs[page()][slider] = quint8(m_feedbackValues[page()][slider]) - quint8(cacheValue(slider));
30✔
244
        }
245

246
        int diff = 0;
247
        if (m_feedbackDiffs.contains(page()))
750✔
248
            diff = m_feedbackDiffs[page()][slider];
1,500✔
249

250
        char value = data[WING_PLAYBACK_BYTE_SLIDER + slider];
750✔
251

252
        if (m_feedbackValues.contains(page()) && diff != 0)
750✔
253
        {
254
            //check sync status
255
            int curdiff = quint8(m_feedbackValues[page()][slider]) - quint8(data[WING_PLAYBACK_BYTE_SLIDER + slider]);
×
256

257
            // send input after crossing widget values (sign of diff is changing)
258
            if (curdiff == 0 || (curdiff > 0 && diff < 0)  || (curdiff < 0 && diff > 0))
×
259
            {
260
                setCacheValue(slider, value);
×
261
                if (m_feedbackDiffs.contains(page()))
×
262
                    m_feedbackDiffs[page()][slider] = 0;
×
263
            }
264
        }
265
        else
266
        {
267
            setCacheValue(slider, value);
750✔
268
        }
269
    }
270
    m_needSync = false;
75✔
271
}
272

273
void PlaybackWing::applyExtraButtons(const QByteArray& data)
75✔
274
{
275
    /* Check that there's enough data for flags */
276
    if (data.size() < WING_PLAYBACK_PACKET_SIZE)
75✔
277
        return;
278

279
    // WING_PLAYBACK_BIT_PAGEUP
280
    if (!(data[WING_PLAYBACK_BYTE_EXTRA_BUTTONS] & WING_PLAYBACK_BIT_PAGEUP))
75✔
281
    {
282
        nextPage();
75✔
283
        sendPageData();
75✔
284
        setCacheValue(WING_PLAYBACK_BUTTON_PAGEUP, UCHAR_MAX);
75✔
285
    }
286
    else
287
    {
288
        setCacheValue(WING_PLAYBACK_BUTTON_PAGEUP, 0);
×
289
    }
290

291
    // WING_PLAYBACK_BIT_PAGEDOWN
292
    if (!(data[WING_PLAYBACK_BYTE_EXTRA_BUTTONS] & WING_PLAYBACK_BIT_PAGEDOWN))
75✔
293
    {
294
        previousPage();
75✔
295
        sendPageData();
75✔
296
        setCacheValue(WING_PLAYBACK_BUTTON_PAGEDOWN, UCHAR_MAX);
75✔
297
    }
298
    else
299
    {
300
        setCacheValue(WING_PLAYBACK_BUTTON_PAGEDOWN, 0);
×
301
    }
302

303
    // WING_PLAYBACK_BIT_PAGEGO
304
    if (!(data[WING_PLAYBACK_BYTE_EXTRA_BUTTONS] & WING_PLAYBACK_BIT_GO))
75✔
305
    {
306
        setCacheValue(WING_PLAYBACK_BUTTON_GO, UCHAR_MAX);
75✔
307
    }
308
    else
309
    {
310
        setCacheValue(WING_PLAYBACK_BUTTON_GO, 0);
×
311
    }
312

313
    // WING_PLAYBACK_BIT_PAGEBACK
314
    if (!(data[WING_PLAYBACK_BYTE_EXTRA_BUTTONS] & WING_PLAYBACK_BIT_BACK))
75✔
315
    {
316
        setCacheValue(WING_PLAYBACK_BUTTON_BACK, UCHAR_MAX);
75✔
317
    }
318
    else
319
    {
320
        setCacheValue(WING_PLAYBACK_BUTTON_BACK, 0);
×
321
    }
322
}
323

324
void PlaybackWing::sendPageData()
151✔
325
{
326
    QByteArray sendData(42, char(0));
151✔
327
    sendData.replace(0, sizeof(WING_HEADER_INPUT), WING_HEADER_INPUT);
151✔
328
    sendData[WING_PLAYBACK_INPUT_BYTE_VERSION] = WING_PLAYBACK_INPUT_VERSION;
151✔
329
    sendData[WING_PLAYBACK_INPUT_BYTE_PAGE] = page() + 1;
151✔
330

331
    QUdpSocket sock(this);
151✔
332
    sock.writeDatagram(sendData, address(), Wing::UDPPort);
151✔
333
}
151✔
334

335
void PlaybackWing::feedBack(quint32 channel, uchar value)
×
336
{
337
    quint16 pageChan = channel & 0xFF;
×
338
    quint16 pageNum = channel >> 16;
×
339

340
    // create new byte array for page, to store values, if it not exists
341
    if (!m_feedbackValues.contains(pageNum))
×
342
        m_feedbackValues.insert(pageNum, QByteArray(WING_PLAYBACK_SLIDER_SIZE, 0));
×
343

344
    if (pageChan < WING_PLAYBACK_SLIDER_SIZE)
×
345
    {
346
        // store widget values for later use
347
        m_feedbackValues[pageNum][pageChan] = value;
×
348

349
        // check sync
350
        if (value != cacheValue(pageChan))
×
351
            m_needSync = true;
×
352
    }
353

354
    //set page
355
    if (pageChan == WING_PLAYBACK_BUTTON_PAGEDOWN || pageChan == WING_PLAYBACK_BUTTON_PAGEUP)
×
356
    {
357
        m_needSync = true;
×
358
        m_page = value;
×
359
        sendPageData();
×
360
    }
361
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc