392 lines
12 KiB
C++
392 lines
12 KiB
C++
/*
|
|
; Project: Open Vehicle Monitor System
|
|
; Date: 14th March 2017
|
|
;
|
|
; Changes:
|
|
; 1.0 Initial release
|
|
;
|
|
; (C) 2011 Michael Stegen / Stegen Electronics
|
|
; (C) 2011-2017 Mark Webb-Johnson
|
|
; (C) 2011 Sonny Chen @ EPRO/DX
|
|
;
|
|
; 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.
|
|
*/
|
|
#ifndef __COMMAND_H__
|
|
#define __COMMAND_H__
|
|
|
|
#include <unistd.h>
|
|
#include <stdarg.h>
|
|
#include <string>
|
|
#include <map>
|
|
#include <set>
|
|
#include <list>
|
|
#include <functional>
|
|
#include <limits.h>
|
|
#include "ovms.h"
|
|
#include "ovms_utils.h"
|
|
#include "ovms_mutex.h"
|
|
#include "task_base.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "microrl_config.h"
|
|
|
|
#define COMMAND_RESULT_MINIMAL 140
|
|
#define COMMAND_RESULT_SMS 160
|
|
#define COMMAND_RESULT_NORMAL 1024
|
|
#define COMMAND_RESULT_VERBOSE 65535
|
|
|
|
class OvmsWriter;
|
|
class OvmsCommand;
|
|
class OvmsCommandMap;
|
|
class LogBuffers;
|
|
typedef std::map<TaskHandle_t, LogBuffers*> PartialLogs;
|
|
typedef bool (*InsertCallback)(OvmsWriter* writer, void* userData, char);
|
|
|
|
class OvmsWriter
|
|
{
|
|
public:
|
|
OvmsWriter();
|
|
virtual ~OvmsWriter();
|
|
|
|
public:
|
|
virtual int puts(const char* s) { return 0; }
|
|
virtual int printf(const char* fmt, ...) { return 0; }
|
|
virtual ssize_t write(const void *buf, size_t nbyte) { return 0; }
|
|
virtual char** SetCompletion(int index, const char* token) { return NULL; }
|
|
virtual char** GetCompletions() { return NULL; }
|
|
virtual void SetArgv(const char* const* argv) { return; }
|
|
virtual const char* const* GetArgv() { return NULL; }
|
|
virtual void Log(LogBuffers* message) {};
|
|
virtual void Exit();
|
|
virtual bool IsInteractive() { return true; }
|
|
void RegisterInsertCallback(InsertCallback cb, void* ctx);
|
|
void DeregisterInsertCallback(InsertCallback cb);
|
|
virtual void finalise() {}
|
|
virtual void ProcessChar(char c) {}
|
|
|
|
public:
|
|
bool IsSecure();
|
|
virtual void SetSecure(bool secure=true);
|
|
bool IsMonitoring() { return m_monitoring; }
|
|
void SetMonitoring(bool mon=true) { m_monitoring = mon; }
|
|
|
|
public:
|
|
bool m_issecure;
|
|
|
|
protected:
|
|
InsertCallback m_insert;
|
|
void* m_userData;
|
|
bool m_monitoring;
|
|
};
|
|
|
|
template <typename T>
|
|
class NameMap : public std::map<std::string, T>
|
|
{
|
|
public:
|
|
const T* FindUniquePrefix(const char* token) const
|
|
{
|
|
size_t len = strlen(token);
|
|
const T* found = NULL;
|
|
for (typename NameMap<T>::const_iterator it = NameMap<T>::begin(); it != NameMap<T>::end(); ++it)
|
|
{
|
|
if (it->first.compare(0, len, token) == 0)
|
|
{
|
|
if (len == it->first.length())
|
|
return &it->second;
|
|
if (found)
|
|
return NULL;
|
|
else
|
|
found = &it->second;
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
bool GetCompletion(OvmsWriter* writer, const char* token) const
|
|
{
|
|
unsigned int index = 0;
|
|
bool match = false;
|
|
writer->SetCompletion(index, NULL);
|
|
if (token)
|
|
{
|
|
size_t len = strlen(token);
|
|
for (typename NameMap<T>::const_iterator it = NameMap<T>::begin(); it != NameMap<T>::end(); ++it)
|
|
{
|
|
if (it->first.compare(0, len, token) == 0)
|
|
{
|
|
writer->SetCompletion(index++, it->first.c_str());
|
|
match = true;
|
|
}
|
|
}
|
|
}
|
|
return match;
|
|
}
|
|
|
|
int Validate(OvmsWriter* writer, int argc, const char* token, bool complete) const
|
|
{
|
|
if (complete)
|
|
{
|
|
if (!GetCompletion(writer, token))
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
if (FindUniquePrefix(token) == NULL)
|
|
{
|
|
if (strcmp(token, "?") != 0)
|
|
writer->printf("Error: %s is not defined\n", token);
|
|
return -1;
|
|
}
|
|
}
|
|
return argc;
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
class CNameMap : public std::map<const char*, T, CmpStrOp>
|
|
{
|
|
public:
|
|
const T* FindUniquePrefix(const char* token) const
|
|
{
|
|
size_t len = strlen(token);
|
|
const T* found = NULL;
|
|
for (typename CNameMap<T>::const_iterator it = CNameMap<T>::begin(); it != CNameMap<T>::end(); ++it)
|
|
{
|
|
if (strncmp(it->first, token, len) == 0)
|
|
{
|
|
if (len == strlen(it->first))
|
|
return &it->second;
|
|
if (found)
|
|
return NULL;
|
|
else
|
|
found = &it->second;
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
bool GetCompletion(OvmsWriter* writer, const char* token) const
|
|
{
|
|
unsigned int index = 0;
|
|
bool match = false;
|
|
writer->SetCompletion(index, NULL);
|
|
if (token)
|
|
{
|
|
size_t len = strlen(token);
|
|
for (typename CNameMap<T>::const_iterator it = CNameMap<T>::begin(); it != CNameMap<T>::end(); ++it)
|
|
{
|
|
if (strncmp(it->first, token, len) == 0)
|
|
{
|
|
writer->SetCompletion(index++, it->first);
|
|
match = true;
|
|
}
|
|
}
|
|
}
|
|
return match;
|
|
}
|
|
|
|
int Validate(OvmsWriter* writer, int argc, const char* token, bool complete) const
|
|
{
|
|
if (complete)
|
|
{
|
|
if (!GetCompletion(writer, token))
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
if (FindUniquePrefix(token) == NULL)
|
|
{
|
|
if (strcmp(token, "?") != 0)
|
|
writer->printf("Error: %s is not defined\n", token);
|
|
return -1;
|
|
}
|
|
}
|
|
return argc;
|
|
}
|
|
};
|
|
|
|
struct CompareCharPtr
|
|
{
|
|
bool operator()(const char* a, const char* b);
|
|
};
|
|
|
|
class OvmsCommandMap : public std::map<const char*, OvmsCommand*, CompareCharPtr>
|
|
{
|
|
public:
|
|
OvmsCommand* FindUniquePrefix(const char* key);
|
|
OvmsCommand* FindCommand(const char* key);
|
|
char** GetCompletion(OvmsWriter* writer, const char* token);
|
|
};
|
|
|
|
typedef std::function<void(int, OvmsWriter*, OvmsCommand*, int, const char* const*)> OvmsCommandExecuteCallback_t;
|
|
typedef std::function<int(OvmsWriter*, OvmsCommand*, int, const char* const*, bool)> OvmsCommandValidateCallback_t;
|
|
|
|
// Compiler utilities: pick the matching method overload in case of ambiguity:
|
|
#define PickOvmsCommandExecuteCallback(method) static_cast<void(*)(int, OvmsWriter*, OvmsCommand*, int, const char* const*)>(method)
|
|
#define PickOvmsCommandValidateCallback(method) static_cast<int(*)(OvmsWriter*, OvmsCommand*, int, const char* const*, bool)>(method)
|
|
|
|
class OvmsCommand : public ExternalRamAllocated
|
|
{
|
|
public:
|
|
OvmsCommand();
|
|
OvmsCommand(const char* name, const char* title,
|
|
OvmsCommandExecuteCallback_t execute,
|
|
const char *usage, int min, int max, bool secure,
|
|
OvmsCommandValidateCallback_t validate);
|
|
virtual ~OvmsCommand();
|
|
|
|
public:
|
|
OvmsCommand* RegisterCommand(const char* name, const char* title,
|
|
OvmsCommandExecuteCallback_t execute = NULL,
|
|
const char *usage = "", int min = 0, int max = 0, bool secure = true,
|
|
OvmsCommandValidateCallback_t validate = NULL);
|
|
bool UnregisterCommand(const char* name = NULL);
|
|
const char* GetName();
|
|
const char* GetTitle();
|
|
const char* GetUsage(OvmsWriter* writer);
|
|
char ** Complete(OvmsWriter* writer, int argc, const char * const * argv);
|
|
void Execute(int verbosity, OvmsWriter* writer, int argc, const char * const * argv);
|
|
OvmsCommand* GetParent();
|
|
OvmsCommand* FindCommand(const char* name);
|
|
bool IsSecure() { return m_secure; }
|
|
void Display(OvmsWriter* writer, int level);
|
|
void PutUsage(OvmsWriter* writer);
|
|
|
|
private:
|
|
void ExpandUsage(const char* templ, OvmsWriter* writer, std::string& result);
|
|
|
|
protected:
|
|
const char* m_name;
|
|
const char* m_title;
|
|
OvmsCommandExecuteCallback_t m_execute;
|
|
OvmsCommandValidateCallback_t m_validate;
|
|
const char* m_usage_template;
|
|
int m_min;
|
|
int m_max;
|
|
bool m_secure;
|
|
OvmsCommandMap m_children;
|
|
OvmsCommand* m_parent;
|
|
};
|
|
|
|
|
|
typedef enum
|
|
{
|
|
OCS_Init,
|
|
OCS_Error,
|
|
OCS_RunOnce,
|
|
OCS_RunLoop,
|
|
OCS_StopRequested,
|
|
} OvmsCommandState_t;
|
|
|
|
class OvmsCommandTask : public TaskBase
|
|
{
|
|
public:
|
|
OvmsCommandTask(int _verbosity, OvmsWriter* _writer, OvmsCommand* _cmd, int _argc, const char* const* _argv);
|
|
virtual ~OvmsCommandTask();
|
|
virtual OvmsCommandState_t Prepare();
|
|
bool Run();
|
|
static bool Terminator(OvmsWriter* writer, void* userdata, char ch);
|
|
bool IsRunning() { return m_state == OCS_RunLoop; }
|
|
bool IsTerminated() { return m_state == OCS_StopRequested; }
|
|
|
|
protected:
|
|
int verbosity;
|
|
OvmsWriter* writer;
|
|
OvmsCommand* cmd;
|
|
int argc;
|
|
char** argv;
|
|
OvmsCommandState_t m_state;
|
|
};
|
|
|
|
class OvmsCommandApp : public OvmsWriter
|
|
{
|
|
public:
|
|
OvmsCommandApp();
|
|
virtual ~OvmsCommandApp();
|
|
|
|
public:
|
|
OvmsCommand* RegisterCommand(const char* name, const char* title,
|
|
OvmsCommandExecuteCallback_t execute = NULL,
|
|
const char *usage = "", int min = 0, int max = 0, bool secure = true);
|
|
bool UnregisterCommand(const char* name);
|
|
OvmsCommand* FindCommand(const char* name);
|
|
OvmsCommand* FindCommandFullName(const char* name);
|
|
void RegisterConsole(OvmsWriter* writer);
|
|
void DeregisterConsole(OvmsWriter* writer);
|
|
int Log(const char* fmt, ...);
|
|
int Log(const char* fmt, va_list args);
|
|
int LogPartial(const char* fmt, ...);
|
|
int HexDump(const char* tag, const char* prefix, const char* data, size_t length, size_t colsize=16);
|
|
void Display(OvmsWriter* writer);
|
|
|
|
public:
|
|
char ** Complete(OvmsWriter* writer, int argc, const char * const * argv);
|
|
void Execute(int verbosity, OvmsWriter* writer, int argc, const char * const * argv);
|
|
|
|
public:
|
|
void ConfigureLogging();
|
|
void LogTask();
|
|
bool StartLogTask(FILE* file);
|
|
bool StopLogTask();
|
|
bool OpenLogfile();
|
|
bool CloseLogfile();
|
|
bool SetLogfile(std::string path);
|
|
std::string GetLogfile() { return m_logfile_path; }
|
|
void SetLoglevel(std::string tag, std::string level);
|
|
void ExpireLogFiles(int verbosity, OvmsWriter* writer, int keepdays);
|
|
void ShowLogStatus(int verbosity, OvmsWriter* writer);
|
|
static void ExpireTask(void* data);
|
|
void EventHandler(std::string event, void* data);
|
|
|
|
private:
|
|
bool CycleLogfile();
|
|
void ReadConfig();
|
|
|
|
private:
|
|
int LogBuffer(LogBuffers* lb, const char* fmt, va_list args);
|
|
|
|
public:
|
|
void Log(LogBuffers* message);
|
|
|
|
private:
|
|
OvmsCommand m_root;
|
|
typedef std::set<OvmsWriter*> ConsoleSet;
|
|
ConsoleSet m_consoles;
|
|
PartialLogs m_partials;
|
|
FILE* m_logfile;
|
|
std::string m_logfile_path;
|
|
size_t m_logfile_size;
|
|
size_t m_logfile_maxsize;
|
|
TaskHandle_t m_logtask;
|
|
OvmsMutex m_logtask_mutex;
|
|
QueueHandle_t m_logtask_queue;
|
|
uint32_t m_logtask_dropcnt;
|
|
uint32_t m_logfile_cyclecnt;
|
|
uint32_t m_logtask_linecnt;
|
|
uint32_t m_logtask_fsynctime;
|
|
time_t m_logtask_laststamp;
|
|
struct timeval m_logtask_basetime;
|
|
|
|
public:
|
|
TaskHandle_t m_expiretask;
|
|
};
|
|
|
|
extern OvmsCommandApp MyCommandApp;
|
|
|
|
#endif //#ifndef __COMMAND_H__
|