Skip to content
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
bfad65f
Merge remote-tracking branch 'upstream/master' into 0.10-libftdi1
Dec 2, 2018
78cc4b9
Added overload of Write() with void Write(ola::io::ByteString)
Dec 15, 2018
e1869e3
Indicate that RDM is supported
Dec 15, 2018
2b5a5fa
Initial attempt at adding queue for RDM commands, also added destruct…
Dec 15, 2018
3b6cb83
This a totally disgusting in-between state of the code, I'm about to …
Dec 19, 2018
c51928e
Initial compiling version that implements a RDM discoverable target f…
Dec 19, 2018
10fc5a0
Removed qtcreator files, added to .gitignore and added minor debug ou…
Dec 19, 2018
1bdf897
Simplified RDM implementation only implement the discovery interface
Dec 28, 2018
71bd693
Merge remote-tracking branch 'upstream/master' into ftdi-rdm
Jan 6, 2019
8553598
Another attempt with no fruits...
Jan 8, 2019
81d120b
Added discovery functions to port, now stuff actually gets called.
Jan 8, 2019
8914f56
Some debug output plus detect that less bytes were written then reque…
Jan 8, 2019
9e95824
Some logic fixes that came up now that discovery actually tries to run.
Jan 8, 2019
ff09297
Clean up Port and remove functions not needed.
Jan 8, 2019
851401b
Minor cosmetic change
Jan 8, 2019
1d09fe1
Added some of the RDMReply callback logic.
Jan 8, 2019
b02363b
Set the pending request back to nullptr in the right places and nome …
Jan 8, 2019
c87ebf6
Added FtdiInterface::WriteAndRead() in an attempt to rule out context…
Jan 10, 2019
0141ad0
3 Fixes
Jan 17, 2019
2373f60
Added single function to handle destruction of any outstanding callba…
Jan 27, 2019
2198e3d
Added 'destruction' of callbacks to destructor.
Jan 27, 2019
65e11d8
Switch back to scheduling with seperate write() and read() functions.
Feb 25, 2019
d719f6b
Removal of WriteAndRead() and related scheduler.
Feb 25, 2019
5645a01
Merge remote-tracking branch 'upstream/master' into ftdi-rdm
Feb 25, 2019
a0e6cb4
Fix for part of comments by PeterNewman in PR1541.
Feb 26, 2019
0ba5dfd
Fix part of travis lint issues.
Feb 26, 2019
29b32d0
Added section about RDM support to README.
Feb 26, 2019
f451f19
Change for compiler error suggested by Peter, currently not next to i…
Feb 27, 2019
abdf4d5
Switch to using serial from FTDI device as serial part in RDM UID.
Feb 28, 2019
b080a84
Switched to using constant defined in header for Vendor part of UID.
Feb 28, 2019
9874f6d
Added logic to handle slow replies and make sure a full frame is rece…
Mar 3, 2019
ab0e961
Changed from OLA.* to *.extensions used by QtCreator at request of @P…
Mar 3, 2019
4b72f70
Fix embarrasing mistake with calculation inside sizeof()
Mar 3, 2019
17a40b1
Fix Travis lint issues.
Mar 4, 2019
532c8c3
Fix memory leak.
Mar 4, 2019
d28d3df
Fix more memory leaks.
Mar 6, 2019
92729c4
Fix for OS X building on Travis modified from PR1535
Mar 7, 2019
5f6187e
Further expanded README and hopefully shut up coverity.
Mar 7, 2019
1946005
Another attempt to appease the README markdown coverity complaints.
Mar 7, 2019
a4cfe30
Add another tested interface
peternewman Mar 9, 2019
faa98ca
Add that the Enttec Open DMX USB doesn't work
peternewman Mar 9, 2019
5b150c1
Minor tidy up and add details of more testing
peternewman Mar 9, 2019
fb51664
Merge branch 'ftdi-rdm' of https://github.com/Keeper-of-the-Keys/ola …
Mar 10, 2019
5564a1e
Initial test with nanosleep(2)
Mar 10, 2019
d533789
Added logic for non-branch broadcast packets that I had forgotten.
Mar 10, 2019
6309300
Fix lint issue
Mar 10, 2019
4553fe3
Extended StringToInt per request of @peternewman
Mar 10, 2019
591234c
Switched from use of strtoul() to ola::StringToInt()
Mar 10, 2019
3f60c66
Extended README with pinouts and schematics.
Mar 10, 2019
7d11c58
Enable Discovery-on-patch
Mar 10, 2019
f7489b6
Modify the description generator so that subsequent stuff doesn't blo…
Mar 10, 2019
c4d0ee3
Fix comments from @peternewman
Mar 10, 2019
c8e010f
Minor: remove backticks, they mess up display on github
Mar 10, 2019
7bf7ea5
Github display fix
Mar 10, 2019
6f84568
Changed to using only last 6 characters of serial.
Mar 10, 2019
f144552
Various comments by @peternewman fixed.
Mar 10, 2019
6b08435
Experimental replacement for usleep based on nanosleep.
Mar 11, 2019
b6d41af
Added ola::OlaSleep class based on the experimentalSleep stuff.
Mar 17, 2019
0a4a51e
Switched to using OlaSleep object, still need to remove native CheckG…
Mar 17, 2019
b084dcf
Added not about resistor strength and formatting changes to shut up c…
Mar 17, 2019
b489f96
README layout change to satisfy the coverity demon.
Mar 19, 2019
8d9691d
Rename OlaSleep to Sleep, modify & optimize some of the code.
Mar 19, 2019
1e004ac
Switch from Sleep::usleep() to this->usleep()
Mar 19, 2019
c222943
Added which output port the Interface deals with in Description()
Mar 19, 2019
8410da0
Make sure the Widget 'knows' the correct interface.
Mar 19, 2019
6252026
Lint fix
Mar 19, 2019
3c5a2e6
Detailed caller string for Sleep, some changes with granularity check…
Mar 19, 2019
4e285cb
Set a random deviceId for a device lacking a serial number in the thr…
Mar 20, 2019
c912c7f
Some doxygen changes
Mar 20, 2019
a90c1ba
Forgot to seed pseudo random number generator.
Mar 20, 2019
c640e4e
Merge remote-tracking branch 'upstream/master' into ftdi-rdm
Jun 26, 2019
8b9ff70
Added echo detection function, initially added at thread level howeve…
Jul 28, 2019
c2083af
Switch from srand to ola::math::Random()
Jul 29, 2019
717462d
Add LOG/DEBUG outputs to echo detection
Jul 29, 2019
7b0ecf8
Add logic to Write(ola::io::ByteString) to skip written bytes if echo…
Jul 29, 2019
ed1eb6b
Disable auto discovery.
Jul 29, 2019
5392ab0
Allow RDM to continue if no pending DMX packet and less then second p…
Jul 29, 2019
c666d71
Attempt at making Write(DmxBuffer) use Write(ola::io::ByteString) so
Jul 29, 2019
249d826
Fix description string to actually reflect what port is being used.
Jul 29, 2019
bfc14db
Now works regardless of echo state and biasing resistors.
Jul 29, 2019
48d1dbe
Updated docs to reflect state of driver.
Jul 29, 2019
a198e63
Fix some Travis spellintian issues
Jul 30, 2019
368a2f1
Merge remote-tracking branch 'upstream/master' into ftdi-rdm
Jul 30, 2019
e80f64d
Pad packets smaller then 24 slots to 24 slots, if forced to send out …
Aug 1, 2019
011f591
Fix spelling errors found by 'codespell' Travis-CI check.
Aug 1, 2019
b1dc145
Added FtdiDmxPluginDescription.h to lint test blacklist and moved sai…
Aug 2, 2019
a0d202a
lint file search expression fixed?
Aug 2, 2019
4566ff8
lint find statement fixed
Aug 2, 2019
c85f065
lint find statement
Aug 2, 2019
e48cebd
Another attempt for lint files
Aug 2, 2019
14a4513
fix wrong bracket in lint
Aug 2, 2019
117f831
Another attempt
Aug 2, 2019
1fb4bda
Merge branch 'master' into ftdi-rdm
peternewman Oct 9, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -246,3 +246,4 @@ javascript/new-src/node_modules
.cproject
.settings
.vscode/
OLA.*
24 changes: 21 additions & 3 deletions plugins/ftdidmx/FtdiDmxPort.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@
#include <string>

