Каркасик. Поток слушающий соединения, апдейтилка температуры, регистры, конфиг, логирование.
This commit is contained in:
@@ -34,8 +34,9 @@
|
|||||||
</inputType>
|
</inputType>
|
||||||
</tool>
|
</tool>
|
||||||
<tool id="com.intel.iotdevkit.ide.tool.gnu.cross.cpp.linker.359193723" name="Cross G++ Linker" superClass="com.intel.iotdevkit.ide.tool.gnu.cross.cpp.linker">
|
<tool id="com.intel.iotdevkit.ide.tool.gnu.cross.cpp.linker.359193723" name="Cross G++ Linker" superClass="com.intel.iotdevkit.ide.tool.gnu.cross.cpp.linker">
|
||||||
<option id="com.intel.iotdevkit.ide.gnu.cpp.option.link.libs.1417124663" superClass="com.intel.iotdevkit.ide.gnu.cpp.option.link.libs" valueType="libs">
|
<option id="com.intel.iotdevkit.ide.gnu.cpp.option.link.libs.1417124663" name="Libraries" superClass="com.intel.iotdevkit.ide.gnu.cpp.option.link.libs" valueType="libs">
|
||||||
<listOptionValue builtIn="false" value="upm-i2clcd"/>
|
<listOptionValue builtIn="false" value="upm-i2clcd"/>
|
||||||
|
<listOptionValue builtIn="false" value="pthread"/>
|
||||||
<listOptionValue builtIn="false" value="upm-grove"/>
|
<listOptionValue builtIn="false" value="upm-grove"/>
|
||||||
</option>
|
</option>
|
||||||
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1337922396" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
|
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1337922396" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
|
||||||
@@ -89,7 +90,7 @@
|
|||||||
</inputType>
|
</inputType>
|
||||||
</tool>
|
</tool>
|
||||||
<tool id="com.intel.iotdevkit.ide.tool.gnu.cross.cpp.linker.669809431" name="Cross G++ Linker" superClass="com.intel.iotdevkit.ide.tool.gnu.cross.cpp.linker">
|
<tool id="com.intel.iotdevkit.ide.tool.gnu.cross.cpp.linker.669809431" name="Cross G++ Linker" superClass="com.intel.iotdevkit.ide.tool.gnu.cross.cpp.linker">
|
||||||
<option id="com.intel.iotdevkit.ide.gnu.cpp.option.link.libs.1276270371" superClass="com.intel.iotdevkit.ide.gnu.cpp.option.link.libs" valueType="libs">
|
<option id="com.intel.iotdevkit.ide.gnu.cpp.option.link.libs.1276270371" name="Libraries" superClass="com.intel.iotdevkit.ide.gnu.cpp.option.link.libs" valueType="libs">
|
||||||
<listOptionValue builtIn="false" value="upm-i2clcd"/>
|
<listOptionValue builtIn="false" value="upm-i2clcd"/>
|
||||||
<listOptionValue builtIn="false" value="upm-grove"/>
|
<listOptionValue builtIn="false" value="upm-grove"/>
|
||||||
</option>
|
</option>
|
||||||
@@ -125,4 +126,5 @@
|
|||||||
</scannerConfigBuildInfo>
|
</scannerConfigBuildInfo>
|
||||||
</storageModule>
|
</storageModule>
|
||||||
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
|
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
|
||||||
|
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
|
||||||
</cproject>
|
</cproject>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
||||||
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
||||||
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
||||||
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="-1818248636967704074" id="com.intel.iotdevkit.ide.BuiltinSpecsDetector" keep-relative-paths="false" name="IoT Builtin Specs Detector" parameter="${COMMAND} ${FLAGS} -E -P -v -dD "${INPUTS}" --sysroot="${POKY_HOME}"" prefer-non-shared="true">
|
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="-1444385726000719677" id="com.intel.iotdevkit.ide.BuiltinSpecsDetector" keep-relative-paths="false" name="IoT Builtin Specs Detector" parameter="${COMMAND} ${FLAGS} -E -P -v -dD "${INPUTS}" --sysroot="${POKY_HOME}"" prefer-non-shared="true">
|
||||||
<language-scope id="org.eclipse.cdt.core.g++"/>
|
<language-scope id="org.eclipse.cdt.core.g++"/>
|
||||||
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
||||||
</provider>
|
</provider>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
||||||
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
||||||
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
||||||
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="-1818248636967704074" id="com.intel.iotdevkit.ide.BuiltinSpecsDetector" keep-relative-paths="false" name="IoT Builtin Specs Detector" parameter="${COMMAND} ${FLAGS} -E -P -v -dD "${INPUTS}" --sysroot="${POKY_HOME}"" prefer-non-shared="true">
|
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="-1444385726000719677" id="com.intel.iotdevkit.ide.BuiltinSpecsDetector" keep-relative-paths="false" name="IoT Builtin Specs Detector" parameter="${COMMAND} ${FLAGS} -E -P -v -dD "${INPUTS}" --sysroot="${POKY_HOME}"" prefer-non-shared="true">
|
||||||
<language-scope id="org.eclipse.cdt.core.g++"/>
|
<language-scope id="org.eclipse.cdt.core.g++"/>
|
||||||
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
||||||
</provider>
|
</provider>
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding/<project>=UTF-8
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
Простенькое управляемое реле.
|
||||||
|
|
||||||
|
При запуске поднимает modbus tcp сервер, с которым можно общаться и управлять (а также читать текущие показания).
|
||||||
|
|
||||||
|
Список регистров:
|
||||||
|
|
||||||
|
0 - Температура включения реле (если ниже, включится) (в 0.01 градуса)
|
||||||
|
1 - Температура выключения реле (если выше, выключится) (в 0.01 градуса)
|
||||||
|
2 - Режим работы (0 - авто, 1 - принудительно включено, 2 - принудительно выключено)
|
||||||
|
3 - Текущая температура (в 0.01 градуса)
|
||||||
|
4 - Текущее состояние реле (0 - выключено, 1 - включено)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ListenPort = 15000
|
||||||
|
LogPath = "/var/log/defrost.log"
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
#include "BaseListeningThread.h"
|
||||||
|
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "libdiff.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
BaseListeningThread::BaseListeningThread(LogFile *log, int port) : log(log), port(port)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseListeningThread::Run()
|
||||||
|
{
|
||||||
|
string name = typeid(*this).name();
|
||||||
|
bool first_time = true;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (first_time) first_time = false;
|
||||||
|
else sleep(5);
|
||||||
|
try {
|
||||||
|
// сокет, на котором мы слушаем соединения
|
||||||
|
int listensock;
|
||||||
|
// создаем сокет для приема соединений
|
||||||
|
if ((listensock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
|
||||||
|
{
|
||||||
|
log->LogString("Could not create socket for listening...");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// задаем опцию REUSEADDR на сокете. Это нужно для его пересозданияв случае, когда
|
||||||
|
// прием соединений временно прекращается
|
||||||
|
if (setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, (&SOCKET_OPTION_TRUE), sizeof(SOCKET_OPTION_TRUE)) == -1)
|
||||||
|
{
|
||||||
|
log->LogString("Could not set socket option...");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in listenaddr;
|
||||||
|
|
||||||
|
// Заполнить структуру данных с адресом сокета
|
||||||
|
memset(&listenaddr, 0, sizeof(listenaddr));
|
||||||
|
listenaddr.sin_family = AF_INET;
|
||||||
|
listenaddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
listenaddr.sin_port = htons(port);
|
||||||
|
|
||||||
|
// привязываем сокет к адресу
|
||||||
|
if (bind(listensock, (struct sockaddr *)(&listenaddr), sizeof(struct sockaddr_in)) == -1)
|
||||||
|
{
|
||||||
|
log->LogString("Could not bind listening socket to port " + ToString(port));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(listen(listensock, LISTEN_BACKLOG) == -1)
|
||||||
|
{
|
||||||
|
log->LogString("Could not set socket into listening mode...");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in peeraddr;
|
||||||
|
socklen_t paddrlen;
|
||||||
|
paddrlen = sizeof(peeraddr);
|
||||||
|
int recsock;
|
||||||
|
log->LogString(name + " accepting connections on port " + ToString(port));
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
(recsock = accept(listensock, (struct sockaddr *)(&peeraddr), &paddrlen));
|
||||||
|
if (recsock == -1)
|
||||||
|
{
|
||||||
|
log->LogString("Error accepting connection, errno = " + ToString(errno));
|
||||||
|
// поспим немного, чтобы не ломать ничего лишнего и не заспамливать логи
|
||||||
|
sleep(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// напишем чего-нибудь про пришедшее соединение
|
||||||
|
log->LogString(name + " accepted connection from " + ToString(inet_ntoa(peeraddr.sin_addr)) + ":" + ToString(ntohs(peeraddr.sin_port)));
|
||||||
|
OnNewClient(recsock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (string s)
|
||||||
|
{
|
||||||
|
log->LogString("Exception in " + name + ": " + s);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
log->LogString("Unknown exception in " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef DATA_PROCESSOR_BASE_LISTENING_THREAD_H
|
||||||
|
#define DATA_PROCESSOR_BASE_LISTENING_THREAD_H
|
||||||
|
|
||||||
|
#include "Thread.h"
|
||||||
|
#include "liblog.h"
|
||||||
|
|
||||||
|
class BaseListeningThread : public ftc::Thread
|
||||||
|
{
|
||||||
|
const int SOCKET_OPTION_TRUE = 1;
|
||||||
|
const int LISTEN_BACKLOG = 100;
|
||||||
|
|
||||||
|
int port;
|
||||||
|
protected:
|
||||||
|
LogFile *log;
|
||||||
|
virtual void OnNewClient(int socket) = 0;
|
||||||
|
virtual void Run() override;
|
||||||
|
public:
|
||||||
|
BaseListeningThread(LogFile *log, int port);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#include "ModbusServiceListenThread.h"
|
||||||
|
#include "ModbusServiceWorker.h"
|
||||||
|
|
||||||
|
ModbusServiceListenThread::ModbusServiceListenThread(LogFile *mainlog, int port)
|
||||||
|
: BaseListeningThread(mainlog, port)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModbusServiceListenThread::OnNewClient(int socket)
|
||||||
|
{
|
||||||
|
ModbusServiceWorker* worker = new ModbusServiceWorker(socket, log);
|
||||||
|
// worker удалится внутри класса Thread, так что можем на него с чистой совестью забить
|
||||||
|
Thread::Perform(worker);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef MODBUSSERVICELISTENTHREAD_H
|
||||||
|
#define MODBUSSERVICELISTENTHREAD_H
|
||||||
|
|
||||||
|
#include "liblog.h"
|
||||||
|
#include "BaseListeningThread.h"
|
||||||
|
|
||||||
|
using namespace ftc;
|
||||||
|
|
||||||
|
class ModbusServiceListenThread : public BaseListeningThread
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual void OnNewClient(int socket) override;
|
||||||
|
public:
|
||||||
|
ModbusServiceListenThread(LogFile *log, int port);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FORMULASERVICELISTENTHREAD_H
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#include "ModbusServiceWorker.h"
|
||||||
|
#include "libdiff.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
ModbusServiceWorker::ModbusServiceWorker(int socket, LogFile *log)
|
||||||
|
: socket(socket), log(log)
|
||||||
|
{
|
||||||
|
s.Init(socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModbusServiceWorker::Run()
|
||||||
|
{
|
||||||
|
log->LogString("Started ModbusServiceWorker");
|
||||||
|
try {
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (string s)
|
||||||
|
{
|
||||||
|
log->LogString("Exception in ModbusServiceWorker: " + s);
|
||||||
|
}
|
||||||
|
catch (std::exception e)
|
||||||
|
{
|
||||||
|
log->LogString("Exception in ModbusServiceWorker: " + ToString(e.what()));
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
log->LogString("Unknown exception in ModbusServiceWorker");
|
||||||
|
// непонятно, что случилось, закрываем сокет и все дела
|
||||||
|
}
|
||||||
|
s.Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusServiceWorker::~ModbusServiceWorker()
|
||||||
|
{
|
||||||
|
s.Disconnect();
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef MODBUS_SERVICE_WORKER_H
|
||||||
|
#define MODBUS_SERVICE_WORKER_H
|
||||||
|
|
||||||
|
#include "Thread.h"
|
||||||
|
#include "SynchronousSocket.h"
|
||||||
|
#include "liblog.h"
|
||||||
|
|
||||||
|
using namespace ftc;
|
||||||
|
|
||||||
|
class ModbusServiceWorker : public Thread
|
||||||
|
{
|
||||||
|
int socket;
|
||||||
|
SynchronousSocket s;
|
||||||
|
LogFile *log;
|
||||||
|
protected:
|
||||||
|
virtual void Run();
|
||||||
|
public:
|
||||||
|
ModbusServiceWorker(int socket, LogFile *log);
|
||||||
|
virtual ~ModbusServiceWorker();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
#include "SynchronousSocket.h"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
SynchronousSocket::SynchronousSocket()
|
||||||
|
{
|
||||||
|
socket = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronousSocket::Init(int s)
|
||||||
|
{
|
||||||
|
socket = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SynchronousSocket::IsConnected()
|
||||||
|
{
|
||||||
|
return (socket != -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronousSocket::Connect(string host, int port)
|
||||||
|
{
|
||||||
|
// уже подключены?
|
||||||
|
if (socket != -1) return;
|
||||||
|
|
||||||
|
struct sockaddr_in remoteaddr;
|
||||||
|
|
||||||
|
struct addrinfo *addresses;
|
||||||
|
struct addrinfo hints;
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
if (getaddrinfo(host.c_str(), NULL, NULL, &addresses) != 0)
|
||||||
|
{
|
||||||
|
throw (string)("Unable to getaddrinfo");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// копипастим себе самый первый адрес (в принципе, нам любой норм)
|
||||||
|
memcpy(&remoteaddr, addresses[0].ai_addr, sizeof(remoteaddr));
|
||||||
|
remoteaddr.sin_port = htons(port);
|
||||||
|
freeaddrinfo(addresses);
|
||||||
|
// создаем сокет
|
||||||
|
if ((socket = ::socket(PF_INET, SOCK_STREAM, 0)) == -1)
|
||||||
|
{
|
||||||
|
throw (string)("Unable to create socket");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (connect(socket, (struct sockaddr *)(&remoteaddr), sizeof(struct sockaddr_in)) == -1)
|
||||||
|
{
|
||||||
|
if (errno != EINPROGRESS)
|
||||||
|
{
|
||||||
|
socket = -1;
|
||||||
|
throw (string)("Unable to connect");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronousSocket::Send(const string & data)
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
time_t start = time(NULL);
|
||||||
|
while (pos < (int)data.length())
|
||||||
|
{
|
||||||
|
int res = send(socket, &((data.data())[pos]), data.length() - pos, 0);
|
||||||
|
if (res == -1)
|
||||||
|
{
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||||
|
{
|
||||||
|
if (time(NULL) - start > TIMEOUT)
|
||||||
|
{
|
||||||
|
throw (string)("Timeout");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// сокет поломался - закрываем его
|
||||||
|
throw (string)("Socket closed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pos += res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string SynchronousSocket::Receive(int amount)
|
||||||
|
{
|
||||||
|
string r = "";
|
||||||
|
time_t start = time(NULL);
|
||||||
|
while ((int)r.length() < amount)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
int to_recv = min((int)sizeof(buf), amount - (int)r.length());
|
||||||
|
int res = recv(socket, buf, to_recv, 0);
|
||||||
|
if (res == 0)
|
||||||
|
{
|
||||||
|
// бросаем специфический exception, означающий, что сокет закрыт
|
||||||
|
throw (string)("Socket closed");
|
||||||
|
}
|
||||||
|
else if (res == -1)
|
||||||
|
{
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||||
|
{
|
||||||
|
if (time(NULL) - start > TIMEOUT)
|
||||||
|
{
|
||||||
|
throw (string)("Timeout");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// что-то сломалось, надо закрыть сокет на всякий случай
|
||||||
|
// бросаем специфический exception, означающий, что сокет закрыт
|
||||||
|
throw (string)("Socket closed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < res; i++) r += buf[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronousSocket::Disconnect()
|
||||||
|
{
|
||||||
|
if (socket == -1) return;
|
||||||
|
close(socket);
|
||||||
|
socket = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string SynchronousSocket::ReceiveMessage()
|
||||||
|
{
|
||||||
|
string len_s = Receive(4);
|
||||||
|
int len = 0;
|
||||||
|
for (size_t i = 0; i < len_s.length(); i++)
|
||||||
|
{
|
||||||
|
len |= ((unsigned char)len_s[i]) << (8 * i);
|
||||||
|
}
|
||||||
|
return Receive(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronousSocket::SendMessage(const string & m)
|
||||||
|
{
|
||||||
|
int len = m.length();
|
||||||
|
string len_s = "";
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
len_s += (char)((len >> (8 * i)) & 0xFF);
|
||||||
|
}
|
||||||
|
Send(len_s);
|
||||||
|
Send(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#ifndef SYNCHRONOUS_SOCKET_HEADER_FILE_INCLUDED
|
||||||
|
#define SYNCHRONOUS_SOCKET_HEADER_FILE_INCLUDED
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
class SynchronousSocket
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
const int TIMEOUT = 10; // in seconds
|
||||||
|
protected:
|
||||||
|
// собственно сокет
|
||||||
|
int socket;
|
||||||
|
public:
|
||||||
|
SynchronousSocket();
|
||||||
|
// Используется для проброса уже готового сокета и работы с ним
|
||||||
|
void Init(int s);
|
||||||
|
bool IsConnected();
|
||||||
|
virtual void Connect(string host, int port);
|
||||||
|
virtual void Send(const string &data);
|
||||||
|
virtual string Receive(int amount);
|
||||||
|
void Disconnect();
|
||||||
|
string ReceiveMessage();
|
||||||
|
void SendMessage(const string &m);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
void * ftc::Thread::DoRun(void * arg)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
((Thread*)arg)->is_running = true;
|
||||||
|
((Thread*)arg)->Run();
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
|
||||||
|
}
|
||||||
|
((Thread*)arg)->is_running = false;
|
||||||
|
if (((Thread*)arg)->delete_after_finish)
|
||||||
|
{
|
||||||
|
delete ((Thread*)arg);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ftc::Thread::Run()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ftc::Thread::Start()
|
||||||
|
{
|
||||||
|
pthread_attr_t attr;
|
||||||
|
if (pthread_attr_init(&attr) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
is_running = true;
|
||||||
|
if (pthread_create(&thread, &attr, &Thread::DoRun, this) != 0)
|
||||||
|
{
|
||||||
|
is_running = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ftc::Thread::Perform(Thread *work)
|
||||||
|
{
|
||||||
|
work->delete_after_finish = true;
|
||||||
|
work->Start();
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef CORE_THREAD_HEADER_FILE_INCLUDED
|
||||||
|
#define CORE_THREAD_HEADER_FILE_INCLUDED
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
namespace ftc
|
||||||
|
{
|
||||||
|
class Thread
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
pthread_t thread;
|
||||||
|
bool is_running = false;
|
||||||
|
bool delete_after_finish = false;
|
||||||
|
static void* DoRun(void *arg);
|
||||||
|
protected:
|
||||||
|
// Основная функция потока, переопределите в дочерних классах
|
||||||
|
virtual void Run();
|
||||||
|
public:
|
||||||
|
bool IsRunning() { return is_running; }
|
||||||
|
virtual ~Thread() {}
|
||||||
|
bool Start();
|
||||||
|
// Запускает поток, удаляя его после завершения
|
||||||
|
static void Perform(Thread *work);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,463 @@
|
|||||||
|
#include "configparser.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
const int OPERATIONSET = 0;
|
||||||
|
const int OPERATIONADD = 1;
|
||||||
|
const int OPERATION_MERGE = 2;
|
||||||
|
|
||||||
|
// конструктор ConfigParam
|
||||||
|
ConfigParam::ConfigParam()
|
||||||
|
{
|
||||||
|
name = "";
|
||||||
|
this->val.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// создаем ConfigParam из строки
|
||||||
|
ConfigParam::ConfigParam(string s)
|
||||||
|
{
|
||||||
|
name = s;
|
||||||
|
this->val.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// создаем ConfigParam по имени и значению
|
||||||
|
ConfigParam::ConfigParam(string name, string value)
|
||||||
|
{
|
||||||
|
this->name = name;
|
||||||
|
this->val.clear();
|
||||||
|
ConfigValue v;
|
||||||
|
v.type = CONFIGVALUETYPESTRING;
|
||||||
|
v.sval = value;
|
||||||
|
this->val.push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigParser::IsDelim(char c)
|
||||||
|
{
|
||||||
|
if (IsWS(c)) return true;
|
||||||
|
if ((c == '{') || (c == '}')) return true;
|
||||||
|
if ((c == '[') || (c == ']')) return true;
|
||||||
|
if (c == ',') return true;
|
||||||
|
if (c == ';') return true;
|
||||||
|
if (c == '#') return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const ConfigParam &a, const ConfigParam &b)
|
||||||
|
{
|
||||||
|
return a.name < b.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ConfigParser::i2s(int a)
|
||||||
|
{
|
||||||
|
ostringstream oss;
|
||||||
|
oss << a;
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Является ли символ whitespace-ом
|
||||||
|
bool ConfigParser::IsWS(char c)
|
||||||
|
{
|
||||||
|
return ((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Можно ли такие символы использовать в названии параметров
|
||||||
|
bool ConfigParser::GoodForName(char c)
|
||||||
|
{
|
||||||
|
if (IsWS(c)) return false;
|
||||||
|
if ((c == '[') || (c == ']')) return false;
|
||||||
|
if ((c == '{') || (c == '}')) return false;
|
||||||
|
if ((c == '(') || (c == ')')) return false;
|
||||||
|
if (c == '"') return false;
|
||||||
|
if (c == '\\') return false;
|
||||||
|
if (c == '+') return false;
|
||||||
|
if (c == '=') return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// возвращает символ, соответствующий escape-последовательности "\c"
|
||||||
|
char ConfigParser::UnSlash(char c)
|
||||||
|
{
|
||||||
|
if (c == 'n') return '\n';
|
||||||
|
if (c == '\\') return '\\';
|
||||||
|
if (c == '\"') return '"';
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// разбирает s с l по r, считая, что она должна быть ConfigParam - ом
|
||||||
|
// возвращает, какая операция была применена
|
||||||
|
int ConfigParser::ParseConfigParam(int &l, int &r, ConfigParam &p)
|
||||||
|
{
|
||||||
|
while ((l <= r) && (IsWS(s[l])))
|
||||||
|
{
|
||||||
|
if (s[l] == '\n') cline++;
|
||||||
|
l++;
|
||||||
|
}
|
||||||
|
if (l > r)
|
||||||
|
throw "Expected parameter at line " + i2s(cline);
|
||||||
|
if (!GoodForName(s[l]))
|
||||||
|
{
|
||||||
|
throw "Parameter name expected at line " + i2s(cline);
|
||||||
|
}
|
||||||
|
p.name = "";
|
||||||
|
while (GoodForName(s[l]))
|
||||||
|
{
|
||||||
|
if (l == r) throw "Unfinished param at line " + i2s(cline);
|
||||||
|
p.name += s[l];
|
||||||
|
l++;
|
||||||
|
}
|
||||||
|
while ((l <= r) && (IsWS(s[l])))
|
||||||
|
{
|
||||||
|
if (s[l] == '\n') cline++;
|
||||||
|
l++;
|
||||||
|
}
|
||||||
|
if (l > r) throw "Unfinished param at line " + i2s(cline);
|
||||||
|
int op;
|
||||||
|
if (s[l] == '+')
|
||||||
|
{
|
||||||
|
if ((l == r) || (s[l + 1] != '=')) throw "Unrecognized operation: + or += expected at line " + i2s(cline);
|
||||||
|
op = OPERATIONADD;
|
||||||
|
l += 2;
|
||||||
|
}
|
||||||
|
else if (s[l] == '=')
|
||||||
|
{
|
||||||
|
op = OPERATIONSET;
|
||||||
|
l++;
|
||||||
|
}
|
||||||
|
else if (s[l] == '(')
|
||||||
|
{
|
||||||
|
// this is a function call
|
||||||
|
l++;
|
||||||
|
string args = "";
|
||||||
|
while (l <= r && s[l] != ')')
|
||||||
|
{
|
||||||
|
args += s[l];
|
||||||
|
if (s[l] == '\n') cline++;
|
||||||
|
l++;
|
||||||
|
}
|
||||||
|
if (l > r) throw (string)("Unfinished function args at line " + i2s(cline));
|
||||||
|
l++;
|
||||||
|
if (p.name == "include")
|
||||||
|
{
|
||||||
|
// пока так костыльно, годный парсинг аргументов надо вместе с глобальным
|
||||||
|
// рефакторингом парсера делать
|
||||||
|
TrimSpaces(args);
|
||||||
|
TrimQuotes(args);
|
||||||
|
ConfigParser t;
|
||||||
|
t.Load(args);
|
||||||
|
p.val.push_back(t.cfg);
|
||||||
|
// в этом случае значение имеет тип "множество"
|
||||||
|
p.val[0].type = CONFIGVALUETYPESET;
|
||||||
|
return OPERATION_MERGE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw "Unknown function " + p.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else throw "Unrecognized operation: + or += expected at line " + i2s(cline);
|
||||||
|
while ((l <= r) && (IsWS(s[l])))
|
||||||
|
{
|
||||||
|
if (s[l] == '\n') cline++;
|
||||||
|
l++;
|
||||||
|
}
|
||||||
|
if (l > r) throw "Expected parameter value " + i2s(cline);
|
||||||
|
if (s[l] == '{')
|
||||||
|
{
|
||||||
|
ConfigValue a;
|
||||||
|
p.val.push_back(a);
|
||||||
|
l++;
|
||||||
|
// в этом случае значение имеет тип "множество"
|
||||||
|
p.val[0].type = CONFIGVALUETYPESET;
|
||||||
|
ParseConfigValue(l, r, p.val[0]);
|
||||||
|
}
|
||||||
|
else if (s[l] == '[')
|
||||||
|
{
|
||||||
|
l++;
|
||||||
|
ParseConfigParamValues(l, r, p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ConfigValue a;
|
||||||
|
p.val.push_back(a);
|
||||||
|
// в этом случае значение имеет тип "строка"
|
||||||
|
p.val[0].type = CONFIGVALUETYPESTRING;
|
||||||
|
ParseConfigValue(l, r, p.val[0]);
|
||||||
|
}
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Разбирает несколько ConfigValues, заданных через запятую
|
||||||
|
void ConfigParser::ParseConfigParamValues(int &l, int &r, ConfigParam &b)
|
||||||
|
{
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
while ((l <= r) && (IsWS(s[l])))
|
||||||
|
{
|
||||||
|
if (s[l] == '\n') cline++;
|
||||||
|
l++;
|
||||||
|
}
|
||||||
|
if (l > r) throw "Unfinished array at line " + i2s(cline);
|
||||||
|
if (s[l] == ']')
|
||||||
|
{
|
||||||
|
l++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ConfigValue a;
|
||||||
|
b.val.push_back(a);
|
||||||
|
if (s[l] == '{')
|
||||||
|
{
|
||||||
|
l++;
|
||||||
|
b.val[b.val.size() - 1].type = CONFIGVALUETYPESET;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
b.val[b.val.size() - 1].type = CONFIGVALUETYPESTRING;
|
||||||
|
}
|
||||||
|
ParseConfigValue(l, r, b.val[b.val.size() - 1]);
|
||||||
|
if (l > r) throw "Unfinished array at line " + i2s(cline);
|
||||||
|
if (s[l] == ']')
|
||||||
|
{
|
||||||
|
l++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
l++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// разбирает строку s с l по r, считая, что она - корректный ConfigValue
|
||||||
|
void ConfigParser::ParseConfigValue(int &l, int &r, ConfigValue &vl)
|
||||||
|
{
|
||||||
|
while ((l <= r) && (IsWS(s[l])))
|
||||||
|
{
|
||||||
|
if (s[l] == '\n') cline++;
|
||||||
|
l++;
|
||||||
|
}
|
||||||
|
if (l > r) return;
|
||||||
|
// отсечем комментарии
|
||||||
|
if ((s[l] == ';') || (s[l] == '#'))
|
||||||
|
{
|
||||||
|
while (s[l] != '\n') l++;
|
||||||
|
ParseConfigValue(l, r, vl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// если в точке l наличествует символ-разделитель, значит
|
||||||
|
// наше Value закончилось
|
||||||
|
if ((s[l] == ',') || (s[l] == ']'))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vl.type == CONFIGVALUETYPESET)
|
||||||
|
{
|
||||||
|
// нашли скобочку - set закрылся
|
||||||
|
if (s[l] == '}')
|
||||||
|
{
|
||||||
|
l++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!GoodForName(s[l]))
|
||||||
|
{
|
||||||
|
throw "Parameter name expected at line " + i2s(cline);
|
||||||
|
}
|
||||||
|
ConfigParam p;
|
||||||
|
int oper = ParseConfigParam(l, r, p);
|
||||||
|
if (oper == OPERATION_MERGE)
|
||||||
|
{
|
||||||
|
for (set<ConfigParam>::iterator t = p.val[0].pval.begin(); t != p.val[0].pval.end(); t++)
|
||||||
|
{
|
||||||
|
vl.pval.insert(*t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
set<ConfigParam>::iterator it = vl.pval.find(p);
|
||||||
|
if (it == vl.pval.end())
|
||||||
|
{
|
||||||
|
vl.pval.insert(p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
if (oper == OPERATIONADD)
|
||||||
|
{
|
||||||
|
reverse(p.val.begin(), p.val.end());
|
||||||
|
for (i = ((int)it->val.size() - 1); i >= 0; i--)
|
||||||
|
{
|
||||||
|
p.val.push_back(it->val[i]);
|
||||||
|
}
|
||||||
|
reverse(p.val.begin(), p.val.end());
|
||||||
|
vl.pval.erase(it);
|
||||||
|
vl.pval.insert(p);
|
||||||
|
}
|
||||||
|
else if (oper == OPERATIONSET)
|
||||||
|
{
|
||||||
|
vl.pval.erase(it);
|
||||||
|
vl.pval.insert(p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw (string)("Internal error (wrong operation returned) at line " + i2s(cline));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseConfigValue(l, r, vl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vl.sval = "";
|
||||||
|
int isslashed = 0;
|
||||||
|
int isquoted = 0;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (l > r) throw "Unfinished value at line " + i2s(cline);
|
||||||
|
if (isslashed)
|
||||||
|
{
|
||||||
|
vl.sval += UnSlash(s[l]);
|
||||||
|
isslashed = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (s[l] == '\\') isslashed = 1;
|
||||||
|
else if (s[l] == '"') isquoted = 1 - isquoted;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (IsDelim(s[l]))
|
||||||
|
{
|
||||||
|
if (isquoted == 0) break;
|
||||||
|
if (s[l] == '\n') throw "Unexpected end of line at line " + i2s(cline);
|
||||||
|
}
|
||||||
|
vl.sval += s[l];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// убирает двойные кавычки в начале/конце строки
|
||||||
|
void ConfigParser::TrimQuotes(string &s)
|
||||||
|
{
|
||||||
|
if (((int)s.length()) < 2) return;
|
||||||
|
if ((s[0] == '\"') && (s[((int)s.length()) - 1] == '\"'))
|
||||||
|
{
|
||||||
|
s = s.substr(1, s.length() - 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigParser::Load(string fname)
|
||||||
|
{
|
||||||
|
// Загружаем весь конфиг в одну строку, теперь будем ее разбирать
|
||||||
|
FILE *fin = fopen(fname.c_str(), "rt");
|
||||||
|
if (!fin) throw "Config file " + fname + " not found!";
|
||||||
|
int c = getc(fin);
|
||||||
|
slen = 0;
|
||||||
|
while (c != EOF)
|
||||||
|
{
|
||||||
|
slen++;
|
||||||
|
c = getc(fin);
|
||||||
|
}
|
||||||
|
s = new char[slen + 1];
|
||||||
|
int i;
|
||||||
|
fclose(fin);
|
||||||
|
fin = fopen(fname.c_str(), "rt");
|
||||||
|
for (i = 0; i < slen; i++) s[i] = getc(fin);
|
||||||
|
fclose(fin);
|
||||||
|
s[slen] = '\n';
|
||||||
|
slen++;
|
||||||
|
// Теперь считаем всю строку конфиг-значением (так оно и должно быть)
|
||||||
|
cline = 1;
|
||||||
|
cfg.type = CONFIGVALUETYPESET;
|
||||||
|
cfg.sval = "";
|
||||||
|
cfg.pval.clear();
|
||||||
|
try{
|
||||||
|
int l = 0;
|
||||||
|
int r = slen - 1;
|
||||||
|
ParseConfigValue(l, r, cfg);
|
||||||
|
delete []s;
|
||||||
|
}
|
||||||
|
catch(string ss)
|
||||||
|
{
|
||||||
|
delete []s;
|
||||||
|
throw "Error parsing " + fname + ": " + ss;
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
delete []s;
|
||||||
|
throw "Unrecognized error parsing " + fname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// возвращает значение параметра (в старом стиле)
|
||||||
|
string ConfigParser::GetParam(string name)
|
||||||
|
{
|
||||||
|
set<ConfigParam>::iterator p;
|
||||||
|
p = this->cfg.pval.find(ConfigParam(name));
|
||||||
|
if (p != this->cfg.pval.end())
|
||||||
|
{
|
||||||
|
if (p->val.size() == 0) throw "There are no values for parameter " + name;
|
||||||
|
return p->val[0].sval;
|
||||||
|
}
|
||||||
|
throw ((string)"No param \"" + name + "\" found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// возвращает значение параметра (в старом стиле)
|
||||||
|
string ConfigParser::GetParamSilent(string name)
|
||||||
|
{
|
||||||
|
set<ConfigParam>::iterator p;
|
||||||
|
p = this->cfg.pval.find(ConfigParam(name));
|
||||||
|
if (p != this->cfg.pval.end())
|
||||||
|
{
|
||||||
|
if (p->val.size() == 0) return "";
|
||||||
|
return p->val[0].sval;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// возвращает какое-то одно значение параметра
|
||||||
|
// генерирует исключение при ошибке
|
||||||
|
string ConfigParam::GetSingleVal() const
|
||||||
|
{
|
||||||
|
if (val.size() == 0) throw ((string)"No value available");
|
||||||
|
if (val[0].type == CONFIGVALUETYPESTRING) return val[0].sval;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw "Wrong parameter value type (string expected)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// убивает в строке пробелы в начале и в конце
|
||||||
|
void ConfigParser::TrimSpaces(string &s)
|
||||||
|
{
|
||||||
|
string tmp = s;
|
||||||
|
s = "";
|
||||||
|
int wf = 0, i;
|
||||||
|
for (i = 0; i < ((int)tmp.length()); i++)
|
||||||
|
{
|
||||||
|
if (! IsWS(tmp[i]) )
|
||||||
|
{
|
||||||
|
wf = 1;
|
||||||
|
}
|
||||||
|
if (wf == 1) s += tmp[i];
|
||||||
|
}
|
||||||
|
while ((s.length() > 0) && (IsWS(s[s.length() - 1])))
|
||||||
|
{
|
||||||
|
s.erase(--s.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ищет в этом value заданный параметр. Возвращает пустую строку, если такого нет.
|
||||||
|
string ConfigValue::GetParamVal(string pname) const
|
||||||
|
{
|
||||||
|
if (this->type == CONFIGVALUETYPESTRING) throw ((string)"Attempted to get param from value, which is not a set");
|
||||||
|
set<ConfigParam>::iterator p = this->pval.find((ConfigParam)(pname));
|
||||||
|
if (p == pval.end()) return "";
|
||||||
|
return p->GetSingleVal();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
#ifndef FELIX_THE_CAT_FSYSTEM_CONFIGPARSER_HEADER_FILE_INCLUDED_KHKLJWGNBBVKJGLKXBVMZVEKLEKFJKJSAGSKAJGHJKBHQRPOTU
|
||||||
|
#define FELIX_THE_CAT_FSYSTEM_CONFIGPARSER_HEADER_FILE_INCLUDED_KHKLJWGNBBVKJGLKXBVMZVEKLEKFJKJSAGSKAJGHJKBHQRPOTU
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
const char CONFIGVALUETYPESTRING = 0;
|
||||||
|
const char CONFIGVALUETYPESET = 1;
|
||||||
|
|
||||||
|
class ConfigValue;
|
||||||
|
|
||||||
|
// Класс описывающий параметр из конфигурационного файла
|
||||||
|
class ConfigParam
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
string name;
|
||||||
|
vector<ConfigValue> val;
|
||||||
|
// возвращает первое значение параметра (если их несколько)
|
||||||
|
string GetSingleVal() const;
|
||||||
|
// создаем из строки
|
||||||
|
ConfigParam(string name);
|
||||||
|
// создаем просто так
|
||||||
|
ConfigParam();
|
||||||
|
// создаем параметр с единственным значением
|
||||||
|
ConfigParam(string name, string value);
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator<(const ConfigParam &a, const ConfigParam &b);
|
||||||
|
|
||||||
|
|
||||||
|
// Класс, описывающий значение параметра в конфигурационном файле
|
||||||
|
class ConfigValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
char type;
|
||||||
|
string sval;
|
||||||
|
set<ConfigParam> pval;
|
||||||
|
string GetParamVal(string pname) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Класс, разбирающий конфигурационный файл
|
||||||
|
class ConfigParser
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
char *s;
|
||||||
|
int slen;
|
||||||
|
int mode;
|
||||||
|
int cline;
|
||||||
|
// переделывает число в строку
|
||||||
|
string i2s(int a);
|
||||||
|
// разбирает s с l по r, считая, что она должна быть ConfigValue - ом
|
||||||
|
void ParseConfigValue(int &l, int &r, ConfigValue &vl);
|
||||||
|
// возвращает, является ли символ whitespace-ом
|
||||||
|
bool IsWS(char c);
|
||||||
|
// возвращает, может ли такой символ быть в имени параметра
|
||||||
|
bool GoodForName(char c);
|
||||||
|
// убирает двойные кавычки в начале/конце строки (если, конечно, таковые имеются)
|
||||||
|
void TrimQuotes(string &s);
|
||||||
|
// возвращает символ, соответствующий escape-последовательности "\c"
|
||||||
|
char UnSlash(char c);
|
||||||
|
// разбирает s с l по r, считая, что она должна быть ConfigParam - ом
|
||||||
|
// возвращает, какая операция была применена
|
||||||
|
int ParseConfigParam(int &l, int &r, ConfigParam &p);
|
||||||
|
// возращает, считается ли такой символ разделителем
|
||||||
|
// разделителями считаются ws-ы, скобки и запятые
|
||||||
|
bool IsDelim(char c);
|
||||||
|
// Разбирает несколько ConfigValues, заданных через запятую
|
||||||
|
void ParseConfigParamValues(int &l, int &r, ConfigParam &b);
|
||||||
|
void TrimSpaces(string &s);
|
||||||
|
public:
|
||||||
|
ConfigValue cfg;
|
||||||
|
// загружает конфигурационный файл
|
||||||
|
void Load(string fname);
|
||||||
|
// возвращает значение параметра (в старом стиле)
|
||||||
|
// генерирует исключение, если такого нет
|
||||||
|
string GetParam(string name);
|
||||||
|
// возвращает значение параметра (в старом стиле)
|
||||||
|
// возвращает пустую строку, если такого нет
|
||||||
|
string GetParamSilent(string name);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
+93
-61
@@ -1,28 +1,23 @@
|
|||||||
|
#include "configparser.h"
|
||||||
#include "grove.h"
|
#include "grove.h"
|
||||||
#include "jhd1313m1.h"
|
#include "jhd1313m1.h"
|
||||||
#include "mraa/common.hpp"
|
#include "mraa/common.hpp"
|
||||||
#include "mraa/gpio.hpp"
|
#include "mraa/gpio.hpp"
|
||||||
|
#include "globals.h"
|
||||||
|
#include "ModbusServiceListenThread.h"
|
||||||
|
#include "libdiff.h"
|
||||||
|
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#define GPIO_PIN_1 2
|
#define GPIO_PIN_1 2
|
||||||
|
|
||||||
void temperature_update(upm::GroveTemp* temperature_sensor, upm::Jhd1313m1 *lcd)
|
void temperature_update(upm::GroveTemp* temperature_sensor, upm::Jhd1313m1 *lcd)
|
||||||
{
|
{
|
||||||
// minimum and maximum temperatures registered, the initial values will be
|
|
||||||
// replaced after the first read
|
|
||||||
static int min_temperature = INT_MAX;
|
|
||||||
static int max_temperature = INT_MIN;
|
|
||||||
|
|
||||||
// the temperature range in degrees Celsius,
|
|
||||||
// adapt to your room temperature for a nicer effect!
|
|
||||||
const int TEMPERATURE_RANGE_MIN_VAL = 18;
|
|
||||||
const int TEMPERATURE_RANGE_MAX_VAL = 31;
|
|
||||||
|
|
||||||
// other helper variables
|
// other helper variables
|
||||||
int temperature; // temperature sensor value in degrees Celsius
|
int temperature; // temperature sensor value in degrees Celsius
|
||||||
float fade; // fade value [0.0 .. 1.0]
|
float fade; // fade value [0.0 .. 1.0]
|
||||||
@@ -30,31 +25,29 @@ void temperature_update(upm::GroveTemp* temperature_sensor, upm::Jhd1313m1 *lcd)
|
|||||||
std::stringstream row_1, row_2; // LCD rows
|
std::stringstream row_1, row_2; // LCD rows
|
||||||
|
|
||||||
// update the min and max temperature values
|
// update the min and max temperature values
|
||||||
temperature = temperature_sensor->value();
|
temperature = temperature_sensor->value() * 100;
|
||||||
|
registers[REGISTER_CUR_TEMP] = temperature;
|
||||||
if (temperature < min_temperature) {
|
|
||||||
min_temperature = temperature;
|
|
||||||
}
|
|
||||||
if (temperature > max_temperature) {
|
|
||||||
max_temperature = temperature;
|
|
||||||
}
|
|
||||||
|
|
||||||
// display the temperature values on the LCD
|
// display the temperature values on the LCD
|
||||||
row_1 << "Temp " << temperature << " ";
|
row_1 << "T " << temperature / 100 << " Mode: ";
|
||||||
row_2 << "Min " << min_temperature << " Max " << max_temperature << " ";
|
int mode = registers[REGISTER_MODE];
|
||||||
|
if (mode == 0) row_1 << "auto";
|
||||||
|
else if (mode == 1) row_1 << "on";
|
||||||
|
else if (mode == 2) row_1 << "off";
|
||||||
|
row_2 << "On: " << registers[REGISTER_ON_TEMP] / 100 << " Off " << registers[REGISTER_OFF_TEMP] / 100 << " ";
|
||||||
lcd->setCursor(0,0);
|
lcd->setCursor(0,0);
|
||||||
lcd->write(row_1.str());
|
lcd->write(row_1.str());
|
||||||
lcd->setCursor(1,0);
|
lcd->setCursor(1,0);
|
||||||
lcd->write(row_2.str());
|
lcd->write(row_2.str());
|
||||||
|
|
||||||
// set the fade value depending on where we are in the temperature range
|
// set the fade value depending on where we are in the temperature range
|
||||||
if (temperature <= TEMPERATURE_RANGE_MIN_VAL) {
|
if (temperature <= registers[REGISTER_ON_TEMP]) {
|
||||||
fade = 0.0;
|
fade = 0.0;
|
||||||
} else if (temperature >= TEMPERATURE_RANGE_MAX_VAL) {
|
} else if (temperature >= registers[REGISTER_OFF_TEMP]) {
|
||||||
fade = 1.0;
|
fade = 1.0;
|
||||||
} else {
|
} else {
|
||||||
fade = (float)(temperature - TEMPERATURE_RANGE_MIN_VAL) /
|
fade = (float)(temperature - registers[REGISTER_ON_TEMP]) /
|
||||||
(TEMPERATURE_RANGE_MAX_VAL - TEMPERATURE_RANGE_MIN_VAL);
|
(registers[REGISTER_OFF_TEMP] - registers[REGISTER_ON_TEMP]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fade the color components separately
|
// fade the color components separately
|
||||||
@@ -68,45 +61,84 @@ void temperature_update(upm::GroveTemp* temperature_sensor, upm::Jhd1313m1 *lcd)
|
|||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
// check that we are running on Galileo or Edison
|
try {
|
||||||
mraa_platform_t platform = mraa_get_platform_type();
|
// ����� lock-file, ����� ���������� ������ ���� ��������� �������
|
||||||
if ((platform != MRAA_INTEL_GALILEO_GEN1) &&
|
int lockfile = open("defrost.lock", O_RDWR | O_CREAT, 0777);
|
||||||
(platform != MRAA_INTEL_GALILEO_GEN2) &&
|
if (lockfile == -1)
|
||||||
(platform != MRAA_INTEL_EDISON_FAB_C))
|
{
|
||||||
{
|
cout << "Unable to open file for locking, errno = " << errno << "\n";
|
||||||
std::cerr << "Unsupported platform, exiting" << std::endl;
|
return 1;
|
||||||
return MRAA_ERROR_INVALID_PLATFORM;
|
}
|
||||||
}
|
if (flock(lockfile, LOCK_EX | LOCK_NB) == -1)
|
||||||
|
{
|
||||||
|
cout << "Unable to lock file, errno = " << errno << "\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// check that we are running on Galileo or Edison
|
||||||
|
mraa_platform_t platform = mraa_get_platform_type();
|
||||||
|
if ((platform != MRAA_INTEL_GALILEO_GEN1) &&
|
||||||
|
(platform != MRAA_INTEL_GALILEO_GEN2) &&
|
||||||
|
(platform != MRAA_INTEL_EDISON_FAB_C))
|
||||||
|
{
|
||||||
|
std::cerr << "Unsupported platform, exiting" << std::endl;
|
||||||
|
return MRAA_ERROR_INVALID_PLATFORM;
|
||||||
|
}
|
||||||
|
|
||||||
mraa::Gpio gpio(GPIO_PIN_1);
|
mraa::Gpio gpio(GPIO_PIN_1);
|
||||||
mraa_result_t status = gpio.dir(mraa::DIR_OUT);
|
mraa_result_t status = gpio.dir(mraa::DIR_OUT);
|
||||||
if (status != MRAA_SUCCESS)
|
if (status != MRAA_SUCCESS)
|
||||||
|
{
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// temperature sensor connected to A0 (analog in)
|
||||||
|
upm::GroveTemp* temp_sensor = new upm::GroveTemp(0);
|
||||||
|
|
||||||
|
// LCD connected to the default I2C bus
|
||||||
|
upm::Jhd1313m1* lcd = new upm::Jhd1313m1(0);
|
||||||
|
|
||||||
|
if ((temp_sensor == NULL) || (lcd == NULL))
|
||||||
|
|
||||||
|
{
|
||||||
|
std::cerr << "Can't create all objects, exiting" << std::endl;
|
||||||
|
return MRAA_ERROR_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
// load config
|
||||||
|
ConfigParser cfg;
|
||||||
|
cfg.Load("defrost.cfg");
|
||||||
|
mainlog.InitLogging(cfg.GetParam("LogPath"), LIBLOGSINGLEFILE);
|
||||||
|
mainlog.LogString("Defrost service started");
|
||||||
|
|
||||||
|
if (pthread_mutex_init(®ister_mutex, NULL) != 0)
|
||||||
|
{
|
||||||
|
mainlog.LogString("Unable to create registers_mutex");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
registers[REGISTER_ON_TEMP] = 100;
|
||||||
|
registers[REGISTER_OFF_TEMP] = 500;
|
||||||
|
registers[REGISTER_MODE] = 0;
|
||||||
|
|
||||||
|
// run modbus service listen thread
|
||||||
|
ModbusServiceListenThread *modbus_service = new ModbusServiceListenThread(&mainlog, atoi(cfg.GetParam("ListenPort").c_str()));
|
||||||
|
modbus_service->Start();
|
||||||
|
|
||||||
|
int io_value = 0;
|
||||||
|
// loop forever updating the temperature values every second
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
LockMutex(register_mutex);
|
||||||
|
temperature_update(temp_sensor, lcd);
|
||||||
|
if (registers[REGISTER_CUR_TEMP] < registers[REGISTER_ON_TEMP]) io_value = 1;
|
||||||
|
else if (registers[REGISTER_CUR_TEMP] > registers[REGISTER_OFF_TEMP]) io_value = 0;
|
||||||
|
gpio.write(io_value);
|
||||||
|
ReleaseMutex(register_mutex);
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (string s)
|
||||||
{
|
{
|
||||||
|
cerr << "Exception: " << s << "\n";
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// temperature sensor connected to A0 (analog in)
|
|
||||||
upm::GroveTemp* temp_sensor = new upm::GroveTemp(0);
|
|
||||||
|
|
||||||
// LCD connected to the default I2C bus
|
|
||||||
upm::Jhd1313m1* lcd = new upm::Jhd1313m1(0);
|
|
||||||
|
|
||||||
if ((temp_sensor == NULL) || (lcd == NULL))
|
|
||||||
|
|
||||||
{
|
|
||||||
std::cerr << "Can't create all objects, exiting" << std::endl;
|
|
||||||
return MRAA_ERROR_UNSPECIFIED;
|
|
||||||
}
|
|
||||||
|
|
||||||
int io_value = 1;
|
|
||||||
// loop forever updating the temperature values every second
|
|
||||||
for (;;) {
|
|
||||||
temperature_update(temp_sensor, lcd);
|
|
||||||
gpio.write(io_value);
|
|
||||||
io_value = 1 - io_value;
|
|
||||||
std::cerr << io_value << "\n";
|
|
||||||
sleep(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return MRAA_SUCCESS;
|
return MRAA_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
int registers[65536];
|
||||||
|
pthread_mutex_t register_mutex;
|
||||||
|
LogFile mainlog;
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef GLOBALS_HEADER_FILE_INCLUDED
|
||||||
|
#define GLOBALS_HEADER_FILE_INCLUDED
|
||||||
|
|
||||||
|
#include "liblog.h"
|
||||||
|
|
||||||
|
const int REGISTER_ON_TEMP = 0;
|
||||||
|
const int REGISTER_OFF_TEMP = 1;
|
||||||
|
const int REGISTER_MODE = 2;
|
||||||
|
const int REGISTER_CUR_TEMP = 3;
|
||||||
|
const int REGISTER_CUR_STATE = 4;
|
||||||
|
|
||||||
|
extern int registers[65536];
|
||||||
|
extern pthread_mutex_t register_mutex;
|
||||||
|
extern LogFile mainlog;
|
||||||
|
|
||||||
|
#endif
|
||||||
+177
@@ -0,0 +1,177 @@
|
|||||||
|
#include "libdiff.h"
|
||||||
|
|
||||||
|
#include <sys/timeb.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <iconv.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
ostream& operator<<(ostream &out, const pthread_mutex_t m)
|
||||||
|
{
|
||||||
|
out << &m;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LockMutex(pthread_mutex_t &m)
|
||||||
|
{
|
||||||
|
int mres;
|
||||||
|
while ((mres = pthread_mutex_lock(&m)) != 0)
|
||||||
|
{
|
||||||
|
if (mres != EAGAIN)
|
||||||
|
{
|
||||||
|
throw (string)("Fatal error acquiring mutex");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseMutex(pthread_mutex_t &m)
|
||||||
|
{
|
||||||
|
int mres = pthread_mutex_unlock(&m);
|
||||||
|
if (mres != 0)
|
||||||
|
{
|
||||||
|
throw (string)("Error releasing mutex " + ToString(m) + " res = " + ToString(mres));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Round(double v)
|
||||||
|
{
|
||||||
|
if (v < -1e-4) return -(int)(-v + 0.5);
|
||||||
|
else return (int)(v + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CP866ToUTF8(unsigned char *src, unsigned char *dest, int destlen)
|
||||||
|
{
|
||||||
|
iconv_t icd = iconv_open("UTF-8", "CP866");
|
||||||
|
if (icd == (iconv_t)-1)
|
||||||
|
{
|
||||||
|
throw (string)("Unable to open iconv descriptor, errno = " + ToString(errno));
|
||||||
|
}
|
||||||
|
size_t slen = strlen((char*)src);
|
||||||
|
destlen--;
|
||||||
|
// странные временные переменные, потому что иначе компилятор ругается, что
|
||||||
|
// результат приведения типов - не lvalue
|
||||||
|
char *s = (char*)src;
|
||||||
|
char *d = (char*)dest;
|
||||||
|
size_t dl = destlen;
|
||||||
|
size_t t = iconv(icd, &s, &slen, &d, &dl);
|
||||||
|
iconv_close(icd);
|
||||||
|
if (t == (size_t)-1)
|
||||||
|
{
|
||||||
|
throw (string)("Error during string conversion, errno = " + ToString(errno));
|
||||||
|
}
|
||||||
|
// терминируем строку, iconv сам этого не сделает
|
||||||
|
d[0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
string CP1251ToUTF8(const string &src)
|
||||||
|
{
|
||||||
|
unsigned char *dest = new unsigned char[src.length() * 4];
|
||||||
|
CP1251ToUTF8((const unsigned char *)src.c_str(), dest, src.length() * 4);
|
||||||
|
string res((char*)dest);
|
||||||
|
delete[] dest;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CP1251ToUTF8(const unsigned char *src, unsigned char *dest, int destlen)
|
||||||
|
{
|
||||||
|
iconv_t icd = iconv_open("UTF-8", "CP1251");
|
||||||
|
if (icd == (iconv_t)-1)
|
||||||
|
{
|
||||||
|
throw (string)("Unable to open iconv descriptor, errno = " + ToString(errno));
|
||||||
|
}
|
||||||
|
size_t slen = strlen((char*)src);
|
||||||
|
destlen--;
|
||||||
|
// странные временные переменные, потому что иначе компилятор ругается, что
|
||||||
|
// результат приведения типов - не lvalue
|
||||||
|
char *s = (char*)src;
|
||||||
|
char *d = (char*)dest;
|
||||||
|
size_t dl = destlen;
|
||||||
|
size_t t = iconv(icd, &s, &slen, &d, &dl);
|
||||||
|
iconv_close(icd);
|
||||||
|
if (t == (size_t)-1)
|
||||||
|
{
|
||||||
|
throw (string)("Error during string conversion, errno = " + ToString(errno));
|
||||||
|
}
|
||||||
|
// терминируем строку, iconv сам этого не сделает
|
||||||
|
d[0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int UnpackBCD(int a)
|
||||||
|
{
|
||||||
|
if ((a & 0xF) > 9 ||
|
||||||
|
((a >> 4) & 0xF) > 9) throw (string)("Wrong BCD number: " + ToString(a));
|
||||||
|
return (((a >> 4) & 0xF) * 10) + (a & 0xF);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckBCD(int a)
|
||||||
|
{
|
||||||
|
if ((a & 0xF) > 9 ||
|
||||||
|
((a >> 4) & 0xF) > 9) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PackBCD(int a)
|
||||||
|
{
|
||||||
|
return (((a / 10) % 10) << 4) | (a % 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long long GetMilliseconds()
|
||||||
|
{
|
||||||
|
struct timeb tm;
|
||||||
|
if (ftime(&tm) == -1)
|
||||||
|
{
|
||||||
|
// should never happen in fact
|
||||||
|
throw (string)("ftime() failed somehow");
|
||||||
|
}
|
||||||
|
unsigned long long res = tm.time;
|
||||||
|
res *= 1000;
|
||||||
|
res += tm.millitm;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ReplaceAll(string text, string pattern, string replacement)
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
size_t pos = text.find(pattern);
|
||||||
|
if (pos == string::npos) return text;
|
||||||
|
text = text.replace(pos, pattern.length(), replacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string IntToString(int a, int base)
|
||||||
|
{
|
||||||
|
// no itoa in modern C++, what a pity
|
||||||
|
int sign = a < 0 ? -1 : 1;
|
||||||
|
if (a < 0) a = -a;
|
||||||
|
string res;
|
||||||
|
while (a > 0)
|
||||||
|
{
|
||||||
|
int cur = a % base;
|
||||||
|
char c = cur + '0';
|
||||||
|
if (cur > 9) c = cur + 'A' - 10;
|
||||||
|
res += c;
|
||||||
|
a /= base;
|
||||||
|
}
|
||||||
|
if (sign < 0) res += '-';
|
||||||
|
reverse(res.begin(), res.end());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RaiseFDLimit()
|
||||||
|
{
|
||||||
|
struct rlimit rl;
|
||||||
|
if (getrlimit(RLIMIT_NOFILE, &rl) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
rl.rlim_cur = rl.rlim_max;
|
||||||
|
if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return rl.rlim_cur;
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
#ifndef FELIX_THE_CAT_DIFFERENT_LIBRARY_INCLUDED_AKJGNKJABVKALJGLKAJGLAKJG
|
||||||
|
#define FELIX_THE_CAT_DIFFERENT_LIBRARY_INCLUDED_AKJGNKJABVKALJGLKAJGLAKJG
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
string ToString(const T& a)
|
||||||
|
{
|
||||||
|
ostringstream oss;
|
||||||
|
oss.setf(ios::fixed | ios::showpoint);
|
||||||
|
oss.precision(10);
|
||||||
|
oss << a;
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
ostream& operator<<(ostream &out, const pthread_mutex_t m);
|
||||||
|
|
||||||
|
void LockMutex(pthread_mutex_t &m);
|
||||||
|
void ReleaseMutex(pthread_mutex_t &m);
|
||||||
|
|
||||||
|
|
||||||
|
int Round(double v);
|
||||||
|
|
||||||
|
// конвертирует из CP866 (в котором часто девайсы шлют всякую фигню)
|
||||||
|
// в UTF-8 (в котором весь наш код), чтобы можно было сравнивать строки
|
||||||
|
// и не иметь в исходниках две кодировки
|
||||||
|
// src предполагается нуль-терминированным
|
||||||
|
void CP866ToUTF8(unsigned char *src, unsigned char *dest, int destlen);
|
||||||
|
// То же самое для CP1251
|
||||||
|
void CP1251ToUTF8(const unsigned char *src, unsigned char *dest, int destlen);
|
||||||
|
|
||||||
|
string CP1251ToUTF8(const string &src);
|
||||||
|
|
||||||
|
// распаковывает BCD-число. Сделано в виде функции, а не макроса, чтобы
|
||||||
|
// можно было подставлять в нее функции, изменяющие состояние параметров
|
||||||
|
int UnpackBCD(int a);
|
||||||
|
// аналогично, упаковывает BCD-число
|
||||||
|
int PackBCD(int a);
|
||||||
|
// проверяет, является ли a валидным BCD-байтом
|
||||||
|
bool CheckBCD(int a);
|
||||||
|
|
||||||
|
|
||||||
|
// получает количество миллисекунд с начала эпохи (иногда нужно для)
|
||||||
|
// всяких точных таймаутов при приеме/отправке
|
||||||
|
unsigned long long GetMilliseconds();
|
||||||
|
|
||||||
|
// заменяет в text все вхождения pattern на replacement
|
||||||
|
// аккуратно со случаем, когда replacement содержит pattern - может повиснуть (!)
|
||||||
|
string ReplaceAll(string text, string pattern, string replacement);
|
||||||
|
|
||||||
|
// конвертирует число в строку в заданной base
|
||||||
|
string IntToString(int a, int base = 10);
|
||||||
|
|
||||||
|
// поднимает до упора лимит на количество открытых дескрипторов
|
||||||
|
int RaiseFDLimit();
|
||||||
|
|
||||||
|
#endif
|
||||||
+230
@@ -0,0 +1,230 @@
|
|||||||
|
#include "liblog.h"
|
||||||
|
#include "libdiff.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
LogFile::LogFile()
|
||||||
|
{
|
||||||
|
logmethod = -1;
|
||||||
|
logpath = "";
|
||||||
|
file = NULL;
|
||||||
|
initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string IntToLength(int a, int len)
|
||||||
|
{
|
||||||
|
string s = ToString(a);
|
||||||
|
while ((int)s.length() < len) s = '0' + s;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LogFile::InitLogging(string logpath, int method, string prefix, bool reopen)
|
||||||
|
{
|
||||||
|
this->logmethod = method;
|
||||||
|
this->logpath = logpath;
|
||||||
|
this->fileprefix = prefix;
|
||||||
|
this->reopen = reopen;
|
||||||
|
// инициализируем mutex
|
||||||
|
if (pthread_mutex_init(&filemutex, NULL) != 0)
|
||||||
|
{
|
||||||
|
throw (string)("Unable to create mutex");
|
||||||
|
}
|
||||||
|
if (!reopen)
|
||||||
|
{
|
||||||
|
// открываем файл
|
||||||
|
if (method == LIBLOGSINGLEFILE)
|
||||||
|
{
|
||||||
|
// формируем имя файла
|
||||||
|
ostringstream filename;
|
||||||
|
filename << logpath << "/" << fileprefix << ".log";
|
||||||
|
file = OpenFileWithRetries(filename.str(), "at");
|
||||||
|
if (file == NULL)
|
||||||
|
{
|
||||||
|
throw (string)("Unable to open file for logging");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct tm curtime;
|
||||||
|
time_t ct = time(NULL);
|
||||||
|
localtime_r(&ct, &curtime);
|
||||||
|
// теперь имя файла выглядит намного забавнее
|
||||||
|
ostringstream filename;
|
||||||
|
filename << logpath << "/" << fileprefix << curtime.tm_year + 1900 << IntToLength(curtime.tm_mon + 1, 2)
|
||||||
|
<< IntToLength(curtime.tm_mday, 2) << ".log";
|
||||||
|
file = OpenFileWithRetries(filename.str(), "at");
|
||||||
|
lastname = filename.str();
|
||||||
|
if (file == NULL)
|
||||||
|
{
|
||||||
|
throw (string)("Unable to open file for logging");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// записывает в лог-файл строку
|
||||||
|
// файл определяется исходя из метода
|
||||||
|
// строка предваряется текущим временем
|
||||||
|
// в некотором формате
|
||||||
|
void LogFile::LogString(string s, string pfx)
|
||||||
|
{
|
||||||
|
// Если лог еще не инициализирован, ничего не пишем (штатная ситуация, если,
|
||||||
|
// например, лог пришлось заглушить).
|
||||||
|
if (!initialized) return;
|
||||||
|
#ifdef FTC_DEBUG
|
||||||
|
cout << s << endl;
|
||||||
|
#endif
|
||||||
|
int res;
|
||||||
|
while ((res = pthread_mutex_lock(&filemutex)) != 0)
|
||||||
|
{
|
||||||
|
if (res != EAGAIN)
|
||||||
|
{
|
||||||
|
throw (string)("Fatal error acquiring mutex");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct tm curtime;
|
||||||
|
time_t ct = time(NULL);
|
||||||
|
localtime_r(&ct, &curtime);
|
||||||
|
if (this->logmethod == LIBLOGSINGLEFILE)
|
||||||
|
{
|
||||||
|
if (reopen)
|
||||||
|
{
|
||||||
|
// формируем имя файла
|
||||||
|
ostringstream filename;
|
||||||
|
filename << logpath << "/" << fileprefix << ".log";
|
||||||
|
file = OpenFileWithRetries(filename.str(), "at");
|
||||||
|
if (file == NULL)
|
||||||
|
{
|
||||||
|
throw (string)("Unable to open file for logging");
|
||||||
|
pthread_mutex_unlock(&filemutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// теперь сгенерить строку
|
||||||
|
ostringstream oss;
|
||||||
|
oss << pfx << " " << IntToLength(curtime.tm_year + 1900, 4) << "-" << IntToLength(curtime.tm_mon + 1, 2) << "-"
|
||||||
|
<< IntToLength(curtime.tm_mday, 2) << " " << IntToLength(curtime.tm_hour, 2)
|
||||||
|
<< ":" << IntToLength(curtime.tm_min, 2) << ":" << IntToLength(curtime.tm_sec, 2);
|
||||||
|
oss << " " << s + "\n";
|
||||||
|
// и записать ее куда следует
|
||||||
|
fprintf(file, "%s", oss.str().c_str());
|
||||||
|
if (reopen)
|
||||||
|
{
|
||||||
|
if (file != NULL)
|
||||||
|
{
|
||||||
|
fclose(file);
|
||||||
|
file = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (this->logmethod == LIBLOGDAYFILES)
|
||||||
|
{
|
||||||
|
// переоткрываем файл если нужно
|
||||||
|
ostringstream filename;
|
||||||
|
filename << logpath << "/" << fileprefix << curtime.tm_year + 1900 << IntToLength(curtime.tm_mon + 1, 2)
|
||||||
|
<< IntToLength(curtime.tm_mday, 2) << ".log";
|
||||||
|
if (!reopen)
|
||||||
|
{
|
||||||
|
if (filename.str() != lastname)
|
||||||
|
{
|
||||||
|
if (file != NULL)
|
||||||
|
{
|
||||||
|
cerr << "LIBLOG, fclose " << file << "\n";
|
||||||
|
cerr << "LIBLOG, lastname = " << lastname << " newname = " << filename.str() << "\n";
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
file = OpenFileWithRetries(filename.str(), "at");
|
||||||
|
if (file == NULL)
|
||||||
|
{
|
||||||
|
throw (string)("Unable to open file for logging");
|
||||||
|
pthread_mutex_unlock(&filemutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastname = filename.str();
|
||||||
|
cerr << "LIBLOG, reopened file = " << file << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
file = OpenFileWithRetries(filename.str(), "at");
|
||||||
|
if (file == NULL)
|
||||||
|
{
|
||||||
|
throw (string)("Unable to open file for logging");
|
||||||
|
pthread_mutex_unlock(&filemutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// теперь сгенерить строку
|
||||||
|
ostringstream oss;
|
||||||
|
oss << pfx << " " << IntToLength(curtime.tm_hour, 2)
|
||||||
|
<< ":" << IntToLength(curtime.tm_min, 2) << ":" << IntToLength(curtime.tm_sec, 2);
|
||||||
|
oss << " " << s << "\n";
|
||||||
|
// и записать ее куда следует
|
||||||
|
if (file != NULL) fprintf(file, "%s", oss.str().c_str());
|
||||||
|
if (reopen)
|
||||||
|
{
|
||||||
|
if (file != NULL)
|
||||||
|
{
|
||||||
|
fclose(file);
|
||||||
|
file = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ничего не делаем - неинициализированный лог - не проблема
|
||||||
|
}
|
||||||
|
if (!reopen)
|
||||||
|
{
|
||||||
|
if (file != NULL) fflush(file);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&filemutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
char GHC(char c)
|
||||||
|
{
|
||||||
|
if (c < 10) return (c + '0');
|
||||||
|
else return (c - 10 + 'A');
|
||||||
|
}
|
||||||
|
|
||||||
|
string GetHex(unsigned char c)
|
||||||
|
{
|
||||||
|
string res;
|
||||||
|
int l, h;
|
||||||
|
h = (c >> 4) & 15;
|
||||||
|
l = c & 15;
|
||||||
|
res += GHC(h);
|
||||||
|
res += GHC(l);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FILE* LogFile::OpenFileWithRetries(string fname, const char *mode)
|
||||||
|
{
|
||||||
|
FILE *res = fopen(fname.c_str(), mode);
|
||||||
|
int rcnt = 0;
|
||||||
|
while (res == NULL)
|
||||||
|
{
|
||||||
|
rcnt++;
|
||||||
|
if (rcnt > LIBLOGNUMRETRIESTOOPEN) break;
|
||||||
|
sleep(1);
|
||||||
|
res = fopen(fname.c_str(), mode);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
string GetHexString(unsigned int a)
|
||||||
|
{
|
||||||
|
return GetHex((a >> 24) & 0xFF) + GetHex((a >> 16) & 0xFF) + GetHex((a >> 8) & 0xFF) + GetHex(a & 0xFF);
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#ifndef FELIX_THE_CAT_LOGGING_LIBRARY_HEADER_FILE_INCLUDED_EJGHJWVNKJNWKJGNKJWGJKGKJHKJH
|
||||||
|
#define FELIX_THE_CAT_LOGGING_LIBRARY_HEADER_FILE_INCLUDED_EJGHJWVNKJNWKJGNKJWGJKGKJHKJH
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// библиотечка, выполняющая запись в логи
|
||||||
|
// два метода - один мегафайл или много
|
||||||
|
// файлов по одному на каждый день
|
||||||
|
const int LIBLOGSINGLEFILE = 1;
|
||||||
|
const int LIBLOGDAYFILES = 2;
|
||||||
|
const int LIBLOGNUMRETRIESTOOPEN = 1;
|
||||||
|
|
||||||
|
|
||||||
|
class LogFile
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// запрещаем копирование и присваивание лог-файла (чтобы FILE* не копипастился почем зря)
|
||||||
|
// Передаем всегда указатели на лог
|
||||||
|
LogFile(const LogFile &l);
|
||||||
|
void operator=(const LogFile &l);
|
||||||
|
bool initialized;
|
||||||
|
public:
|
||||||
|
// mutex для блокирования дескриптора файла (чтобы толпой в один дескриптор не писать)
|
||||||
|
pthread_mutex_t filemutex;
|
||||||
|
// файл для записи
|
||||||
|
FILE *file;
|
||||||
|
// и его имя
|
||||||
|
string lastname;
|
||||||
|
int logmethod; // метод записи (смтор константы)
|
||||||
|
string logpath; // куда писать
|
||||||
|
string fileprefix; // префикс, который перед файлами генерировать
|
||||||
|
bool reopen; // нужно ли каждый раз переоткрывать лог-файл
|
||||||
|
LogFile();
|
||||||
|
void InitLogging(string logpath, int method, string prefix = "", bool reopen = false);
|
||||||
|
void LogString(string s, string pfx = "");
|
||||||
|
FILE *OpenFileWithRetries(string fname, const char *mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
string GetHex(unsigned char c);
|
||||||
|
string GetHexString(unsigned int a);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user