diff options
author | RALOVICH, Kristof <tade60@freemail.hu> | 2013-12-14 17:39:37 +0100 |
---|---|---|
committer | RALOVICH, Kristof <tade60@freemail.hu> | 2013-12-14 17:39:37 +0100 |
commit | 86fa7227befe3a3e8ba076924e5910c6a0772227 (patch) | |
tree | 0c50a51bad7793d65b6107926158b83635c03401 /src | |
parent | f1bb20de8eb699ee1de9389fb99fd6d7f6debd98 (diff) |
housekeeping
- rename AntFr310XT2 -> AntFr310XT
- add state machine for 405 (initially copy of 310)
Diffstat (limited to 'src')
-rw-r--r-- | src/AntFr310XT.cpp | 52 | ||||
-rw-r--r-- | src/AntFr310XT.hpp | 10 | ||||
-rw-r--r-- | src/AntFr405.cpp | 740 | ||||
-rw-r--r-- | src/AntFr405.hpp | 90 | ||||
-rw-r--r-- | src/CMakeLists.txt | 10 | ||||
-rw-r--r-- | src/antpm-downloader.cpp | 42 |
6 files changed, 901 insertions, 43 deletions
diff --git a/src/AntFr310XT.cpp b/src/AntFr310XT.cpp index 3689367..27bb7f5 100644 --- a/src/AntFr310XT.cpp +++ b/src/AntFr310XT.cpp @@ -53,9 +53,9 @@ const uchar fsSearchTimeout = 0x03; -struct AntFr310XT2_EventLoop +struct AntFr310XT_EventLoop { - void operator() (AntFr310XT2* arg) + void operator() (AntFr310XT* arg) { //printf("msgFunc, arg: %p\n", arg); fflush(stdout); if(!arg) @@ -63,14 +63,14 @@ struct AntFr310XT2_EventLoop rv=0; return; } - AntFr310XT2* This = reinterpret_cast<AntFr310XT2*>(arg); + AntFr310XT* This = reinterpret_cast<AntFr310XT*>(arg); //printf("msgFunc, This: %p\n", This); fflush(stdout); rv = This->th_eventLoop(); } void* rv; }; -AntFr310XT2::AntFr310XT2(bool eventLoopInBgTh) +AntFr310XT::AntFr310XT(bool eventLoopInBgTh) : m_serial(new ANTPM_SERIAL_IMPL()) , m_antMessenger(new AntMessenger(eventLoopInBgTh)) , aplc(getConfigFolder()+std::string("antparse_")+getDateString()+".txt") @@ -85,14 +85,14 @@ AntFr310XT2::AntFr310XT2(bool eventLoopInBgTh) state = ST_ANTFS_0; m_eventThKill=0; - AntFr310XT2_EventLoop eventTh; + AntFr310XT_EventLoop eventTh; eventTh.rv=0; m_eventTh = boost::thread(eventTh, this); } -AntFr310XT2::~AntFr310XT2() +AntFr310XT::~AntFr310XT() { m_antMessenger->setCallback(0); //m_antMessenger->setHandler(0); @@ -108,14 +108,14 @@ AntFr310XT2::~AntFr310XT2() void -AntFr310XT2::setModeDownloadAll() +AntFr310XT::setModeDownloadAll() { mode = MD_DOWNLOAD_ALL; LOG_VAR2(mode, ModeOfOperation2Str(mode)); } void -AntFr310XT2::setModeDownloadSingleFile( const uint16_t fileIdx ) +AntFr310XT::setModeDownloadSingleFile( const uint16_t fileIdx ) { mode = MD_DOWNLOAD_SINGLE_FILE; singleFileIdx = fileIdx; @@ -123,14 +123,14 @@ AntFr310XT2::setModeDownloadSingleFile( const uint16_t fileIdx ) } void -AntFr310XT2::setModeDirectoryListing() +AntFr310XT::setModeDirectoryListing() { mode = MD_DIRECTORY_LISTING; LOG_VAR2(mode, ModeOfOperation2Str(mode)); } void -AntFr310XT2::setModeEraseSingleFile(const uint16_t fileIdx) +AntFr310XT::setModeEraseSingleFile(const uint16_t fileIdx) { mode = MD_ERASE_SINGLE_FILE; singleFileIdx = fileIdx; @@ -139,7 +139,7 @@ AntFr310XT2::setModeEraseSingleFile(const uint16_t fileIdx) void -AntFr310XT2::setModeEraseAllActivities() +AntFr310XT::setModeEraseAllActivities() { mode = MD_ERASE_ALL_ACTIVITIES; LOG_VAR2(mode, ModeOfOperation2Str(mode)); @@ -147,20 +147,20 @@ AntFr310XT2::setModeEraseAllActivities() void -AntFr310XT2::onAntReceived(const AntMessage m) +AntFr310XT::onAntReceived(const AntMessage m) { postEvent(m); } void -AntFr310XT2::onAntSent(const AntMessage m) +AntFr310XT::onAntSent(const AntMessage m) { } void -AntFr310XT2::start() +AntFr310XT::start() { CHECK_RETURN(m_serial->open()); @@ -176,7 +176,7 @@ AntFr310XT2::start() m_antMessenger->eventLoop(); } -void AntFr310XT2::stop() +void AntFr310XT::stop() { m_eventThKill = 1; //m_eventTh.join(); @@ -192,7 +192,7 @@ void AntFr310XT2::stop() changeState(ST_ANTFS_START0, true); } -void AntFr310XT2::stopAsync() +void AntFr310XT::stopAsync() { // FIXME: setting ST_ANTFS_LAST might not be enough for stopping immediately, // as other thread might be @@ -202,21 +202,21 @@ void AntFr310XT2::stopAsync() const int -AntFr310XT2::getSMState() const +AntFr310XT::getSMState() const { //boost::unique_lock<boost::mutex> lock(this->stateMtx); // not needed, as this is a atomic read return state; } const char* -AntFr310XT2::getSMStateStr() const +AntFr310XT::getSMStateStr() const { //boost::unique_lock<boost::mutex> lock(this->stateMtx); // not needed, as this is a atomic read return StateFSWork2Str(state); } void -AntFr310XT2::postEvent(const AntMessage& m) +AntFr310XT::postEvent(const AntMessage& m) { m_evQue.push(m); } @@ -224,7 +224,7 @@ AntFr310XT2::postEvent(const AntMessage& m) void* -AntFr310XT2::th_eventLoop() +AntFr310XT::th_eventLoop() { for(;;) { @@ -245,7 +245,7 @@ AntFr310XT2::th_eventLoop() bool -AntFr310XT2::handleEvents() +AntFr310XT::handleEvents() { #define changeStateSafe(x) do \ { \ @@ -648,7 +648,7 @@ AntFr310XT2::handleEvents() int -AntFr310XT2::changeState(const int newState, bool force) +AntFr310XT::changeState(const int newState, bool force) { boost::unique_lock<boost::mutex> lock(stateMtx); int oldState = this->state; @@ -665,8 +665,8 @@ AntFr310XT2::changeState(const int newState, bool force) } -AntFr310XT2::StateANTFS -AntFr310XT2::changeFSState(const AntFr310XT2::StateANTFS newState) +AntFr310XT::StateANTFS +AntFr310XT::changeFSState(const AntFr310XT::StateANTFS newState) { StateANTFS oldState = this->clientState; this->clientState = newState; @@ -676,7 +676,7 @@ AntFr310XT2::changeFSState(const AntFr310XT2::StateANTFS newState) bool -AntFr310XT2::createDownloadFolder() +AntFr310XT::createDownloadFolder() { if(!folder.empty()) { @@ -709,7 +709,7 @@ AntFr310XT2::createDownloadFolder() /// TODO: we need to refine these matches based on more trace data bool -AntFr310XT2::guessDeviceType(const ushort devNum, const uchar devId, const uchar transType, GarminProducts* prod) +AntFr310XT::guessDeviceType(const ushort devNum, const uchar devId, const uchar transType, GarminProducts* prod) { if(!prod) return false; diff --git a/src/AntFr310XT.hpp b/src/AntFr310XT.hpp index d809705..371cf01 100644 --- a/src/AntFr310XT.hpp +++ b/src/AntFr310XT.hpp @@ -22,13 +22,13 @@ namespace antpm{ class DeviceSettings; -struct AntFr310XT2_EventLoop; +struct AntFr310XT_EventLoop; // State-machine for ANT+ communication with Forerunner 310XT. -class AntFr310XT2: public AntCallback +class AntFr310XT: public AntCallback { public: - AntFr310XT2(bool eventLoopInBgTh = true); - virtual ~AntFr310XT2(); + AntFr310XT(bool eventLoopInBgTh = true); + virtual ~AntFr310XT(); void setModeForcePairing() { doPairing=true; } void setModeDownloadAll(); @@ -78,7 +78,7 @@ protected: int mode; uint16_t singleFileIdx; private: - friend struct AntFr310XT2_EventLoop; + friend struct AntFr310XT_EventLoop; void* th_eventLoop(); bool handleEvents(); int changeState(const int newState, bool force = false); diff --git a/src/AntFr405.cpp b/src/AntFr405.cpp new file mode 100644 index 0000000..535551a --- /dev/null +++ b/src/AntFr405.cpp @@ -0,0 +1,740 @@ +// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*- +// ***** BEGIN LICENSE BLOCK ***** +//////////////////////////////////////////////////////////////////// +// Copyright (c) 2011-2013 RALOVICH, Kristóf // +// // +// This program is free software; you can redistribute it and/or // +// modify it under the terms of the GNU General Public License // +// version 2 as published by the Free Software Foundation. // +// // +//////////////////////////////////////////////////////////////////// +// ***** END LICENSE BLOCK ***** + +#include "AntFr405.hpp" +#include "SerialTty.hpp" +#include "SerialUsb.hpp" +#include "antdefs.hpp" +#include "common.hpp" +#include "DeviceSettings.hpp" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <functional> +#include <algorithm> +#include <vector> +#include <string> +#include <boost/thread/thread_time.hpp> +#include <iostream> +#include "stdintfwd.hpp" + + + +using namespace std; + +namespace antpm{ + + +const uchar net = 0x00; +const uchar chan = 0x00; +//const uint hostSN = 0x7c9101e0; // from Garmin ANT+ Agent +//const uint64_t pairedKey = 0xd273f79a6f166fa5; +const uint hostSN = 0x00000000;//0xbcdef012; +//const uint64_t pairedKey = 0xa56f166f9af773d2; +const ushort msgPeriod = 0x1000; +const uchar chanSearchTimeout = 0xff; +const uchar rfFreq = 0x32; +const ushort waveform = 0x5300; + +const uchar fsFreq = 0x46; // other values seen: 0x46 0x50 0x0f +const uchar beaconPer = 0x04; +const uchar fsSearchTimeout = 0x03; + + + + +struct AntFr405_EventLoop +{ + void operator() (AntFr405* arg) + { + //printf("msgFunc, arg: %p\n", arg); fflush(stdout); + if(!arg) + { + rv=0; + return; + } + AntFr405* This = reinterpret_cast<AntFr405*>(arg); + //printf("msgFunc, This: %p\n", This); fflush(stdout); + rv = This->th_eventLoop(); + } + void* rv; +}; + +AntFr405::AntFr405(bool eventLoopInBgTh) + : m_serial(new ANTPM_SERIAL_IMPL()) + , m_antMessenger(new AntMessenger(eventLoopInBgTh)) + , aplc(getConfigFolder()+std::string("antparse_")+getDateString()+".txt") + , clientSN(0) + , pairedKey(0) + , m_eventLoopInBgTh(eventLoopInBgTh) + , doPairing(false) + , mode(MD_DOWNLOAD_ALL) +{ + m_antMessenger->setHandler(m_serial.get()); + m_antMessenger->setCallback(this); + state = ST_ANTFS_0; + m_eventThKill=0; + + AntFr405_EventLoop eventTh; + eventTh.rv=0; + m_eventTh = boost::thread(eventTh, this); + +} + + +AntFr405::~AntFr405() +{ + m_antMessenger->setCallback(0); + //m_antMessenger->setHandler(0); + + m_eventThKill=1; + m_eventTh.join(); + state = ST_ANTFS_0; + + m_antMessenger.reset(); + m_serial.reset(); + lprintf(LOG_DBG, "%s\n", __FUNCTION__); +} + + +void +AntFr405::setModeDownloadAll() +{ + mode = MD_DOWNLOAD_ALL; + LOG_VAR2(mode, ModeOfOperation2Str(mode)); +} + +void +AntFr405::setModeDownloadSingleFile( const uint16_t fileIdx ) +{ + mode = MD_DOWNLOAD_SINGLE_FILE; + singleFileIdx = fileIdx; + LOG_VAR2(mode, ModeOfOperation2Str(mode)); +} + +void +AntFr405::setModeDirectoryListing() +{ + mode = MD_DIRECTORY_LISTING; + LOG_VAR2(mode, ModeOfOperation2Str(mode)); +} + +void +AntFr405::setModeEraseSingleFile(const uint16_t fileIdx) +{ + mode = MD_ERASE_SINGLE_FILE; + singleFileIdx = fileIdx; + LOG_VAR2(mode, ModeOfOperation2Str(mode)); +} + + +void +AntFr405::setModeEraseAllActivities() +{ + mode = MD_ERASE_ALL_ACTIVITIES; + LOG_VAR2(mode, ModeOfOperation2Str(mode)); +} + + +void +AntFr405::onAntReceived(const AntMessage m) +{ + postEvent(m); +} + +void +AntFr405::onAntSent(const AntMessage m) +{ +} + + + +void +AntFr405::start() +{ + CHECK_RETURN(m_serial->open()); + + //createDownloadFolder(); + + //m_antMessenger->addListener(boost::bind(&AntFr310XT2::listenerFunc2, this, _1)); + + //m_antMessenger->setCallback(&aplc); + + changeState(ST_ANTFS_START0); + + if(!m_eventLoopInBgTh) + m_antMessenger->eventLoop(); +} + +void AntFr405::stop() +{ + m_eventThKill = 1; + //m_eventTh.join(); + m_antMessenger->kill(); + if(m_serial->isOpen()) + { + if(state>ST_ANTFS_LINKING) + m_antMessenger->ANTFS_Disconnect(chan); + m_antMessenger->ANT_CloseChannel(chan); + m_antMessenger->ANT_ResetSystem(); + } + m_serial->close(); + changeState(ST_ANTFS_START0, true); +} + +void AntFr405::stopAsync() +{ + // FIXME: setting ST_ANTFS_LAST might not be enough for stopping immediately, + // as other thread might be + // sleeping in a listener, and we stop only when that returns. + changeState(ST_ANTFS_LAST); +} + + +const int +AntFr405::getSMState() const +{ + //boost::unique_lock<boost::mutex> lock(this->stateMtx); // not needed, as this is a atomic read + return state; +} + +const char* +AntFr405::getSMStateStr() const +{ + //boost::unique_lock<boost::mutex> lock(this->stateMtx); // not needed, as this is a atomic read + return StateFSWork2Str(state); +} + +void +AntFr405::postEvent(const AntMessage& m) +{ + m_evQue.push(m); +} + + + +void* +AntFr405::th_eventLoop() +{ + for(;;) + { + if(m_eventThKill) + break; + if(handleEvents()) + { + //sleepms(2); + } + else + { + changeState(ST_ANTFS_BAD); + sleepms(1000); + } + } + return 0; +} + + +bool +AntFr405::handleEvents() +{ +#define changeStateSafe(x) do \ + { \ + boost::unique_lock<boost::mutex> lock(this->stateMtx); \ + if(this->state == ST_ANTFS_LAST) \ + { \ + lock.unlock(); \ + this->stop(); \ + return true; \ + } \ + else \ + { \ + lock.unlock(); \ + this->changeState(x); \ + } \ + } while(0) + +#define checkForExit() do \ + { \ + boost::unique_lock<boost::mutex> lock(this->stateMtx); \ + if(this->state == ST_ANTFS_LAST) \ + { \ + lock.unlock(); \ + this->stop(); \ + return true; \ + } \ + } while(0) + + while(!m_evQue.empty()) + { + AntMessage m; + m_evQue.pop(m); + if(m.getMsgId()==MESG_RESPONSE_EVENT_ID) + { + //uint8_t chan=m.getPayloadRef()[0]; + uint8_t msgId=m.getPayloadRef()[1]; + if(msgId==MESG_EVENT_ID) + { + uint8_t msgCode = m.getPayloadRef()[2]; + if(msgCode==EVENT_RX_SEARCH_TIMEOUT) // handle RX_SEARCH_TIMEOUT + { + changeState(ST_ANTFS_BAD); + } + } + } + } + + // new state machine + if(state==ST_ANTFS_RESTART) + { + m_evQue.clear(); + //m_antMessenger->clearRxQueue(); + m_antMessenger->ANT_ResetSystem(); + m_antMessenger->ANT_ResetSystem(); + changeStateSafe(ST_ANTFS_START0); + } + if(state==ST_ANTFS_START0) + { + CHECK_RETURN_FALSE(m_antMessenger->ANT_ResetSystem()); + + //CHECK_RETURN_FALSE(m_antMessenger->ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID)); + + CHECK_RETURN_FALSE(m_antMessenger->ANT_SetNetworkKey(net, ANTP_NETKEY)); + + CHECK_RETURN_FALSE(m_antMessenger->ANT_AssignChannel(chan,0,net)); + + CHECK_RETURN_FALSE(m_antMessenger->ANT_SetChannelMessagePeriod(chan, msgPeriod)); + + CHECK_RETURN_FALSE(m_antMessenger->ANT_SetChannelSearchTimeout(chan, chanSearchTimeout)); + CHECK_RETURN_FALSE(m_antMessenger->ANT_SetChannelRadioFreq(chan, rfFreq)); + CHECK_RETURN_FALSE(m_antMessenger->ANT_SetSearchWaveform(chan, waveform)); + CHECK_RETURN_FALSE(m_antMessenger->ANT_SetChannelId(chan, 0, 0x01, 0x05)); + CHECK_RETURN_FALSE(m_antMessenger->ANT_OpenChannel(chan)); + CHECK_RETURN_FALSE(m_antMessenger->ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID)); + + changeStateSafe(ST_ANTFS_LINKING); + } + else if(state==ST_ANTFS_LINKING) + { + AntMessage m; + //CHECK_RETURN_FALSE(m_antMessenger->waitForBroadcastDataAvail(chan, &m, 20000));//link beacon + CHECK_RETURN_FALSE(m_antMessenger->waitForBroadcast(chan, &m, 20000));//link beacon + M_ANTFS_Beacon* beacon(reinterpret_cast<M_ANTFS_Beacon*>(&m.getPayloadRef()[1])); + // TODO:handle case of no available data + if(!beacon->dataAvail) + { + changeStateSafe(ST_ANTFS_NODATA); + LOG(LOG_RAW) << "\n\nNo data available from client!\n\n\n"; + return true; + } + + //CHECK_RETURN_FALSE(m_antMessenger->ANT_RequestMessage(chan, MESG_CHANNEL_ID_ID)); + ushort devNum=0; + uchar devId=0; + uchar transType=0; + CHECK_RETURN_FALSE(m_antMessenger->ANT_GetChannelId(chan, &devNum, &devId, &transType, 1000)); + LOG(LOG_RAW) << "\n\nFound device devNum=0x" << toString<ushort>(devNum) << " devId=0x" << toString<uint>(devId,2,'0') << " transType=0x" << toString<uint>(transType,2,'0') << "\n\n\n"; + GarminProducts prod; + if(guessDeviceType(devNum, devId, transType, &prod)) + { + if(prod==GarminFR310XT) { LOG(LOG_INF) << "guessed: GarminFR310XT\n\n\n"; } + if(prod==GarminFR405) { LOG(LOG_INF) << "guessed: GarminFR405\n\n\n"; } + } + else { LOG(LOG_WARN) << "guessing failed!\n"; } + + CHECK_RETURN_FALSE(m_antMessenger->ANTFS_Link(chan, fsFreq, beaconPer, hostSN)); + + changeStateSafe(ST_ANTFS_AUTH0_SN); + } + else if(state == ST_ANTFS_BAD) + { + // TODO: acc counter how many BADs we handle, then bail out + m_antMessenger->ANT_CloseChannel(chan); + sleepms(800); + changeStateSafe(ST_ANTFS_RESTART); + return true; + } + else if(state == ST_ANTFS_AUTH0_SN) + { + CHECK_RETURN_FALSE(m_antMessenger->ANT_SetChannelMessagePeriod(chan, msgPeriod)); + CHECK_RETURN_FALSE(m_antMessenger->ANT_SetChannelSearchTimeout(chan, fsSearchTimeout)); + CHECK_RETURN_FALSE(m_antMessenger->ANT_SetChannelRadioFreq(chan, fsFreq)); + + CHECK_RETURN_FALSE(m_antMessenger->waitForBroadcast(chan));//auth beacon + + // R 105.996 40 MESG_RESPONSE_EVENT_ID chan=0x00 mId=MESG_EVENT_ID mCode=EVENT_RX_FAIL + //CHECK_RETURN_FALSE(m_antMessenger->waitForRxFail(chan)); + + //CHECK_RETURN_FALSE_LOG_OK(m_antMessenger->waitForBroadcast(chan)); + + CHECK_RETURN_FALSE_LOG_OK(m_antMessenger->ANTFS_RequestClientDeviceSerialNumber(chan, hostSN, clientSN, clientDevName)); + + LOG(LOG_RAW) << "\n\nFound client \"" << clientDevName << "\" SN=0x" << toString<uint>(clientSN,8,'0') << " SN=" << clientSN << "\n\n\n"; + + m_ds.reset(new DeviceSettings(toStringDec<uint>(clientSN).c_str())); + assert(m_ds.get()); + m_ds->loadDefaultValues(); + m_ds->loadFromFile(m_ds->getConfigFileName()); + m_ds->saveToFile(m_ds->getConfigFileName()); + + readUInt64(clientSN, pairedKey); + + LOG_VAR3(doPairing, toString<uint64_t>(pairedKey,16,'0'), clientSN); + if(doPairing || pairedKey==0) + changeStateSafe(ST_ANTFS_AUTH1_PAIR); + else + changeStateSafe(ST_ANTFS_AUTH1_PASS); + } + else if(state == ST_ANTFS_AUTH1_PAIR) + { + const std::string hostName("libantpm"); + uint dummy; + if(!m_antMessenger->ANTFS_Pairing(chan, hostSN, hostName, dummy, pairedKey)) + { + changeStateSafe(ST_ANTFS_LAST); + return true; + } + writeUInt64(clientSN, pairedKey); + + CHECK_RETURN_FALSE_LOG_OK(m_antMessenger->ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID)); + + //changeStateSafe(ST_ANTFS_LAST); + changeStateSafe(ST_ANTFS_AUTH1_PASS); + } + else if(state == ST_ANTFS_AUTH1_PAIR) + { + //FIXME: + //m_antMessenger->ANTFS_Pairing(chan, hostSN, ); + } + else if(state == ST_ANTFS_AUTH1_PASS) + { + CHECK_RETURN_FALSE_LOG_OK(m_antMessenger->ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID)); + + if(!m_antMessenger->ANTFS_Authenticate(chan, hostSN, pairedKey)) + { + changeStateSafe(ST_ANTFS_RESTART); + return true; + } + + LOG(LOG_RAW) << "\n\nClient authenticated successfully!\n\n\n"; + + // channel status <> + CHECK_RETURN_FALSE_LOG_OK(m_antMessenger->ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID)); + + if(mode==MD_DOWNLOAD_ALL || mode==MD_DIRECTORY_LISTING) + changeStateSafe(ST_ANTFS_DL_DIRECTORY); + else if(mode==MD_DOWNLOAD_SINGLE_FILE) + changeStateSafe(ST_ANTFS_DL_SINGLE_FILE); + else if(mode==MD_ERASE_SINGLE_FILE) + changeStateSafe(ST_ANTFS_ERASE_SINGLE_FILE); + else + changeStateSafe(ST_ANTFS_LAST); + } + else if(state == ST_ANTFS_DL_DIRECTORY) + { + CHECK_RETURN_FALSE(createDownloadFolder()); + + //ANTFS_Upload(); //command pipe + //ANTFS_UploadData(); + + std::vector<uchar> dir; + if(!m_antMessenger->ANTFS_Download(chan, 0x0000, dir)) + { + changeStateSafe(ST_ANTFS_RESTART); + return true; + } + LOG(LOG_RAW) << "\n\nDownloaded directory file idx=0x0000\n\n\n"; + + AntFsFile file0; + file0.bytes=dir; + LOG_VAR(file0.checkCrc()); + file0.saveToFile((folder+"0000.fit").c_str()); + + CHECK_RETURN_FALSE(fit.parseZeroFile(dir, zfc)); + LOG_VAR(zfc.activityFiles.size()); + LOG_VAR(zfc.courseFiles.size()); + LOG_VAR(zfc.waypointsFiles.size()); + + // TODO: read bcast here? + + // channel status <> + //CHECK_RETURN_FALSE_LOG_OK(ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID)); + if(mode==MD_DIRECTORY_LISTING) + changeStateSafe(ST_ANTFS_LAST); + else + changeStateSafe(ST_ANTFS_DL_FILES); + } + else if(state==ST_ANTFS_DL_FILES) + { + // standard operation: download everything between LastUserProfileTime and now + // dl waypoint files + // dl activity files + // dl course files + // NOTE: seems like, if a file was downloaded, it's date in the directory file changes to the date of transfer + + uint fileCnt=0; + for(size_t i=0; i<zfc.waypointsFiles.size() && fileCnt<m_ds->MaxFileDownloads; i++) + { + checkForExit(); + LOG_VAR3(fileCnt, m_ds->MaxFileDownloads, zfc.waypointsFiles.size()); + ushort fileIdx = zfc.waypointsFiles[i]; + time_t t = GarminConvert::gOffsetTime(zfc.getFitFileTime(fileIdx)); + if(t < m_ds->LastUserProfileTime) + { + logger() << "Skipping waypoints file 0x" << toString<ushort>(fileIdx,4,'0') + << "@" << DeviceSettings::time2str(t) << " older than " + << DeviceSettings::time2str(m_ds->LastUserProfileTime) << "\n"; + continue; + } + logger() << "Transfer waypoints file 0x" << hex << fileIdx << "\n"; + + std::vector<uchar> data; + if(!m_antMessenger->ANTFS_Download(chan, fileIdx, data)) + { + changeStateSafe(ST_ANTFS_LAST); + return true; + } + LOG(LOG_RAW) << "\n\nDownloaded file idx=" << toString<ushort>(fileIdx,4,'0') << "\n\n\n"; + AntFsFile file0; file0.bytes=data; file0.saveToFile((folder+toString(fileIdx, 4, '0')+".fit").c_str()); + LOG_VAR(file0.checkCrc()); + + fit.parse(data, gpx); + + time_t fitDate; + if(!FIT::getCreationDate(data, fitDate)) + fitDate = t; + //m_ds->mergeLastUserProfileTime(fitDate); + + fileCnt++; + } + + for (size_t i=0; i<zfc.activityFiles.size() && fileCnt<m_ds->MaxFileDownloads; i++) + { + checkForExit(); + LOG_VAR3(fileCnt, m_ds->MaxFileDownloads, zfc.activityFiles.size()); + ushort fileIdx = zfc.activityFiles[i]; + time_t t = GarminConvert::gOffsetTime(zfc.getFitFileTime(fileIdx)); + if(t < m_ds->LastUserProfileTime) + { + logger() << "Skipping activity file 0x" << toString<ushort>(fileIdx,4,'0') + << "@" << DeviceSettings::time2str(t) << " older than " + << DeviceSettings::time2str(m_ds->LastUserProfileTime) << "\n"; + continue; + } + logger() << "# Transfer activity file 0x" << hex << fileIdx << "\n"; + + std::vector<uchar> data; + if(!m_antMessenger->ANTFS_Download(chan, fileIdx, data)) + { + changeStateSafe(ST_ANTFS_LAST); + return true; + } + LOG(LOG_RAW) << "\n\nDownloaded file idx=" << toString<ushort>(fileIdx,4,'0') << "\n\n\n"; + AntFsFile file0; file0.bytes=data; file0.saveToFile((folder+toString(fileIdx, 4, '0')+".fit").c_str()); + + fit.parse(data, gpx); + + time_t fitDate=0; + if(!FIT::getCreationDate(data, fitDate)) + { + LOG_VAR3(fitDate, t, m_ds->LastUserProfileTime); + fitDate = t; + } + else + { + fitDate = GarminConvert::gOffsetTime(fitDate); + LOG_VAR3(DeviceSettings::time2str(fitDate), DeviceSettings::time2str(t), DeviceSettings::time2str(m_ds->LastUserProfileTime)); + } + //m_ds->mergeLastUserProfileTime(fitDate); // can't update it in the middle of the loop + + fileCnt++; + } + + for (size_t i=0; i<zfc.courseFiles.size() && fileCnt<m_ds->MaxFileDownloads; i++) + { + checkForExit(); + LOG_VAR3(fileCnt, m_ds->MaxFileDownloads, zfc.courseFiles.size()); + ushort fileIdx = zfc.courseFiles[i]; + time_t t = GarminConvert::gOffsetTime(zfc.getFitFileTime(fileIdx)); + if(t < m_ds->LastUserProfileTime) + { + logger() << "Skipping course file 0x" << toString<ushort>(fileIdx,4,'0') + << "@" << DeviceSettings::time2str(t) << " older than " + << DeviceSettings::time2str(m_ds->LastUserProfileTime) << "\n"; + continue; + } + logger() << "Transfer course file 0x" << hex << fileIdx << "\n"; + + std::vector<uchar> data; + if(!m_antMessenger->ANTFS_Download(chan, fileIdx, data)) + { + changeStateSafe(ST_ANTFS_LAST); + return true; + } + LOG(LOG_RAW) << "\n\nDownloaded file idx=" << toString<ushort>(fileIdx,4,'0') << "\n\n\n"; + AntFsFile file0; file0.bytes=data; file0.saveToFile((folder+toString(fileIdx, 4, '0')+".fit").c_str()); + + fit.parse(data, gpx); + + // FIXME: case of fit date in future, also check the date from the directory file for consistency + time_t fitDate; + if(!FIT::getCreationDate(data, fitDate)) + fitDate = t; + //m_ds->mergeLastUserProfileTime(fitDate); + + fileCnt++; + } + + std::string gpxFile=folder+"libantpm.gpx"; + logger() << "Writing output to '" << gpxFile << "'\n"; + gpx.writeToFile(gpxFile); + + m_ds->mergeLastTransferredTime(time(NULL)); + m_ds->saveToFile(m_ds->getConfigFileName()); + + changeStateSafe(ST_ANTFS_LAST); + } + else if(state==ST_ANTFS_DL_SINGLE_FILE) + { + logger() << "Transfer of file 0x" << hex << singleFileIdx << dec << "\n"; + + std::vector<uchar> data; + if(!m_antMessenger->ANTFS_Download(chan, singleFileIdx, data)) + { + changeStateSafe(ST_ANTFS_LAST); + return true; + } + LOG(LOG_RAW) << "\n\nDownloaded file idx=" << toString<ushort>(singleFileIdx,4,'0') << "\n\n\n"; + AntFsFile file0; file0.bytes=data; file0.saveToFile((folder+toString(singleFileIdx, 4, '0')+".fit").c_str());//...might not be a fit file + LOG_VAR(file0.checkCrc()); + + fit.parse(data, gpx); + + std::string gpxFile=folder+"libantpm.gpx"; + logger() << "Writing output to '" << gpxFile << "'\n"; + gpx.writeToFile(gpxFile); + + changeStateSafe(ST_ANTFS_LAST); + } + else if(state==ST_ANTFS_ERASE_SINGLE_FILE) + { + CHECK_RETURN_FALSE_LOG_OK(m_antMessenger->ANTFS_Erase(chan, singleFileIdx)); + + LOG(LOG_RAW) << "\n\nErased file idx=" << toString<ushort>(singleFileIdx,4,'0') << "\n\n\n"; + + changeStateSafe(ST_ANTFS_LAST); + } + else if(state==ST_ANTFS_NODATA) + { + changeStateSafe(ST_ANTFS_LAST); + } + else if(state==ST_ANTFS_LAST) + { + stop(); + } + + return true; +#undef changeStateSafe +} + + +int +AntFr405::changeState(const int newState, bool force) +{ + boost::unique_lock<boost::mutex> lock(stateMtx); + int oldState = this->state; + if(oldState == ST_ANTFS_LAST && newState != ST_ANTFS_LAST&& !force) + { + LOG(LOG_WARN) << "This seems to be a bug, we've tried ST_ANTFS_LAST => " << StateFSWork2Str(newState) << "!\n\n"; + return oldState; + } + this->state = newState; + LOG(antpm::LOG_RAW) << "\nSTATE: " << std::dec << oldState << " => " << newState + << "\t " << StateFSWork2Str(oldState) << " => " << StateFSWork2Str(newState) + << "\n\n"; + return oldState; +} + + +AntFr405::StateANTFS +AntFr405::changeFSState(const AntFr405::StateANTFS newState) +{ + StateANTFS oldState = this->clientState; + this->clientState = newState; + LOG(antpm::LOG_RAW) << "\nFS: " << std::dec << oldState << " => " << newState << "\n\n"; + return oldState; +} + + +bool +AntFr405::createDownloadFolder() +{ + if(!folder.empty()) + { + LOG(LOG_WARN) << "folder is \"" << folder << "\", why not empty?\n"; + //return false; + } + CHECK_RETURN_FALSE(m_ds); + if(clientSN==0) + { + LOG(LOG_WARN) << "this is strange, clientSN is 0!\n"; + return false; + } + std::stringstream ss; + //ss << getConfigFolder() << "/" << clientSN << "/" << getDateString() + "/"; + ss << m_ds->getFolder() << "/" << getDateString() + "/"; + folder = ss.str(); + //folder = getConfigFolder() + "/" + getDateString() + "/"; + if(!mkDir(folder.c_str())) + { + if(folderExists(folder.c_str())) + return true; + else + { + LOG(LOG_ERR) << "Folder \"" << folder << "\" doesn't exist, and could not be created either!\n"; + return false; + } + } + return true; +} + +/// TODO: we need to refine these matches based on more trace data +bool +AntFr405::guessDeviceType(const ushort devNum, const uchar devId, const uchar transType, GarminProducts* prod) +{ + if(!prod) + return false; + + // 310 + // devNum=0x4cd4, devId=0x01, transType=0x05 + // Beacon=8Hz + if( devNum==0x4cd4 ) + { + *prod = GarminFR310XT; + return true; + } + + + // 405 + // devNum=0xc12e, devId=0x01, transType=0x05 + // Beacon=1Hz + if( devNum==0xc12e ) + { + *prod = GarminFR405; + return true; + } + + + return false; +} + +} diff --git a/src/AntFr405.hpp b/src/AntFr405.hpp new file mode 100644 index 0000000..6de3931 --- /dev/null +++ b/src/AntFr405.hpp @@ -0,0 +1,90 @@ +// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*- +// ***** BEGIN LICENSE BLOCK ***** +//////////////////////////////////////////////////////////////////// +// Copyright (c) 2011-2013 RALOVICH, Kristóf // +// // +// This program is free software; you can redistribute it and/or // +// modify it under the terms of the GNU General Public License // +// version 2 as published by the Free Software Foundation. // +// // +//////////////////////////////////////////////////////////////////// +// ***** END LICENSE BLOCK ***** +#pragma once + +#include "AntMessenger.hpp" +#include <queue> +#include "antdefs.hpp" +#include <list> +#include <memory> +#include <boost/thread.hpp> +#include "FIT.hpp" + +namespace antpm{ + +class DeviceSettings; +struct AntFr405_EventLoop; +// State-machine for ANT+ communication with Forerunner 405. +class AntFr405: public AntCallback +{ +public: + AntFr405(bool eventLoopInBgTh = true); + virtual ~AntFr405(); + + void setModeForcePairing() { doPairing=true; } + void setModeDownloadAll(); + void setModeDownloadSingleFile(const uint16_t fileIdx); + void setModeDirectoryListing(); + void setModeEraseSingleFile(const uint16_t fileIdx); + void setModeEraseAllActivities(); + + virtual void onAntReceived(const AntMessage m); + virtual void onAntSent(const AntMessage m); + + void start(); + void stop(); + void stopAsync(); + + const int getSMState() const; + const char* getSMStateStr() const; + AntMessenger* getMessenger() { return m_antMessenger.get(); } + + void postEvent(const AntMessage& m); + +protected: + boost::scoped_ptr<Serial> m_serial; + boost::scoped_ptr<AntMessenger> m_antMessenger; + typedef enum { LINK,AUTHENTICATION,TRANSPORT,BUSY} StateANTFS; + StateANTFS clientState; + int state; + boost::mutex stateMtx; + volatile int m_eventThKill; + boost::thread m_eventTh; + lqueue4<AntMessage> m_evQue; + AntParsedLoggerCallback aplc; + boost::scoped_ptr<DeviceSettings> m_ds; + + FIT fit; + ZeroFileContent zfc; + GPX gpx; + + uint clientSN; + std::string clientDevName; + uint64_t pairedKey; + + bool m_eventLoopInBgTh; + + bool doPairing; + std::string folder; + int mode; + uint16_t singleFileIdx; +private: + friend struct AntFr405_EventLoop; + void* th_eventLoop(); + bool handleEvents(); + int changeState(const int newState, bool force = false); + StateANTFS changeFSState(const StateANTFS newState); + bool createDownloadFolder(); + static bool guessDeviceType(const ushort devNum, const uchar devId, const uchar transType, GarminProducts *prod); +}; + +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 348eeda..16c1da2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,7 @@ PROJECT(antpm C CXX) OPTION(USE_COVERAGE "use gcov" FALSE)
OPTION(USE_BOOST_TEST "use boost::test" FALSE)
+OPTION(USE_BOOST_STATIC_LINK "link statically against boost" TRUE)
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
@@ -48,12 +49,16 @@ IF(NOT LIBUSB_FOUND) MESSAGE(FATAL_ERROR "Could not find LIBUSB")
ENDIF()
-set(Boost_USE_STATIC_LIBS ON)
+IF(USE_BOOST_STATIC_LINK)
+ set(Boost_USE_STATIC_LIBS ON)
+ELSE()
+ set(Boost_USE_STATIC_LIBS OFF)
+ENDIF()
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
FIND_PACKAGE(Boost 1.41.0 COMPONENTS thread unit_test_framework program_options system filesystem date_time REQUIRED)
IF(NOT Boost_FOUND)
- MESSAGE(FATAL_ERROR "Boost not found, please set BOOST_ROOT!")
+ MESSAGE(FATAL_ERROR "Boost not found!")
ENDIF()
@@ -81,6 +86,7 @@ SET(SRCS AntMessenger.hpp AntMessenger.cpp
SerialUsb.hpp SerialUsb.cpp
AntFr310XT.hpp AntFr310XT.cpp
+ AntFr405.hpp AntFr405.cpp
lqueue.hpp
Serial.hpp
diff --git a/src/antpm-downloader.cpp b/src/antpm-downloader.cpp index 65d381c..7521108 100644 --- a/src/antpm-downloader.cpp +++ b/src/antpm-downloader.cpp @@ -18,6 +18,7 @@ #include "AntMessage.hpp" #include "AntFr310XT.hpp" +#include "AntFr405.hpp" #include "common.hpp" #include "Log.hpp" @@ -202,19 +203,40 @@ main(int argc, char** argv) LOG_VAR4(pairing, dirOnly, int(dlFileIdx), int(eraseFileIdx)); - AntFr310XT2 watch2(false); - stopFunc = boost::bind(&AntFr310XT2::stopAsync, &watch2); + char* ANTPM_405 = getenv("ANTPM_405"); + if(ANTPM_405!=NULL && strncmp("1",ANTPM_405,1)==0) { - watch2.setModeDownloadAll(); - if(pairing) watch2.setModeForcePairing(); - if(dirOnly) watch2.setModeDirectoryListing(); - else if(dlFileIdx!=0x0000) watch2.setModeDownloadSingleFile(dlFileIdx); - else if(eraseFileIdx!=0x000) watch2.setModeEraseSingleFile(eraseFileIdx); - - watch2.start(); + AntFr405 watch2(false); + stopFunc = boost::bind(&AntFr405::stopAsync, &watch2); + { + watch2.setModeDownloadAll(); + if(pairing) watch2.setModeForcePairing(); + if(dirOnly) watch2.setModeDirectoryListing(); + else if(dlFileIdx!=0x0000) watch2.setModeDownloadSingleFile(dlFileIdx); + else if(eraseFileIdx!=0x000) watch2.setModeEraseSingleFile(eraseFileIdx); + + watch2.start(); + + + watch2.stop(); + } + } + else + { + AntFr310XT watch2(false); + stopFunc = boost::bind(&AntFr310XT::stopAsync, &watch2); + { + watch2.setModeDownloadAll(); + if(pairing) watch2.setModeForcePairing(); + if(dirOnly) watch2.setModeDirectoryListing(); + else if(dlFileIdx!=0x0000) watch2.setModeDownloadSingleFile(dlFileIdx); + else if(eraseFileIdx!=0x000) watch2.setModeEraseSingleFile(eraseFileIdx); + watch2.start(); - watch2.stop(); + + watch2.stop(); + } } |