<commit_before>#include "FireSight.hpp"
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdio.h>
#include <time.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include "firefuse.h"
#include "version.h"

int SERIAL_TIMEOUT_SECS = 10;

bool is_cnc_path(const char *path) {
    for (const char *s = path; s && *s; s++) {
        if (strncmp("/cnc", s, 4) == 0) {
            return TRUE;
        }
    }
    return FALSE;
}

int cnc_getattr(const char *path, struct stat *stbuf) {
    int res = 0;
    if (firefuse_isFile(path, FIREREST_GCODE_FIRE)) {
        res = firefuse_getattr_file(path, stbuf, worker.dce(path).src_gcode_fire.peek().size(), 0666);
    } else {
        res = firerest_getattr_default(path, stbuf);
    }
    if (res == 0) {
        LOGTRACE2("cnc_getattr(%s) stat->st_size:%ldB -> OK", path, (ulong) stbuf->st_size);
    }
    return res;
}

int cnc_open(const char *path, struct fuse_file_info *fi) {
    int result = 0;

    if (firefuse_isFile(path, FIREREST_GCODE_FIRE)) {
        if (verifyOpenRW(path, fi, &result)) {
            if ((fi->flags&3) == O_WRONLY && worker.dce(path).snk_gcode_fire.isFresh()) {
                LOGTRACE("snk_gcode_fire.isFresh()");
                result = -EAGAIN;
            } else {
                fi->fh = (uint64_t) (size_t) new SmartPointer<char>(worker.dce(path).src_gcode_fire.get());
            }
        }
    }
    return result;
}

int cnc_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
    int result = 0;
    return result;
}

int cnc_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
    size_t sizeOut = size;
    size_t len;
    (void) fi;

    if (firefuse_isFile(path, FIREREST_GCODE_FIRE) ||
            firefuse_isFile(path, FIREREST_PROPERTIES_JSON) ||
            FALSE) {
        SmartPointer<char> *pData = (SmartPointer<char> *) fi->fh;
        sizeOut = firefuse_readBuffer(buf, (char *)pData->data(), size, offset, pData->size());
    } else {
        LOGERROR2("cnc_read(%s, %ldB) ENOENT", path, size);
        return -ENOENT;
    }

    LOGTRACE3("cnc_read(%s, %ldB) -> %ldB", path, size, sizeOut);
    return sizeOut;
}

int cnc_write(const char *path, const char *buf, size_t bufsize, off_t offset, struct fuse_file_info *fi) {
    assert(offset == 0);
    assert(buf != NULL);
    assert(bufsize >= 0);
    SmartPointer<char> data((char *) buf, bufsize);
    if (firefuse_isFile(path, FIREREST_GCODE_FIRE)) {
		DCE &dce = worker.dce(path);
		dce.setSync(FireREST::isSync(path));
		dce.send(data);
        string cmd(buf, bufsize);
        LOGTRACE2("DCE::cnc_write(%s) sync:%d", cmd.c_str(), dce.isSync());
        json_t * response = json_object();
        json_object_set(response, "status", json_string("ACTIVE"));
        json_object_set(response, "gcode", json_string(cmd.c_str()));
        char *responseStr = json_dumps(response, JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_INDENT(0));
        worker.dce(path).src_gcode_fire.post(
			SmartPointer<char>(responseStr, strlen(responseStr), SmartPointer<char>::MANAGE));
        json_decref(response);
    } else {
        LOGERROR2("cnc_write(%s,%ldB) ENOENT", path, bufsize);
        return -ENOENT;
    }

    return bufsize;
}

int cnc_release(const char *path, struct fuse_file_info *fi) {
    int result = 0;
    LOGTRACE1("cnc_release(%s)", path);
    if (firefuse_isFile(path, FIREREST_GCODE_FIRE)) {
        if (fi->fh) {
            free( (SmartPointer<char> *) fi->fh);
        }
    }
    return 0;
}


int cnc_truncate(const char *path, off_t size) {
    int result = 0;
    return result;
}

//////////////////////////////// DCE //////////////////////////////////

#define CMDMAX 255
#define INBUFMAX 3000
#define JSONMAX 3000

DCE::DCE(string name) {
    this->name = name;
	this->is_sync = FALSE;
    this->serial_fd = -1;
    this->jsonBuf = (char*)malloc(JSONMAX+3); // +nl, cr, EOS
    this->inbuf = (char*)malloc(INBUFMAX+1); // +EOS
	LOGTRACE2("DCE::DCE(%s) isSync:%d", name.c_str(), is_sync);
    init();
}

DCE::~DCE() {
    if (jsonBuf) {
        free(jsonBuf);
    }
    if (inbuf) {
        free(inbuf);
    }
}

void DCE::setSync(bool value) {
	if (value != is_sync) {
		if (activeRequests > 0) {
			LOGWARN2("DCE::setSync(%d) clearing activeRequests:%d", value, activeRequests);
			activeRequests = 0;
		} 
		is_sync = value;
		LOGINFO1("DCE::setSync(%d)", value);
	}
}

