OVMS3/OVMS.V3/main/ovms_command.h

393 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__