OVMS3/OVMS.V3/components/retools_testerpresent/src/retools_testerpresent.cpp

320 lines
9.2 KiB
C++

/*
; Project: Open Vehicle Monitor System
; Date: 15th September 2020
;
; Changes:
; 1.0 Initial release
;
; (C) 2011 Michael Stegen / Stegen Electronics
; (C) 2011-2017 Mark Webb-Johnson
; (C) 2011 Sonny Chen @ EPRO/DX
; (C) 2020 Chris Staite
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
*/
#include "ovms_log.h"
static const char *TAG = "re-tp";
#include "retools_testerpresent.h"
#include "vehicle.h"
namespace {
bool ReadHexString(const char* value, unsigned long& output)
{
char* ptr = nullptr;
errno = 0;
output = strtoul(value, &ptr, 16);
if (ptr == value || errno != 0 || *ptr)
{
return false;
}
return true;
}
canbus* GetCan(int bus)
{
char name[] = "canx";
name[3] = '0' + bus;
auto can = reinterpret_cast<canbus*>(MyPcpApp.FindDeviceByName(name));
if (can != nullptr && (can->GetPowerMode() != On || can->m_mode != CAN_MODE_ACTIVE))
{
can = nullptr;
}
return can;
}
} // anon namespace
OvmsReToolsTesterPresent::OvmsReToolsTesterPresent(
canbus* bus, uint16_t ecu, int interval) :
m_bus(bus),
m_id(ecu),
m_ticker(0u),
m_interval(interval)
{ }
void OvmsReToolsTesterPresent::Ticker1()
{
++m_ticker;
if ((m_ticker % m_interval) == 0)
{
SendTesterPresent();
}
}
void OvmsReToolsTesterPresent::SendTesterPresent()
{
CAN_frame_t testerFrame = {
m_bus,
nullptr,
{ .B = { 8, 0, CAN_no_RTR, CAN_frame_std, 0 } },
m_id,
{ .u8 = {
(ISOTP_FT_SINGLE<<4) + 2, VEHICLE_POLL_TYPE_TESTERPRESENT, 0, 0, 0, 0, 0, 0
} }
};
if (m_bus->Write(&testerFrame) == ESP_FAIL)
{
ESP_LOGE(TAG, "Error sending tester present to %03x", m_id);
}
}
class OvmsReToolsTesterPresentInit
{
public:
OvmsReToolsTesterPresentInit();
~OvmsReToolsTesterPresentInit();
void Start();
void Stop();
private:
static void Start(int, OvmsWriter*, OvmsCommand*, int, const char* const*);
static void List(int, OvmsWriter*, OvmsCommand*, int, const char* const*);
static void Stop(int, OvmsWriter*, OvmsCommand*, int, const char* const*);
static void StopAll(int, OvmsWriter*, OvmsCommand*, int, const char* const*);
void Ticker1(std::string, void*);
bool m_registered;
OvmsMutex m_testersMutex;
std::vector<OvmsReToolsTesterPresent> m_testers;
} OvmsReToolsTesterPresentInitInstance __attribute__ ((init_priority (8801)));
OvmsReToolsTesterPresentInit::OvmsReToolsTesterPresentInit() :
m_registered(false),
m_testersMutex(),
m_testers()
{
OvmsCommand* cmd_reobdii = MyCommandApp.FindCommandFullName("re obdii");
if (cmd_reobdii == nullptr)
{
ESP_LOGE(TAG, "Tester present command depends on re command");
return;
}
OvmsCommand* cmd_tester = cmd_reobdii->RegisterCommand(
"tester", "Tester present sender"
);
cmd_tester->RegisterCommand(
"start", "Start sending a tester present signal to an ECU",
PickOvmsCommandExecuteCallback(OvmsReToolsTesterPresentInit::Start),
"<bus> <ecu> <interval>", 3, 3
);
cmd_tester->RegisterCommand(
"list", "List current tester present signals",
&OvmsReToolsTesterPresentInit::List
);
cmd_tester->RegisterCommand(
"stop", "Stop tester present for an ECU",
PickOvmsCommandExecuteCallback(OvmsReToolsTesterPresentInit::Stop),
"<bus> <ecu>", 2, 2
);
cmd_tester->RegisterCommand(
"stopall", "Stop tester present for all ECUs",
&OvmsReToolsTesterPresentInit::StopAll
);
}
void OvmsReToolsTesterPresentInit::Start()
{
if (m_registered == true)
{
return;
}
ESP_LOGD(TAG, "Starting tester present ticker");
MyEvents.RegisterEvent(
TAG, "ticker.1",
std::bind(
&OvmsReToolsTesterPresentInit::Ticker1, this,
std::placeholders::_1, std::placeholders::_2
)
);
m_registered = true;
}
void OvmsReToolsTesterPresentInit::Stop()
{
if (m_registered == false)
{
return;
}
ESP_LOGD(TAG, "Stopping tester present ticker");
MyEvents.DeregisterEvent(TAG);
m_registered = false;
}
OvmsReToolsTesterPresentInit::~OvmsReToolsTesterPresentInit()
{
Stop();
}
void OvmsReToolsTesterPresentInit::Ticker1(std::string, void*)
{
OvmsMutexLock lock(&OvmsReToolsTesterPresentInitInstance.m_testersMutex);
for (auto& tester : OvmsReToolsTesterPresentInitInstance.m_testers)
{
tester.Ticker1();
}
}
void OvmsReToolsTesterPresentInit::Start(
int, OvmsWriter* writer, OvmsCommand*, int, const char* const* argv)
{
unsigned long bus, ecu;
bool valid = true;
if (!ReadHexString(argv[0], bus) || bus < 1 || bus > 4)
{
writer->printf("Error: Invalid bus to scan %s", argv[0]);
valid = false;
}
if (!ReadHexString(argv[1], ecu) || ecu <= 0 || ecu >= 0xfff)
{
writer->printf("Error: Invalid ECU Id to scan %s", argv[1]);
valid = false;
}
int interval = atoi(argv[2]);
if (interval <= 0)
{
writer->printf("Error: Invalid interval %s", argv[2]);
valid = false;
}
if (!valid)
{
return;
}
canbus* can = GetCan(bus);
if (can == nullptr)
{
writer->printf("CAN not started in active mode, please start and try again");
valid = false;
}
if (valid)
{
OvmsMutexLock lock(&OvmsReToolsTesterPresentInitInstance.m_testersMutex);
for (auto& tester : OvmsReToolsTesterPresentInitInstance.m_testers)
{
if (tester.Bus() == can && tester.Ecu() == ecu)
{
writer->printf("Already sending tester present to that ECU");
return;
}
}
if (OvmsReToolsTesterPresentInitInstance.m_testers.empty())
{
OvmsReToolsTesterPresentInitInstance.Start();
}
OvmsReToolsTesterPresentInitInstance.m_testers.push_back(
OvmsReToolsTesterPresent(can, ecu, interval)
);
}
}
void OvmsReToolsTesterPresentInit::List(
int, OvmsWriter* writer, OvmsCommand*, int, const char* const*)
{
OvmsMutexLock lock(&OvmsReToolsTesterPresentInitInstance.m_testersMutex);
for (auto& tester : OvmsReToolsTesterPresentInitInstance.m_testers)
{
writer->printf(
"Tester present sending to %03x on bus %d every %d seconds",
tester.Ecu(), tester.Bus()->m_busnumber + 1, tester.Interval()
);
}
}
void OvmsReToolsTesterPresentInit::Stop(
int, OvmsWriter* writer, OvmsCommand*, int, const char* const* argv)
{
unsigned long bus, ecu;
bool valid = true;
if (!ReadHexString(argv[0], bus) || bus < 1 || bus > 4)
{
writer->printf("Error: Invalid bus %s", argv[0]);
valid = false;
}
if (!ReadHexString(argv[1], ecu) || ecu <= 0 || ecu >= 0xfff)
{
writer->printf("Error: Invalid ECU Id %s", argv[1]);
valid = false;
}
if (!valid)
{
return;
}
OvmsMutexLock lock(&OvmsReToolsTesterPresentInitInstance.m_testersMutex);
auto it = OvmsReToolsTesterPresentInitInstance.m_testers.begin();
while (it != OvmsReToolsTesterPresentInitInstance.m_testers.end())
{
if (it->Bus()->m_busnumber + 1 == bus && it->Ecu() == ecu)
{
OvmsReToolsTesterPresentInitInstance.m_testers.erase(it);
writer->puts("Stopped sending");
break;
}
++it;
}
if (it == OvmsReToolsTesterPresentInitInstance.m_testers.end())
{
writer->puts("Error: Not sending tester present to that");
}
if (OvmsReToolsTesterPresentInitInstance.m_testers.empty())
{
OvmsReToolsTesterPresentInitInstance.Stop();
}
}
void OvmsReToolsTesterPresentInit::StopAll(
int, OvmsWriter* writer, OvmsCommand*, int, const char* const*)
{
OvmsMutexLock lock(&OvmsReToolsTesterPresentInitInstance.m_testersMutex);
if (OvmsReToolsTesterPresentInitInstance.m_testers.empty())
{
writer->puts("Error: Not sending tester present to anything");
}
else
{
OvmsReToolsTesterPresentInitInstance.m_testers.clear();
OvmsReToolsTesterPresentInitInstance.Stop();
writer->puts("Stopped all");
}
}