#include "ola/DmxBuffer.h"
#include "ola/rdm/DiscoveryAgent.h"
#include "ola/rdm/RDMResponseCodes.h"

#include "olad/Port.h"
#include "olad/Preferences.h"

#include "plugins/ftdidmx/FtdiDmxDevice.h"
#include "plugins/ftdidmx/FtdiWidget.h"
#include "plugins/ftdidmx/FtdiDmxThread.h"
Expand All @@ -39,13 +43,14 @@ namespace ola {
namespace plugin {
namespace ftdidmx {

class FtdiDmxOutputPort : public ola::BasicOutputPort {
class FtdiDmxOutputPort
: public ola::BasicOutputPort {
public:
FtdiDmxOutputPort(FtdiDmxDevice *parent,
FtdiInterface *interface,
unsigned int id,
unsigned int freq)
: BasicOutputPort(parent, id),
: BasicOutputPort(parent, id, false, true),
m_interface(interface),
m_thread(interface, freq) {
m_thread.Start();
Expand All @@ -59,11 +64,24 @@ class FtdiDmxOutputPort : public ola::BasicOutputPort {
return m_thread.WriteDMX(buffer);
}

void SendRDMRequest(ola::rdm::RDMRequest *request,
ola::rdm::RDMCallback *callback) {
m_thread.SendRDMRequest(request, callback);
}

void RunFullDiscovery(ola::rdm::RDMDiscoveryCallback *callback) {
m_thread.RunFullDiscovery(callback);
}
void RunIncrementalDiscovery(ola::rdm::RDMDiscoveryCallback *callback) {
m_thread.RunIncrementalDiscovery(callback);
}

std::string Description() const { return m_interface->Description(); }

private:
private:
FtdiInterface *m_interface;
FtdiDmxThread m_thread;

};
} // namespace ftdidmx
} // namespace plugin
Expand Down
243 changes: 238 additions & 5 deletions plugins/ftdidmx/FtdiDmxThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,19 @@
#include <unistd.h>

#include <string>
#include <queue>
#include <utility>

#include "ola/Clock.h"
#include "ola/Logging.h"
#include "ola/StringUtils.h"

#include "ola/rdm/RDMCommand.h"
#include "ola/rdm/RDMControllerInterface.h"
#include "ola/rdm/RDMCommandSerializer.h"
#include "ola/rdm/RDMResponseCodes.h"
#include "ola/rdm/DiscoveryAgent.h"

#include "plugins/ftdidmx/FtdiWidget.h"
#include "plugins/ftdidmx/FtdiDmxThread.h"

Expand All @@ -42,7 +51,15 @@ FtdiDmxThread::FtdiDmxThread(FtdiInterface *interface, unsigned int frequency)
: m_granularity(UNKNOWN),
m_interface(interface),
m_term(false),
m_frequency(frequency) {
m_frequency(frequency),
m_transaction_number(0),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is now managed centrally due to what I did here: https://github.com/OpenLightingProject/ola/pull/1343/files

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean I can remove all code and references to this?

At the moment for this pull I tend to think leave it and then I can remove and test again, but whatever you prefer.

m_discovery_agent(this),
m_uid(0x7a70, 0x12345678),
m_pending_request(nullptr),
m_rdm_callback(nullptr),
m_mute_complete(nullptr),
m_unmute_complete(nullptr),
m_branch_callback(nullptr) {
}

FtdiDmxThread::~FtdiDmxThread() {
Expand All @@ -54,10 +71,17 @@ FtdiDmxThread::~FtdiDmxThread() {
* @brief Stop this thread
*/
bool FtdiDmxThread::Stop() {

{
ola::thread::MutexLocker locker(&m_term_mutex);
m_term = true;
}

if(m_pending_request != nullptr) {
m_pending_request = nullptr;
destroyPendindingCallback(ola::rdm::RDM_FAILED_TO_SEND);
}
m_discovery_agent.Abort();
return Join();
}

Expand All @@ -73,15 +97,133 @@ bool FtdiDmxThread::WriteDMX(const DmxBuffer &buffer) {
}
}

void FtdiDmxThread::SendRDMRequest(ola::rdm::RDMRequest *request,
ola::rdm::RDMCallback *callback) {
ola::thread::MutexLocker locker(&m_rdm_mutex);
if(m_pending_request == nullptr) {
m_pending_request = request;
m_rdm_callback = callback;
} else {
OLA_WARN << "Unable to queue RDM request, RDM operation already pending";
}
OLA_WARN << "Function not properly implemented yet, callback may not get called.";
}

void FtdiDmxThread::RunFullDiscovery(ola::rdm::RDMDiscoveryCallback *callback) {
m_discovery_agent.StartFullDiscovery(ola::NewSingleCallback(this, &FtdiDmxThread::DiscoveryComplete, callback));
}

void FtdiDmxThread::RunIncrementalDiscovery(ola::rdm::RDMDiscoveryCallback *callback) {
m_discovery_agent.StartIncrementalDiscovery(ola::NewSingleCallback(this, &FtdiDmxThread::DiscoveryComplete, callback));
}

/**
* Called when the discovery process finally completes
* @param callback the callback passed to StartFullDiscovery or
* StartIncrementalDiscovery that we should execute.
* @param status true if discovery worked, false otherwise
* @param uids the UIDSet of UIDs that were found.
*/
void FtdiDmxThread::DiscoveryComplete(ola::rdm::RDMDiscoveryCallback *callback,
bool,
const ola::rdm::UIDSet &uids) {
OLA_DEBUG << "FTDI discovery complete: " << uids;
if (callback) {
callback->Run(uids);
}
}

/**
* @brief Method called to cleanup any outstanding callbacks
* @param state
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please either document this parameter or drop the Doxygen line

*
* All callbacks except the RDMCallback lack a way of reporting an error state to the caller.
*/
void FtdiDmxThread::destroyPendindingCallback(ola::rdm::RDMStatusCode state) {
MuteDeviceCallback *thread_mute_callback = nullptr;
UnMuteDeviceCallback *thread_unmute_callback = nullptr;
BranchCallback *thread_branch_callback = nullptr;
ola::rdm::RDMCallback *thread_rdm_callback = nullptr;

if(m_mute_complete != nullptr) {
thread_mute_callback = m_mute_complete;
m_mute_complete = nullptr;
thread_mute_callback->Run(false);
} else if(m_unmute_complete != nullptr) {
thread_unmute_callback = m_unmute_complete;
m_unmute_complete = nullptr;
thread_unmute_callback->Run();
} else if(m_branch_callback != nullptr) {
thread_branch_callback = m_branch_callback;
m_branch_callback = nullptr;
thread_branch_callback->Run(nullptr, 0);
} else if(m_rdm_callback != nullptr) {
thread_rdm_callback = m_rdm_callback;
m_rdm_callback = nullptr;
ola::rdm::RunRDMCallback(thread_rdm_callback, state);
}
}

void FtdiDmxThread::MuteDevice(const ola::rdm::UID &target,
MuteDeviceCallback *mute_complete) {
ola::thread::MutexLocker locker(&m_rdm_mutex);
if(m_pending_request == nullptr) {
OLA_INFO << "Muting device";
m_mute_complete = mute_complete;
m_pending_request = ola::rdm::NewMuteRequest(m_uid, target, m_transaction_number += 1);
} else {
// Already pending request
OLA_WARN << "Unable to queue Mute request, RDM operation already pending";
}
}

void FtdiDmxThread::UnMuteAll(UnMuteDeviceCallback *unmute_complete) {
ola::thread::MutexLocker locker(&m_rdm_mutex);
if(m_pending_request == nullptr) {
OLA_INFO << "Sending UnMuteAll";
m_unmute_complete = unmute_complete;
m_pending_request = ola::rdm::NewUnMuteRequest(m_uid, ola::rdm::UID::AllDevices(), m_transaction_number += 1);
} else {
// Already pending request
OLA_WARN << "Unable to queue UnMuteAll request, RDM operation already pending";
}
}

void FtdiDmxThread::Branch(const ola::rdm::UID &lower,
const ola::rdm::UID &upper,
BranchCallback *callback) {
ola::thread::MutexLocker locker(&m_rdm_mutex);
if(m_pending_request == nullptr) {
OLA_INFO << "Sending branch";
m_branch_callback = callback;
m_pending_request = ola::rdm::NewDiscoveryUniqueBranchRequest(m_uid, lower, upper, m_transaction_number += 1);
} else {
// Already pending request
OLA_WARN << "Unable to queue Branch request, RDM operation already pending";
}
}

/**
* @brief The method called by the thread
*/
void *FtdiDmxThread::Run() {
TimeStamp ts1, ts2, ts3;
OLA_INFO << "Starting FtdiDmxThread";
TimeStamp ts1, ts2, ts3, lastDMX;
Clock clock;
CheckTimeGranularity();
DmxBuffer buffer;
bool sendRDM = false;
TimeInterval elapsed, interval;
int readBytes;
unsigned char readBuffer[258];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again where has this magic number come from? I'm guessing it's max RDM packet size + a bit?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually - FTDI will always add 1 null byte at the beginning (not 100% sure but I think that is the break being treated as data) of DMX data (except for the DUB reply), the max packet length is 256 bytes and 1 more byte just in case.

ola::io::ByteString packetBuffer;

MuteDeviceCallback *thread_mute_callback = nullptr;
UnMuteDeviceCallback *thread_unmute_callback = nullptr;
BranchCallback *thread_branch_callback = nullptr;
ola::rdm::RDMCallback *thread_rdm_callback = nullptr;
ola::rdm::RDMRequest *thread_pending_request = nullptr;


int frameTime = static_cast<int>(floor(
(static_cast<double>(1000) / m_frequency) + static_cast<double>(0.5)));
Expand All @@ -105,6 +247,32 @@ void *FtdiDmxThread::Run() {
}

clock.CurrentTime(&ts1);
if(m_pending_request != nullptr) {

elapsed = ts1 - lastDMX;

if(elapsed.InMilliSeconds() < 500) {
if(!packetBuffer.empty()) {
packetBuffer.clear();
}

if(!ola::rdm::RDMCommandSerializer::PackWithStartCode(*m_pending_request, &packetBuffer)) {
OLA_WARN << "RDMCommandSerializer failed. Dropping packet.";
m_pending_request = nullptr;

destroyPendindingCallback(ola::rdm::RDM_FAILED_TO_SEND);
sendRDM = false;
} else {
OLA_INFO << "OK To send RDM";
sendRDM = true;
}
} else {
OLA_INFO << "NOK to send RDM (DMX interval)";
sendRDM = false;
}
} else {
sendRDM = false;
}

if (!m_interface->SetBreak(true)) {
goto framesleep;
Expand All @@ -122,14 +290,79 @@ void *FtdiDmxThread::Run() {
usleep(DMX_MAB);
}

if (!m_interface->Write(buffer)) {
if(!sendRDM) {
if (!m_interface->Write(buffer)) {
goto framesleep;
} else {
clock.CurrentTime(&lastDMX);
}
} else {
if(m_interface->Write(&packetBuffer)) {
OLA_INFO << "RDM packet written to line";
if(m_pending_request->IsDUB()) {
usleep(58000); //min time before next packet broadcast allowed
readBytes = m_interface->Read(readBuffer, 258);
OLA_INFO << "DUB Read: " << readBytes;
if(m_branch_callback != nullptr) {
thread_branch_callback = m_branch_callback;
m_branch_callback = nullptr;
m_pending_request = nullptr;
thread_branch_callback->Run(readBuffer, (readBytes >= 0 ? readBytes : 0));
}
}
else if(!m_pending_request->DestinationUID().IsBroadcast()) {

usleep(30000); //min time before next packet allowed
readBytes = m_interface->Read(readBuffer, 258);

if(readBytes >=0) {
if(m_mute_complete != nullptr) {
thread_mute_callback = m_mute_complete;
m_mute_complete = nullptr;

if(rdm::RDMReply::FromFrame(rdm::RDMFrame(readBuffer+1, readBytes-1))->Response()->SourceUID() == m_pending_request->DestinationUID()) {
m_pending_request = nullptr;
thread_mute_callback->Run(true);
} else {
m_pending_request = nullptr;
thread_mute_callback->Run(false);
}
} else if(m_rdm_callback != nullptr) {
thread_rdm_callback = m_rdm_callback;
m_rdm_callback = nullptr;

if(readBytes > 0) {
thread_pending_request = m_pending_request;
m_pending_request = nullptr;
thread_rdm_callback->Run(rdm::RDMReply::FromFrame(rdm::RDMFrame(readBuffer+1, readBytes-1), thread_pending_request));
} else {
m_pending_request = nullptr;
RunRDMCallback(thread_rdm_callback, rdm::RDM_TIMEOUT);
}
}
}
} else {
if(m_unmute_complete != nullptr) {
thread_unmute_callback = m_unmute_complete;
m_unmute_complete = nullptr;
OLA_INFO << "UnMuteAllCallback";
m_pending_request = nullptr;
thread_unmute_callback->Run();
}
}
} else {
// Something went wrong, already reported at hw level but we'll need to handle the callbacks
// Strictly speaking we failed to receive OR send, I have proposed another code: RDM_HW_ERROR
destroyPendindingCallback(ola::rdm::RDM_FAILED_TO_SEND);
} // End of Write loop */

goto framesleep;
}

framesleep:
// Sleep for the remainder of the DMX frame time
clock.CurrentTime(&ts2);
TimeInterval elapsed = ts2 - ts1;
elapsed = ts2 - ts1;

if (m_granularity == GOOD) {
while (elapsed.InMilliSeconds() < frameTime) {
Expand All @@ -141,7 +374,7 @@ void *FtdiDmxThread::Run() {
// See if we can drop out of bad mode.
usleep(1000);
clock.CurrentTime(&ts3);
TimeInterval interval = ts3 - ts2;
interval = ts3 - ts2;
if (interval.InMilliSeconds() < BAD_GRANULARITY_LIMIT) {
m_granularity = GOOD;
OLA_INFO << "Switching from BAD to GOOD granularity for ftdi thread";
Expand Down
Loading