void DCE::init() {
    LOGINFO1("DCE::init(%s)", name.c_str());
    const char *emptyJson = "{}";
    src_gcode_fire.post(SmartPointer<char>((char *)emptyJson, strlen(emptyJson)));
    if (serial_fd >= 0) {
        LOGINFO2("DCE::init(%s) close serial port: %s", name.c_str(), serial_path.c_str());
        close(serial_fd);
        serial_fd = -1;
    }
    jsonLen = 0;
    jsonDepth = 0;
    inbuflen = 0;
    inbufEmptyLine = 0;
    activeRequests = 0;
}

void DCE::send(SmartPointer<char> &data) {
	snk_gcode_fire.post(data);
	if (is_sync) {
		time_t starttime = time(NULL);
		double seconds = 0;
		while (snk_gcode_fire.isFresh() || activeRequests > 0) {
			seconds = difftime(time(NULL), starttime);
			if (seconds > SERIAL_TIMEOUT_SECS) {
				break;
			}
			usleep(100*1000);
		}
		if (activeRequests > 0 && seconds > SERIAL_TIMEOUT_SECS) {
			LOGERROR1("DCE::serial_send_eol() SERIAL TIMEOUT:%ds", SERIAL_TIMEOUT_SECS);
		} else {
			LOGDEBUG2("DCE::serial_send_eol() request complete:%gs activeRequests:%d", seconds, activeRequests);
		}
	}
}

/**
 * Return canonical DCE path. E.g.:
 *   /dev/firefuse/sync/cnc/marlin/gcode.fire => /cnc/marlin
 *   /dev/firefuse/cnc/marlin/gcode.fire => /cnc/marlin
 *
 * There can be at most one DCE for each serial port,
 * since the serial_reader_thread must have exclusive access
 * to that port.
 *
 * Return empty string if path is not a canonical DCE path
 */
string DCE::dce_path(const char *pPath) {
    if (pPath == NULL) {
        return string();
    }
    const char *pSlash = NULL;
    const char *pDce = NULL;
    for (const char *s=pPath; *s; s++) {
        if (*s == '/') {
            pSlash = s;
            if (strncmp("/cnc/", s, 5) == 0) {
                pDce = s;
                s += 4;
            } else if (pDce) {
                break;<commit_msg>millis<commit_after>#include "FireSight.hpp"
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdio.h>
#include <time.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include "firefuse.h"
#include "version.h"

int SERIAL_TIMEOUT_SECS = 10;

bool is_cnc_path(const char *path) {
    for (const char *s = path; s && *s; s++) {
        if (strncmp("/cnc", s, 4) == 0) {
            return TRUE;
        }
    }
    return FALSE;
}

int cnc_getattr(const char *path, struct stat *stbuf) {
    int res = 0;
    if (firefuse_isFile(path, FIREREST_GCODE_FIRE)) {
        res = firefuse_getattr_file(path, stbuf, worker.dce(path).src_gcode_fire.peek().size(), 0666);
    } else {
        res = firerest_getattr_default(path, stbuf);
    }
    if (res == 0) {
        LOGTRACE2("cnc_getattr(%s) stat->st_size:%ldB -> OK", path, (ulong) stbuf->st_size);
    }
    return res;
}

int cnc_open(const char *path, struct fuse_file_info *fi) {
    int result = 0;

    if (firefuse_isFile(path, FIREREST_GCODE_FIRE)) {
        if (verifyOpenRW(path, fi, &result)) {
            if ((fi->flags&3) == O_WRONLY && worker.dce(path).snk_gcode_fire.isFresh()) {
                LOGTRACE("snk_gcode_fire.isFresh()");
                result = -EAGAIN;
            } else {
                fi->fh = (uint64_t) (size_t) new SmartPointer<char>(worker.dce(path).src_gcode_fire.get());
            }
        }
    }
    return result;
}

int cnc_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
    int result = 0;
    return result;
}

int cnc_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
    size_t sizeOut = size;
    size_t len;
    (void) fi;

    if (firefuse_isFile(path, FIREREST_GCODE_FIRE) ||
            firefuse_isFile(path, FIREREST_PROPERTIES_JSON) ||
            FALSE) {
        SmartPointer<char> *pData = (SmartPointer<char> *) fi->fh;
        sizeOut = firefuse_readBuffer(buf, (char *)pData->data(), size, offset, pData->size());
    } else {
        LOGERROR2("cnc_read(%s, %ldB) ENOENT", path, size);
        return -ENOENT;
    }

    LOGTRACE3("cnc_read(%s, %ldB) -> %ldB", path, size, sizeOut);
    return sizeOut;
}

int cnc_write(const char *path, const char *buf, size_t bufsize, off_t offset, struct fuse_file_info *fi) {
    assert(offset == 0);
    assert(buf != NULL);
    assert(bufsize >= 0);
    SmartPointer<char> data((char *) buf, bufsize);
    if (firefuse_isFile(path, FIREREST_GCODE_FIRE)) {
		DCE &dce = worker.dce(path);
		dce.setSync(FireREST::isSync(path));
		dce.send(data);
        string cmd(buf, bufsize);
        LOGTRACE2("DCE::cnc_write(%s) sync:%d", cmd.c_str(), dce.isSync());
        json_t * response = json_object();
        json_object_set(response, "status", json_string("ACTIVE"));
        json_object_set(response, "gcode", json_string(cmd.c_str()));
        char *responseStr = json_dumps(response, JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_INDENT(0));
        worker.dce(path).src_gcode_fire.post(
			SmartPointer<char>(responseStr, strlen(responseStr), SmartPointer<char>::MANAGE));
        json_decref(response);
    } else {
        LOGERROR2("cnc_write(%s,%ldB) ENOENT", path, bufsize);
        return -ENOENT;
    }

    return bufsize;
}

int cnc_release(const char *path, struct fuse_file_info *fi) {
    int result = 0;
    LOGTRACE1("cnc_release(%s)", path);
    if (firefuse_isFile(path, FIREREST_GCODE_FIRE)) {
        if (fi->fh) {
            free( (SmartPointer<char> *) fi->fh);
        }
    }
    return 0;
}


int cnc_truncate(const char *path, off_t size) {
    int result = 0;
    return result;
}

//////////////////////////////// DCE //////////////////////////////////

#define CMDMAX 255
#define INBUFMAX 3000
#define JSONMAX 3000

DCE::DCE(string name) {
    this->name = name;
	this->is_sync = FALSE;
    this->serial_fd = -1;
    this->jsonBuf = (char*)malloc(JSONMAX+3); // +nl, cr, EOS
    this->inbuf = (char*)malloc(INBUFMAX+1); // +EOS
	LOGTRACE2("DCE::DCE(%s) isSync:%d", name.c_str(), is_sync);
    init();
}

DCE::~DCE() {
    if (jsonBuf) {
        free(jsonBuf);
    }
    if (inbuf) {
        free(inbuf);
    }
}

void DCE::setSync(bool value) {
	if (value != is_sync) {
		if (activeRequests > 0) {
			LOGWARN2("DCE::setSync(%d) clearing activeRequests:%d", value, activeRequests);
			activeRequests = 0;
		} 
		is_sync = value;
		LOGINFO1("DCE::setSync(%d)", value);
	}
}

void DCE::init() {
    LOGINFO1("DCE::init(%s)", name.c_str());
    const char *emptyJson = "{}";
    src_gcode_fire.post(SmartPointer<char>((char *)emptyJson, strlen(emptyJson)));
    if (serial_fd >= 0) {
        LOGINFO2("DCE::init(%s) close serial port: %s", name.c_str(), serial_path.c_str());
        close(serial_fd);
        serial_fd = -1;
    }
    jsonLen = 0;
    jsonDepth = 0;
    inbuflen = 0;
    inbufEmptyLine = 0;
    activeRequests = 0;
}

void DCE::send(SmartPointer<char> &data) {
	long msStart = millis();
	snk_gcode_fire.post(data);
	if (is_sync) {
		double seconds = 0;
		while (snk_gcode_fire.isFresh() || activeRequests > 0) {
			seconds = (millis() - msStart)/1000.0;
			if (seconds > SERIAL_TIMEOUT_SECS) {
				break;
			}
			usleep(100*1000);
		}
		if (activeRequests > 0 && seconds > SERIAL_TIMEOUT_SECS) {
			LOGERROR1("DCE::send() SERIAL TIMEOUT:%ds", SERIAL_TIMEOUT_SECS);
		} else {
			LOGDEBUG2("DCE::send() request complete:%gs activeRequests:%d", seconds, activeRequests);
		}
	}
}

/**
 * Return canonical DCE path. E.g.:
 *   /dev/firefuse/sync/cnc/marlin/gcode.fire => /cnc/marlin
 *   /dev/firefuse/cnc/marlin/gcode.fire => /cnc/marlin
 *
 * There can be at most one DCE for each serial port,
 * since the serial_reader_thread must have exclusive access
 * to that port.
 *
 * Return empty string if path is not a canonical DCE path
 */
string DCE::dce_path(const char *pPath) {
    if (pPath == NULL) {
        return string();
    }
    const char *pSlash = NULL;
    const char *pDce = NULL;
    for (const char *s=pPath; *s; s++) {
        if (*s == '/') {
            pSlash = s;
            if (strncmp("/cnc/", s, 5) == 0) {
                pDce = s;
                s += 4;
            } else if (pDce) {
                break;
{'fraction_non_alphanumeric': 0.16369993642720915, 'fraction_numerical': 0.01152256834075016, 'mean_word_length': 2.858062538320049, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 89, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6102731', 'n_tokens_mistral': 4898, 'n_tokens_neox': 4663, 'n_words': 989}