<commit_before>/*
 * Copyright 2014 The Imaging Source Europe GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "tcamgstbase.h"
#include "base_types.h"

#include <stddef.h> // NULL
#include <string.h> // strcmp

GstElement* tcam_gst_find_camera_src (GstElement* element)
{

    GstElement* e = GST_ELEMENT( gst_object_get_parent(GST_OBJECT(element)));

    GList* l = GST_BIN(e)->children;
    GstElement* ret = nullptr;
    while (1==1)
    {
        const char* name = g_type_name(gst_element_factory_get_element_type(gst_element_get_factory((GstElement*)l->data)));

        if (g_strcmp0(name, "GstTcamSrc") == 0)
        {
            ret = (GstElement*)l->data;
            break;
        }

        if (g_list_next(l) == NULL)
            break;

        l = g_list_next(l);
    }

    if (ret == nullptr)
    {
        GST_ERROR("Camera source not set!");
    }
    return ret;
}

bool tcam_gst_raw_only_has_mono (const GstCaps* src_caps)
{
    GstCaps* raw_filter = gst_caps_from_string("video/x-raw");

    GstCaps* caps = gst_caps_intersect(src_caps, raw_filter);

    gst_caps_unref(raw_filter);

    GstCaps* filter = gst_caps_from_string("video/x-raw,format={GRAY8, GRAY16_LE}; ANY");

    GstCaps* sub = gst_caps_subtract(caps, filter);

    gst_caps_unref(filter);

    if (gst_caps_is_empty(sub))
    {
        gst_caps_unref(sub);
        return TRUE;
    }
    gst_caps_unref(sub);

    gst_caps_unref(caps);

    return FALSE;
}

bool tcam_gst_is_fourcc_bayer (const unsigned int fourcc)
{
    if (fourcc == GST_MAKE_FOURCC('g', 'b', 'r', 'g')
        || fourcc == GST_MAKE_FOURCC('g', 'r', 'b', 'g')
        || fourcc == GST_MAKE_FOURCC('r', 'g', 'g', 'b')
        || fourcc == GST_MAKE_FOURCC('b', 'g', 'g', 'r'))
    {
        return TRUE;
    }
    return FALSE;
}

bool tcam_gst_is_fourcc_rgb (const unsigned int fourcc)
{
    if (fourcc == GST_MAKE_FOURCC('R', 'G', 'B', 'x')
        || fourcc == GST_MAKE_FOURCC('x', 'R', 'G', 'B')
        || fourcc == GST_MAKE_FOURCC('B', 'G', 'R', 'x')
        || fourcc == GST_MAKE_FOURCC('x', 'B', 'G', 'R')
        || fourcc == GST_MAKE_FOURCC('R', 'G', 'B', 'A')
        || fourcc == GST_MAKE_FOURCC('A', 'R', 'G', 'B')
        || fourcc == GST_MAKE_FOURCC('B', 'G', 'R', 'A')
        || fourcc == GST_MAKE_FOURCC('A', 'B', 'G', 'R'))
    {
        return TRUE;
    }

    return FALSE;
}

bool tcam_gst_fixate_caps (GstCaps* caps)
{
    if (caps == nullptr || gst_caps_is_empty(caps))
    {
        return FALSE;
    }

    GstStructure* structure = gst_caps_get_structure(caps, 0);

    if (gst_structure_has_field(structure, "width"))
    {
        gst_structure_fixate_field_nearest_int(structure, "width", G_MAXINT);
    }
    if (gst_structure_has_field(structure, "height"))
    {
        gst_structure_fixate_field_nearest_int(structure, "height", G_MAXINT);
    }
    if (gst_structure_has_field(structure, "framerate"))
    {
        gst_structure_fixate_field_nearest_fraction(structure, "framerate", G_MAXINT, 1);
    }

    return TRUE;
}

void gst_caps_change_name (GstCaps* caps, const char* name)
{
    for (unsigned int i = 0; i < gst_caps_get_size(caps); ++i)
    {
        GstStructure* struc = gst_caps_get_structure(caps, i);

        if (struc != nullptr)
        {
            gst_structure_set_name(struc, name);
            gst_structure_remove_field(struc, "format");
        }
    }
}

GstCaps* bayer_transform_intersect (const GstCaps* bayer, const GstCaps* raw)
{
    GstCaps* caps2 = gst_caps_copy(raw);
    gst_caps_change_name(caps2, "video/x-bayer");
    GstCaps* caps1 = gst_caps_intersect((GstCaps*)bayer, caps2);
    return caps1;
}

bool gst_caps_are_bayer_only (const GstCaps* caps)
{
    for (unsigned int i = 0; i < gst_caps_get_size(caps); ++i)
    {
        GstStructure* struc = gst_caps_get_structure(caps, i);

        if (strcmp(gst_structure_get_name(struc), "video/x-bayer") != 0)
        {
            return false;
        }
    }
    return true;
}

GstCaps* tcam_gst_find_largest_caps (const GstCaps* incoming)
{
    int largest_index = -1;
    int largest_width = -1;
    int largest_height = -1;

    for (int i = 0; i < gst_caps_get_size(incoming); ++i)
    {
        GstStructure* struc = gst_caps_get_structure(incoming, i);

        int width = -1;
        int height = -1;
        bool new_width = false;
        bool new_height = false;

        // will fail if width is a range so we only handle
        // halfway fixated caps
        if (gst_structure_get_int(struc, "width", &width))
        {
            if (largest_width < width)
            {
                largest_width = width;
                new_width = true;
            }
        }

        if (gst_structure_get_int(struc, "height", &height))
        {
            if (largest_height < height)
            {
                largest_height = height;
                new_height = true;
            }
        }

        if (new_width && new_height)
        {
            largest_index = i;
        }
    }

    GstCaps* largest_caps = gst_caps_copy_nth(incoming, largest_index);

    if (!tcam_gst_fixate_caps(largest_caps))
    {
        GST_ERROR("Cannot fixate largest caps. Returning NULL");
        return nullptr;
    }

    GstStructure* s = gst_caps_get_structure(largest_caps, 0);

    int h;
    gst_structure_get_int(s, "height", &h);
    int w;
    gst_structure_get_int(s, "width", &w);

    int num;
    int den;
    gst_structure_get_fraction(s, "framerate", &num, &den);

    GValue vh = {};

    g_value_init(&vh, G_TYPE_INT);
    g_value_set_int(&vh, h);

    gst_caps_set_value(largest_caps, "height", &vh);
    largest_caps = gst_caps_new_simple (gst_structure_get_name(s),
                                        "framerate", GST_TYPE_FRACTION, num, den,
                                        "width", G_TYPE_INT, w,
                                        "height", G_TYPE_INT, h,
                                        NULL);

    return largest_caps;
}

bool contains_bayer (const GstCaps* caps)
{
    for (unsigned int i = 0; i < gst_caps_get_size(caps); ++i)
    {
        if (strcmp("video/x-bayer", gst_structure_get_name(gst_caps_get_structure(caps, i))) == 0)
        {
            return true;
        }
    }

    return false;
}

GstCaps* get_caps_from_element (const char* elementname, const char* padname)
{

    GstElement* ele = gst_element_factory_make(elementname, "tmp-element");

    if (!ele)
    {
        return nullptr;
    }

    GstCaps* ret = gst_pad_query_caps(gst_element_get_static_pad(ele, padname), NULL);

    gst_object_unref(ele);

    return ret;
}

GstCaps* find_input_caps (const GstCaps* available_caps,
                          const GstCaps* wanted_caps,
                          bool& requires_conversion)
{
    GstCaps* ret = nullptr;
    requires_conversion = false;

    if (!GST_IS_CAPS(available_caps))
    {
        return nullptr;
    }

    if (wanted_caps == nullptr || gst_caps_is_empty(wanted_caps))
    {
        GST_INFO("No sink caps specified. Continuing with caps from source device.");
        wanted_caps = gst_caps_copy(available_caps);
    }

    GstCaps* intersect = gst_caps_intersect(available_caps, wanted_caps);

    if (gst_caps_is_empty(intersect))
    {
        GstElementFactory* debayer = gst_element_factory_find("bayer2rgb");

        if (gst_element_factory_can_src_any_caps(debayer, wanted_caps)
            && gst_element_factory_can_sink_any_caps(debayer, available_caps))
        {
            requires_conversion = true;
            GstCaps* temp = get_caps_from_element("bayer2rgb", "sink");

            ret = gst_caps_intersect(available_caps, temp);

            gst_caps_unref(temp);
        }

        gst_object_unref(debayer);
    }
    else
    {
        return intersect;
    }

    return ret;
}
<commit_msg>Simplify tcam_gst_raw_only_has_mono<commit_after>/*
 * Copyright 2014 The Imaging Source Europe GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "tcamgstbase.h"
#include "base_types.h"

#include <stddef.h> // NULL
#include <string.h> // strcmp
#include <algorithm> //std::find

GstElement* tcam_gst_find_camera_src (GstElement* element)
{

    GstElement* e = GST_ELEMENT( gst_object_get_parent(GST_OBJECT(element)));

    GList* l = GST_BIN(e)->children;
    GstElement* ret = nullptr;
    while (1==1)
    {
        const char* name = g_type_name(gst_element_factory_get_element_type(gst_element_get_factory((GstElement*)l->data)));

        if (g_strcmp0(name, "GstTcamSrc") == 0)
        {
            ret = (GstElement*)l->data;
            break;
        }

        if (g_list_next(l) == NULL)
            break;

        l = g_list_next(l);
    }

    if (ret == nullptr)
    {
        GST_ERROR("Camera source not set!");
    }
    return ret;
}

bool tcam_gst_raw_only_has_mono (const GstCaps* caps)
{
    auto correct_format = [] (const char* str)
    {
        const static std::vector<std::string> formats = {"GRAY8", "GRAY16_LE", "GRAY16_BE"};

        if (std::find(formats.begin(), formats.end(), str) == formats.end())
        {
            return false;
        }
        return true;
    };

    for (unsigned int i = 0; i < gst_caps_get_size(caps); ++i)
    {
        GstStructure* struc = gst_caps_get_structure(caps, i);

        if (strcmp("video/x-raw", gst_structure_get_name(struc)) == 0)
        {
            if (gst_structure_has_field(struc, "format"))
            {
                if (!correct_format(gst_structure_get_string(struc, "format")))
                {
                    return false;
                }
            }
            else
            {
                // since raw can be anything
                // do not assume it is gray but color
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    return true;
}

bool tcam_gst_is_fourcc_bayer (const unsigned int fourcc)
{
    if (fourcc == GST_MAKE_FOURCC('g', 'b', 'r', 'g')
        || fourcc == GST_MAKE_FOURCC('g', 'r', 'b', 'g')
        || fourcc == GST_MAKE_FOURCC('r', 'g', 'g', 'b')
        || fourcc == GST_MAKE_FOURCC('b', 'g', 'g', 'r'))
    {
        return TRUE;
    }
    return FALSE;
}

bool tcam_gst_is_fourcc_rgb (const unsigned int fourcc)
{
    if (fourcc == GST_MAKE_FOURCC('R', 'G', 'B', 'x')
        || fourcc == GST_MAKE_FOURCC('x', 'R', 'G', 'B')
        || fourcc == GST_MAKE_FOURCC('B', 'G', 'R', 'x')
        || fourcc == GST_MAKE_FOURCC('x', 'B', 'G', 'R')
        || fourcc == GST_MAKE_FOURCC('R', 'G', 'B', 'A')
        || fourcc == GST_MAKE_FOURCC('A', 'R', 'G', 'B')
        || fourcc == GST_MAKE_FOURCC('B', 'G', 'R', 'A')
        || fourcc == GST_MAKE_FOURCC('A', 'B', 'G', 'R'))
    {
        return TRUE;
    }

    return FALSE;
}

bool tcam_gst_fixate_caps (GstCaps* caps)
{
    if (caps == nullptr || gst_caps_is_empty(caps))
    {
        return FALSE;
    }

    GstStructure* structure = gst_caps_get_structure(caps, 0);

    if (gst_structure_has_field(structure, "width"))
    {
        gst_structure_fixate_field_nearest_int(structure, "width", G_MAXINT);
    }
    if (gst_structure_has_field(structure, "height"))
    {
        gst_structure_fixate_field_nearest_int(structure, "height", G_MAXINT);
    }
    if (gst_structure_has_field(structure, "framerate"))
    {
        gst_structure_fixate_field_nearest_fraction(structure, "framerate", G_MAXINT, 1);
    }

    return TRUE;
}

void gst_caps_change_name (GstCaps* caps, const char* name)
{
    for (unsigned int i = 0; i < gst_caps_get_size(caps); ++i)
    {
        GstStructure* struc = gst_caps_get_structure(caps, i);

        if (struc != nullptr)
        {
            gst_structure_set_name(struc, name);
            gst_structure_remove_field(struc, "format");
        }
    }
}

GstCaps* bayer_transform_intersect (const GstCaps* bayer, const GstCaps* raw)
{
    GstCaps* caps2 = gst_caps_copy(raw);
    gst_caps_change_name(caps2, "video/x-bayer");
    GstCaps* caps1 = gst_caps_intersect((GstCaps*)bayer, caps2);
    return caps1;
}

bool gst_caps_are_bayer_only (const GstCaps* caps)
{
    for (unsigned int i = 0; i < gst_caps_get_size(caps); ++i)
    {
        GstStructure* struc = gst_caps_get_structure(caps, i);

        if (strcmp(gst_structure_get_name(struc), "video/x-bayer") != 0)
        {
            return false;
        }
    }
    return true;
}

GstCaps* tcam_gst_find_largest_caps (const GstCaps* incoming)
{
    int largest_index = -1;
    int largest_width = -1;
    int largest_height = -1;

    for (int i = 0; i < gst_caps_get_size(incoming); ++i)
    {
        GstStructure* struc = gst_caps_get_structure(incoming, i);

        int width = -1;
        int height = -1;
        bool new_width = false;
        bool new_height = false;

        // will fail if width is a range so we only handle
        // halfway fixated caps
        if (gst_structure_get_int(struc, "width", &width))
        {
            if (largest_width < width)
            {
                largest_width = width;
                new_width = true;
            }
        }

        if (gst_structure_get_int(struc, "height", &height))
        {
            if (largest_height < height)
            {
                largest_height = height;
                new_height = true;
            }
        }

        if (new_width && new_height)
        {
            largest_index = i;
        }
    }

    GstCaps* largest_caps = gst_caps_copy_nth(incoming, largest_index);

    if (!tcam_gst_fixate_caps(largest_caps))
    {
        GST_ERROR("Cannot fixate largest caps. Returning NULL");
        return nullptr;
    }

    GstStructure* s = gst_caps_get_structure(largest_caps, 0);

    int h;
    gst_structure_get_int(s, "height", &h);
    int w;
    gst_structure_get_int(s, "width", &w);

    int num;
    int den;
    gst_structure_get_fraction(s, "framerate", &num, &den);

    GValue vh = {};

    g_value_init(&vh, G_TYPE_INT);
    g_value_set_int(&vh, h);

    gst_caps_set_value(largest_caps, "height", &vh);
    largest_caps = gst_caps_new_simple (gst_structure_get_name(s),
                                        "framerate", GST_TYPE_FRACTION, num, den,
                                        "width", G_TYPE_INT, w,
                                        "height", G_TYPE_INT, h,
                                        NULL);

    return largest_caps;
}

bool contains_bayer (const GstCaps* caps)
{
    for (unsigned int i = 0; i < gst_caps_get_size(caps); ++i)
    {
        if (strcmp("video/x-bayer", gst_structure_get_name(gst_caps_get_structure(caps, i))) == 0)
        {
            return true;
        }
    }

    return false;
}

GstCaps* get_caps_from_element (const char* elementname, const char* padname)
{

    GstElement* ele = gst_element_factory_make(elementname, "tmp-element");

    if (!ele)
    {
        return nullptr;
    }

    GstCaps* ret = gst_pad_query_caps(gst_element_get_static_pad(ele, padname), NULL);

    gst_object_unref(ele);

    return ret;
}

GstCaps* find_input_caps (const GstCaps* available_caps,
                          const GstCaps* wanted_caps,
                          bool& requires_conversion)
{
    GstCaps* ret = nullptr;
    requires_conversion = false;

    if (!GST_IS_CAPS(available_caps))
    {
        return nullptr;
    }

    if (wanted_caps == nullptr || gst_caps_is_empty(wanted_caps))
    {
        GST_INFO("No sink caps specified. Continuing with caps from source device.");
        wanted_caps = gst_caps_copy(available_caps);
    }

    GstCaps* intersect = gst_caps_intersect(available_caps, wanted_caps);

    if (gst_caps_is_empty(intersect))
    {
        GstElementFactory* debayer = gst_element_factory_find("bayer2rgb");

        if (gst_element_factory_can_src_any_caps(debayer, wanted_caps)
            && gst_element_factory_can_sink_any_caps(debayer, available_caps))
        {
            requires_conversion = true;
            GstCaps* temp = get_caps_from_element("bayer2rgb", "sink");

            ret = gst_caps_intersect(available_caps, temp);

            gst_caps_unref(temp);
        }

        gst_object_unref(debayer);
    }
    else
    {
        return intersect;
    }

    return ret;
}
<|endoftext|>


<commit_before>#include "lidaradapter.h"
#include "ui_lidaradapter.h"
#include <QPainter>
#include <QCheckBox>
#include <cmath>
#include <QDebug>

LidarAdapter::LidarAdapter(IGVC::Sensors::Lidar *lidar, QWidget *parent) :
    QWidget(parent),
    ui(new Ui::LidarAdapter),
    LOnLidarData(this)
{
    ui->setupUi(this);
    NUMPTS = 1024;

    isFit = false;

    _lidar = lidar;
    if(_lidar != nullptr)
        _lidar->onNewData += &LOnLidarData;
}

LidarAdapter::~LidarAdapter()
{
    if(_lidar != nullptr)
        _lidar->onNewData -= &LOnLidarData;
    delete ui;
}

void LidarAdapter::paintEvent(QPaintEvent *)
{
    int range;
    if (!isFit)
        range = ui->btn_slider->value() + 1;
    else
        range = fitRange;
        // 0 to use longest distance in given data for ellipse bound
        // otherwise, ellipse bound is currently selected RANGES value (determined by btn_slider)

    QPainter p(this);

    /*
     *  first constrain drawing region to largest possible circle
     */

    double wid = size().width();    // speed up data access
    double hei = size().height() - 1;

    double centerX = wid * .5;      // center of widget
    double centerY = hei * .5;

    // constrain graphics to square size
    double minDimension = wid;      // smallest of width and height
    double minOffset = hei - wid;   // difference in sizes
    bool minIsWidth = true;         // TRUE if width is smallest; false if height is smallest
    if (hei < minDimension)
    {
        minDimension = hei;
        minOffset = wid - hei;
        minIsWidth = false;
    }

    // draw limit ellipse
    p.setPen(QPen(QColor(0, 0, 0, 128)));
    p.drawEllipse((minIsWidth ? 0 : minOffset * .5),
                   (minIsWidth ? minOffset * .5 : 0),
                   wid - (minIsWidth ? 0 : minOffset),
                   hei - (minIsWidth ? minOffset: 0));

    ui->tf_range->setText(QString("Range: %1").arg(range / 1000.0)); // mm to m

    /*
     *  now draw lidar rays using relative distances;
     *  furthest distance is largest line and all other lines use a % of the longest
     */

    p.setPen(QPen(QColor(0, 0, 0, 32)));
    double mag;         // % of longest line

    for(IGVC::Sensors::LidarPoint point : _data.points)
    {
        if(point.valid)
        {
            mag = point.distance / range;
            if(mag > 1)
                mag = 1;
            p.drawLine(centerX, centerY,
                       centerX + cos(point.angle) * mag * minDimension * .5,
                       centerY - sin(point.angle) * mag * minDimension * .5);
        }
    }
}

void LidarAdapter::OnLidarData(IGVC::Sensors::LidarState state)
{
    _data = state;
}

// TODO : Maybe add input textfield for range

void LidarAdapter::on_btn_fit_clicked()
{
    double maxSize = 0;
    for(IGVC::Sensors::LidarPoint point : _data.points)
        if(point.valid)
            if(point.distance > maxSize)
                maxSize = point.distance;

    fitRange = maxSize * 1000.0; // m to mm
    isFit = true;
    ui->btn_slider->setSliderPosition(fitRange);
    update();
}

void LidarAdapter::on_btn_slider_actionTriggered(int)
{
    isFit = false;
    update();
}
<commit_msg>Fixes logic in LidarAdapter drawing code.<commit_after>#include "lidaradapter.h"
#include "ui_lidaradapter.h"
#include <QPainter>
#include <QCheckBox>
#include <cmath>
#include <QDebug>

LidarAdapter::LidarAdapter(IGVC::Sensors::Lidar *lidar, QWidget *parent) :
    QWidget(parent),
    ui(new Ui::LidarAdapter),
    LOnLidarData(this)
{
    ui->setupUi(this);
    NUMPTS = 1024;

    isFit = false;

    _lidar = lidar;
    if(_lidar != nullptr)
        _lidar->onNewData += &LOnLidarData;
}

LidarAdapter::~LidarAdapter()
{
    if(_lidar != nullptr)
        _lidar->onNewData -= &LOnLidarData;
    delete ui;
}

void LidarAdapter::paintEvent(QPaintEvent *)
{
    int range;
    if (!isFit)
        range = ui->btn_slider->value() + 1;
    else
        range = fitRange;
        // 0 to use longest distance in given data for ellipse bound
        // otherwise, ellipse bound is currently selected RANGES value (determined by btn_slider)

    QPainter p(this);

    /*
     *  first constrain drawing region to largest possible circle
     */

    double wid = size().width();    // speed up data access
    double hei = size().height() - 1;

    double centerX = wid * .5;      // center of widget
    double centerY = hei * .5;

    // constrain graphics to square size
    double minDimension = wid;      // smallest of width and height
    double minOffset = hei - wid;   // difference in sizes
    bool minIsWidth = true;         // TRUE if width is smallest; false if height is smallest
    if (hei < minDimension)
    {
        minDimension = hei;
        minOffset = wid - hei;
        minIsWidth = false;
    }

    // draw limit ellipse
    p.setPen(QPen(QColor(0, 0, 0, 128)));
    p.drawEllipse((minIsWidth ? 0 : minOffset * .5),
                   (minIsWidth ? minOffset * .5 : 0),
                   wid - (minIsWidth ? 0 : minOffset),
                   hei - (minIsWidth ? minOffset: 0));

    ui->tf_range->setText(QString("Range: %1").arg(range / 1000.0)); // mm to m

    /*
     *  now draw lidar rays using relative distances;
     *  furthest distance is largest line and all other lines use a % of the longest
     */

    p.setPen(QPen(QColor(0, 0, 0, 32)));
    double mag;         // % of longest line

    for(IGVC::Sensors::LidarPoint point : _data.points)
    {
        if(point.valid)
        {
            mag = point.distance / ( range / 1000.0 );
            if(mag > 1)
                mag = 1;
            p.drawLine(centerX, centerY,
                       centerX + cos(point.angle) * mag * minDimension * .5,
                       centerY - sin(point.angle) * mag * minDimension * .5);
        }
    }
}

void LidarAdapter::OnLidarData(IGVC::Sensors::LidarState state)
{
    _data = state;
}

// TODO : Maybe add input textfield for range

void LidarAdapter::on_btn_fit_clicked()
{
    double maxSize = 0;
    for(IGVC::Sensors::LidarPoint point : _data.points)
        if(point.valid)
            if(point.distance > maxSize)
                maxSize = point.distance;

    fitRange = maxSize * 1000.0; // m to mm
    isFit = true;
    ui->btn_slider->setSliderPosition(fitRange);
    update();
}

void LidarAdapter::on_btn_slider_actionTriggered(int)
{
    isFit = false;
    update();
}
<|endoftext|>


<commit_before>#include <Poco/Clock.h>
#include <Poco/Exception.h>
#include <Poco/DateTimeFormatter.h>
#include <Poco/Logger.h>

#include "di/Injectable.h"
#include "gws/GatewayScanController.h"
#include "util/LambdaTimerTask.h"

BEEEON_OBJECT_BEGIN(BeeeOn, GatewayScanController)
BEEEON_OBJECT_CASTABLE(StoppableLoop)
BEEEON_OBJECT_PROPERTY("gatewayRPC", &GatewayScanController::setGatewayRPC)
BEEEON_OBJECT_PROPERTY("defaultDuration", &GatewayScanController::setDefaultDuration)
BEEEON_OBJECT_PROPERTY("cleanupInterval", &GatewayScanController::setCleanupInterval)
BEEEON_OBJECT_PROPERTY("cleanupOlderThan", &GatewayScanController::setCleanupOlderThan)
BEEEON_OBJECT_END(BeeeOn, GatewayScanController)

using namespace std;
using namespace Poco;
using namespace BeeeOn;

const static Timespan MAX_SCAN_DURATION = 10 * Timespan::MINUTES;

GatewayScanController::ScanHandler::ScanHandler(const GatewayID &id):
	m_id(id)
{
}

const GatewayScan &GatewayScanController::ScanHandler::scan() const
{
	ScopedLock guard(const_cast<ScanHandler&>(*this));
	return m_scan;
}

GatewayScan &GatewayScanController::ScanHandler::scan()
{
	ScopedLock guard(const_cast<ScanHandler&>(*this));
	return m_scan;
}

void GatewayScanController::ScanHandler::onAny(GatewayRPCResult::Ptr r)
{
	Logger &logger = Loggable::forClass(typeid(GatewayScanController));
	ScanHandler::ScopedLock guard(*this);

	switch (r->status()) {
	case GatewayRPCResult::Status::PENDING:
	case GatewayRPCResult::Status::ACCEPTED:
		logger.information("scanning " + m_id.toString() + " is starting",
				__FILE__, __LINE__);
		m_scan.changeState(GatewayScan::SCAN_PROCESSING);
		break;

	case GatewayRPCResult::Status::SUCCESS:
		logger.information("scanning " + m_id.toString() + " in progress",
				__FILE__, __LINE__);
		m_scan.changeState(GatewayScan::SCAN_PROCESSING);
		break;

	case GatewayRPCResult::Status::FAILED:
		logger.error("scanning " + m_id.toString() + " has failed",
				__FILE__, __LINE__);

		m_scan.changeState(GatewayScan::SCAN_FAILED);
		break;

	case GatewayRPCResult::Status::TIMEOUT:
	case GatewayRPCResult::Status::NOT_CONNECTED:
		logger.error("scanning " + m_id.toString() + " did not start on time",
				__FILE__, __LINE__);

		m_scan.changeState(GatewayScan::SCAN_FAILED);
		break;
	}
}

GatewayScanController::CleanUpTask::CleanUpTask(
		FastMutex &lock,
		GatewayScanController::ScanMap &scanMap):
	m_olderThen(30 * Timespan::MINUTES),
	m_lock(lock),
	m_scanMap(scanMap)
{
}

void GatewayScanController::CleanUpTask::setOlderThan(
		const Poco::Timespan &olderThen)
{
	if (olderThen < 0)
		throw InvalidArgumentException("cleanup older-than timeout must be positive");

	m_olderThen = olderThen;
}

void GatewayScanController::CleanUpTask::run()
{
	FastMutex::ScopedLock guard(m_lock);
	const Clock started;
	const size_t total = m_scanMap.size();
	size_t count = 0;

	logger().information("cleaning up scan records ("
			+ to_string(total) + ")",
			__FILE__, __LINE__);

	for (auto it = m_scanMap.begin(); it != m_scanMap.end();) {
		ScanHandler::Ptr context = it->second;
		ScanHandler::ScopedLock guard(*context);

		if (logger().debug()) {
			logger().debug("cleaning up scan record for "
					+ it->first.toString(),
					__FILE__, __LINE__);
		}

		const auto &started = context->scan().started();
		const auto &duration = context->scan().duration();
		const Timestamp now;

		switch (context->scan().state()) {
		case GatewayScan::SCAN_IDLE:
			it = m_scanMap.erase(it);
			count += 1;
			break;

		case GatewayScan::SCAN_FINISHED:
		case GatewayScan::SCAN_FAILED:
			if (started + duration < now - m_olderThen) {
				it = m_scanMap.erase(it);
				count += 1;
			}
			else {
				++it;
			}

			break;

		case GatewayScan::SCAN_WAITING:
		case GatewayScan::SCAN_PROCESSING:
			if (started + duration < now) {
				logger().warning("scan record for " + it->first.toString()
						+ " was not stopped on time, marking as failed",
						__FILE__, __LINE__);
				context->scan().changeState(GatewayScan::SCAN_FAILED);
			}

			++it;
			break;

		default:
			++it;
		}
	}

	logger().notice("cleaned up "
			+ to_string(count) + "/" + to_string(total)
			+ " scan records in "
			+ to_string(started.elapsed()) + "us",
			__FILE__, __LINE__);
}

GatewayScanController::GatewayScanController():
	m_defaultDuration(2 * Timespan::MINUTES),
	m_cleanupInterval(15 * Timespan::MINUTES),
	m_cleanupTask(new CleanUpTask(m_lock, m_scanMap))
{
}

void GatewayScanController::setDefaultDuration(const Poco::Timespan &duration)
{
	if (duration.totalSeconds() <= 0)
		throw InvalidArgumentException("duration must be at least a second");

	if (duration > MAX_SCAN_DURATION)
		throw InvalidArgumentException("too long default duration");

	m_defaultDuration = duration;
}

void GatewayScanController::setCleanupInterval(const Timespan &interval)
{
	if (interval.totalMilliseconds() <= 0)
		throw InvalidArgumentException("cleanup interval must be at least a millisecond");

	m_cleanupInterval = interval;
}

void GatewayScanController::setCleanupOlderThan(const Timespan &time)
{
	m_cleanupTask->setOlderThan(time);
}

void GatewayScanController::setGatewayRPC(GatewayRPC::Ptr rpc)
{
	m_rpc = rpc;
}

void GatewayScanController::start()
{
	logger().information("starting gateway scan controller");

	m_timer.scheduleAtFixedRate(
		m_cleanupTask,
		m_cleanupInterval.totalMilliseconds(),
		m_cleanupInterval.totalMilliseconds());
}

void GatewayScanController::stop()
{
	logger().information("stopping gateway scan controller");

	m_timer.cancel(true);

	FastMutex::ScopedLock guard(m_lock);
	m_scanMap.clear();
}

GatewayScan GatewayScanController::scan(const Gateway &gateway, const Timespan &timeout)
{
	ScopedLockWithUnlock<FastMutex> guard(m_lock);

	auto it = m_scanMap.find(gateway.id());
	if (it != m_scanMap.end())
		return it->second->scan();

	const auto id = gateway.id();
	const Timespan &duration = timeout <= 0? m_defaultDuration : timeout;

	if (duration > MAX_SCAN_DURATION)
		throw InvalidArgumentException("too big scanning timeout given");

	ScanHandler::Ptr context = new ScanHandler(id);
	context->scan().setDuration(duration);

	m_scanMap.emplace(id, context);

	guard.unlock();

	const Clock start;

	m_rpc->sendListen(
		context,
		gateway,
		duration
	);

	m_timer.schedule(new LambdaTimerTask([start, id, context]() mutable
		{
			Logger &logger = Loggable::forClass(typeid(GatewayScanController));

			ScanHandler::ScopedLock guard(*context);
			GatewayScan &scan = context->scan();

			const Timespan &duration = scan.started().elapsed();

			switch (scan.state()) {
			case GatewayScan::SCAN_FINISHED:
				logger.warning("scanning of "
						+ id.toString()
						+ " is already finished",
						__FILE__, __LINE__);
				break;

			case GatewayScan::SCAN_WAITING:
			case GatewayScan::SCAN_PROCESSING:
				logger.information("scanning of "
						+ id.toString()
						+ " was finished after "
						+ DateTimeFormatter::format(duration),
						__FILE__, __LINE__);

				scan.changeState(GatewayScan::SCAN_FINISHED);
				break;

			default:
				if (logger.debug()) {
					logger.debug("skipping explicit finish of "
							+ id.toString() + " scan",
							__FILE__, __LINE__);
				}
				break;
			}
		}),
		start + duration.totalMicroseconds());

	return context->scan();
}

GatewayScan GatewayScanController::find(const Gateway &gateway)
{
	FastMutex::ScopedLock guard(m_lock);

	auto it = m_scanMap.find(gateway.id());
	if (it != m_scanMap.end())
		return it->second->scan();

	return GatewayScan::createIdle();
}
<commit_msg>GatewayScanController::ScanHandler: distuingish between timeout and not-connected<commit_after>#include <Poco/Clock.h>
#include <Poco/Exception.h>
#include <Poco/DateTimeFormatter.h>
#include <Poco/Logger.h>

#include "di/Injectable.h"
#include "gws/GatewayScanController.h"
#include "util/LambdaTimerTask.h"

BEEEON_OBJECT_BEGIN(BeeeOn, GatewayScanController)
BEEEON_OBJECT_CASTABLE(StoppableLoop)
BEEEON_OBJECT_PROPERTY("gatewayRPC", &GatewayScanController::setGatewayRPC)
BEEEON_OBJECT_PROPERTY("defaultDuration", &GatewayScanController::setDefaultDuration)
BEEEON_OBJECT_PROPERTY("cleanupInterval", &GatewayScanController::setCleanupInterval)
BEEEON_OBJECT_PROPERTY("cleanupOlderThan", &GatewayScanController::setCleanupOlderThan)
BEEEON_OBJECT_END(BeeeOn, GatewayScanController)

using namespace std;
using namespace Poco;
using namespace BeeeOn;

const static Timespan MAX_SCAN_DURATION = 10 * Timespan::MINUTES;

GatewayScanController::ScanHandler::ScanHandler(const GatewayID &id):
	m_id(id)
{
}

const GatewayScan &GatewayScanController::ScanHandler::scan() const
{
	ScopedLock guard(const_cast<ScanHandler&>(*this));
	return m_scan;
}

GatewayScan &GatewayScanController::ScanHandler::scan()
{
	ScopedLock guard(const_cast<ScanHandler&>(*this));
	return m_scan;
}

void GatewayScanController::ScanHandler::onAny(GatewayRPCResult::Ptr r)
{
	Logger &logger = Loggable::forClass(typeid(GatewayScanController));
	ScanHandler::ScopedLock guard(*this);

	switch (r->status()) {
	case GatewayRPCResult::Status::PENDING:
	case GatewayRPCResult::Status::ACCEPTED:
		logger.information("scanning " + m_id.toString() + " is starting",
				__FILE__, __LINE__);
		m_scan.changeState(GatewayScan::SCAN_PROCESSING);
		break;

	case GatewayRPCResult::Status::SUCCESS:
		logger.information("scanning " + m_id.toString() + " in progress",
				__FILE__, __LINE__);
		m_scan.changeState(GatewayScan::SCAN_PROCESSING);
		break;

	case GatewayRPCResult::Status::FAILED:
		logger.error("scanning " + m_id.toString() + " has failed",
				__FILE__, __LINE__);

		m_scan.changeState(GatewayScan::SCAN_FAILED);
		break;

	case GatewayRPCResult::Status::TIMEOUT:
		logger.error("scanning " + m_id.toString() + " did not start on time",
				__FILE__, __LINE__);

		m_scan.changeState(GatewayScan::SCAN_FAILED);
		break;

	case GatewayRPCResult::Status::NOT_CONNECTED:
		logger.error("scanning " + m_id.toString() + " failed, not connected",
				__FILE__, __LINE__);

		m_scan.changeState(GatewayScan::SCAN_FAILED);
		break;
	}
}

GatewayScanController::CleanUpTask::CleanUpTask(
		FastMutex &lock,
		GatewayScanController::ScanMap &scanMap):
	m_olderThen(30 * Timespan::MINUTES),
	m_lock(lock),
	m_scanMap(scanMap)
{
}

void GatewayScanController::CleanUpTask::setOlderThan(
		const Poco::Timespan &olderThen)
{
	if (olderThen < 0)
		throw InvalidArgumentException("cleanup older-than timeout must be positive");

	m_olderThen = olderThen;
}

void GatewayScanController::CleanUpTask::run()
{
	FastMutex::ScopedLock guard(m_lock);
	const Clock started;
	const size_t total = m_scanMap.size();
	size_t count = 0;

	logger().information("cleaning up scan records ("
			+ to_string(total) + ")",
			__FILE__, __LINE__);

	for (auto it = m_scanMap.begin(); it != m_scanMap.end();) {
		ScanHandler::Ptr context = it->second;
		ScanHandler::ScopedLock guard(*context);

		if (logger().debug()) {
			logger().debug("cleaning up scan record for "
					+ it->first.toString(),
					__FILE__, __LINE__);
		}

		const auto &started = context->scan().started();
		const auto &duration = context->scan().duration();
		const Timestamp now;

		switch (context->scan().state()) {
		case GatewayScan::SCAN_IDLE:
			it = m_scanMap.erase(it);
			count += 1;
			break;

		case GatewayScan::SCAN_FINISHED:
		case GatewayScan::SCAN_FAILED:
			if (started + duration < now - m_olderThen) {
				it = m_scanMap.erase(it);
				count += 1;
			}
			else {
				++it;
			}

			break;

		case GatewayScan::SCAN_WAITING:
		case GatewayScan::SCAN_PROCESSING:
			if (started + duration < now) {
				logger().warning("scan record for " + it->first.toString()
						+ " was not stopped on time, marking as failed",
						__FILE__, __LINE__);
				context->scan().changeState(GatewayScan::SCAN_FAILED);
			}

			++it;
			break;

		default:
			++it;
		}
	}

	logger().notice("cleaned up "
			+ to_string(count) + "/" + to_string(total)
			+ " scan records in "
			+ to_string(started.elapsed()) + "us",
			__FILE__, __LINE__);
}

GatewayScanController::GatewayScanController():
	m_defaultDuration(2 * Timespan::MINUTES),
	m_cleanupInterval(15 * Timespan::MINUTES),
	m_cleanupTask(new CleanUpTask(m_lock, m_scanMap))
{
}

void GatewayScanController::setDefaultDuration(const Poco::Timespan &duration)
{
	if (duration.totalSeconds() <= 0)
		throw InvalidArgumentException("duration must be at least a second");

	if (duration > MAX_SCAN_DURATION)
		throw InvalidArgumentException("too long default duration");

	m_defaultDuration = duration;
}

void GatewayScanController::setCleanupInterval(const Timespan &interval)
{
	if (interval.totalMilliseconds() <= 0)
		throw InvalidArgumentException("cleanup interval must be at least a millisecond");

	m_cleanupInterval = interval;
}

void GatewayScanController::setCleanupOlderThan(const Timespan &time)
{
	m_cleanupTask->setOlderThan(time);
}

void GatewayScanController::setGatewayRPC(GatewayRPC::Ptr rpc)
{
	m_rpc = rpc;
}

void GatewayScanController::start()
{
	logger().information("starting gateway scan controller");

	m_timer.scheduleAtFixedRate(
		m_cleanupTask,
		m_cleanupInterval.totalMilliseconds(),
		m_cleanupInterval.totalMilliseconds());
}

void GatewayScanController::stop()
{
	logger().information("stopping gateway scan controller");

	m_timer.cancel(true);

	FastMutex::ScopedLock guard(m_lock);
	m_scanMap.clear();
}

GatewayScan GatewayScanController::scan(const Gateway &gateway, const Timespan &timeout)
{
	ScopedLockWithUnlock<FastMutex> guard(m_lock);

	auto it = m_scanMap.find(gateway.id());
	if (it != m_scanMap.end())
		return it->second->scan();

	const auto id = gateway.id();
	const Timespan &duration = timeout <= 0? m_defaultDuration : timeout;

	if (duration > MAX_SCAN_DURATION)
		throw InvalidArgumentException("too big scanning timeout given");

	ScanHandler::Ptr context = new ScanHandler(id);
	context->scan().setDuration(duration);

	m_scanMap.emplace(id, context);

	guard.unlock();

	const Clock start;

	m_rpc->sendListen(
		context,
		gateway,
		duration
	);

	m_timer.schedule(new LambdaTimerTask([start, id, context]() mutable
		{
			Logger &logger = Loggable::forClass(typeid(GatewayScanController));

			ScanHandler::ScopedLock guard(*context);
			GatewayScan &scan = context->scan();

			const Timespan &duration = scan.started().elapsed();

			switch (scan.state()) {
			case GatewayScan::SCAN_FINISHED:
				logger.warning("scanning of "
						+ id.toString()
						+ " is already finished",
						__FILE__, __LINE__);
				break;

			case GatewayScan::SCAN_WAITING:
			case GatewayScan::SCAN_PROCESSING:
				logger.information("scanning of "
						+ id.toString()
						+ " was finished after "
						+ DateTimeFormatter::format(duration),
						__FILE__, __LINE__);

				scan.changeState(GatewayScan::SCAN_FINISHED);
				break;

			default:
				if (logger.debug()) {
					logger.debug("skipping explicit finish of "
							+ id.toString() + " scan",
							__FILE__, __LINE__);
				}
				break;
			}
		}),
		start + duration.totalMicroseconds());

	return context->scan();
}

GatewayScan GatewayScanController::find(const Gateway &gateway)
{
	FastMutex::ScopedLock guard(m_lock);

	auto it = m_scanMap.find(gateway.id());
	if (it != m_scanMap.end())
		return it->second->scan();

	return GatewayScan::createIdle();
}
<|endoftext|>


<commit_before>#include "../ClipperUtils.hpp"
#include "../ShortestPath.hpp"
#include "../Surface.hpp"
#include <cmath>
#include <algorithm>
#include <iostream>

#include "FillGyroid.hpp"

namespace Slic3r {

static inline double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
{
    if (vertical) {
        double phase_offset = (z_cos < 0 ? M_PI : 0) + M_PI;
        double a   = sin(x + phase_offset);
        double b   = - z_cos;
        double res = z_sin * cos(x + phase_offset + (flip ? M_PI : 0.));
        double r   = sqrt(sqr(a) + sqr(b));
        return asin(a/r) + asin(res/r) + M_PI;
    }
    else {
        double phase_offset = z_sin < 0 ? M_PI : 0.;
        double a   = cos(x + phase_offset);
        double b   = - z_sin;
        double res = z_cos * sin(x + phase_offset + (flip ? 0 : M_PI));
        double r   = sqrt(sqr(a) + sqr(b));
        return (asin(a/r) + asin(res/r) + 0.5 * M_PI);
    }
}

static inline Polyline make_wave(
    const std::vector<Vec2d>& one_period, double width, double height, double offset, double scaleFactor,
    double z_cos, double z_sin, bool vertical, bool flip)
{
    std::vector<Vec2d> points = one_period;
    double period = points.back()(0);
    if (width != period) // do not extend if already truncated
    {
        points.reserve(one_period.size() * size_t(floor(width / period)));
        points.pop_back();

        size_t n = points.size();
        do {
            points.emplace_back(points[points.size()-n].x() + period, points[points.size()-n].y());
        } while (points.back()(0) < width - EPSILON);

        points.emplace_back(Vec2d(width, f(width, z_sin, z_cos, vertical, flip)));
    }

    // and construct the final polyline to return:
    Polyline polyline;
    polyline.points.reserve(points.size());
    for (auto& point : points) {
        point(1) += offset;
        point(1) = clamp(0., height, double(point(1)));
        if (vertical)
            std::swap(point(0), point(1));
        polyline.points.emplace_back((point * scaleFactor).cast<coord_t>());
    }

    return polyline;
}

static std::vector<Vec2d> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip, double tolerance)
{
    std::vector<Vec2d> points;
    double dx = M_PI_2; // exact coordinates on main inflexion lobes
    double limit = std::min(2*M_PI, width);
    points.reserve(coord_t(ceil(limit / tolerance / 3)));

    for (double x = 0.; x < limit - EPSILON; x += dx) {
        points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
    }
    points.emplace_back(Vec2d(limit, f(limit, z_sin, z_cos, vertical, flip)));

    // piecewise increase in resolution up to requested tolerance
    for(;;)
    {
        size_t size = points.size();
        for (unsigned int i = 1;i < size; ++i) {
            auto& lp = points[i-1]; // left point
            auto& rp = points[i];   // right point
            double x = lp(0) + (rp(0) - lp(0)) / 2;
            double y = f(x, z_sin, z_cos, vertical, flip);
            Vec2d ip = {x, y};
            if (std::abs(cross2(Vec2d(ip - lp), Vec2d(ip - rp))) > sqr(tolerance)) {
                points.emplace_back(std::move(ip));
            }
        }

        if (size == points.size())
            break;
        else
        {
            // insert new points in order
            std::sort(points.begin(), points.end(),
                      [](const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0); });
        }
    }

    return points;
}

static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height)
{
    const double scaleFactor = scale_(line_spacing) / density_adjusted;

    // tolerance in scaled units. clamp the maximum tolerance as there's
    // no processing-speed benefit to do so beyond a certain point
    const double tolerance = std::min(line_spacing / 2, FillGyroid::PatternTolerance) / unscale<double>(scaleFactor);

    //scale factor for 5% : 8 712 388
    // 1z = 10^-6 mm ?
    const double z     = gridZ / scaleFactor;
    const double z_sin = sin(z);
    const double z_cos = cos(z);

    bool vertical = (std::abs(z_sin) <= std::abs(z_cos));
    double lower_bound = 0.;
    double upper_bound = height;
    bool flip = true;
    if (vertical) {
        flip = false;
        lower_bound = -M_PI;
        upper_bound = width - M_PI_2;
        std::swap(width,height);
    }

    std::vector<Vec2d> one_period_odd = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance); // creates one period of the waves, so it doesn't have to be recalculated all the time
    flip = !flip;                                                                   // even polylines are a bit shifted
    std::vector<Vec2d> one_period_even = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance);
    Polylines result;

    for (double y0 = lower_bound; y0 < upper_bound + EPSILON; y0 += M_PI) {
        // creates odd polylines
        result.emplace_back(make_wave(one_period_odd, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
        // creates even polylines
        y0 += M_PI;
        if (y0 < upper_bound + EPSILON) {
            result.emplace_back(make_wave(one_period_even, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
        }
    }

    return result;
}

// FIXME: needed to fix build on Mac on buildserver
constexpr double FillGyroid::PatternTolerance;

void FillGyroid::_fill_surface_single(
    const FillParams                &params,
    unsigned int                     thickness_layers,
    const std::pair<float, Point>   &direction,
    ExPolygon                        expolygon,
    Polylines                       &polylines_out)
{
    auto infill_angle = float(this->angle + (CorrectionAngle * 2*M_PI) / 360.);
    if(std::abs(infill_angle) >= EPSILON)
        expolygon.rotate(-infill_angle);

    BoundingBox bb = expolygon.contour.bounding_box();
    // Density adjusted to have a good %of weight.
    double      density_adjusted = std::max(0., params.density * DensityAdjust);
    // Distance between the gyroid waves in scaled coordinates.
    coord_t     distance = coord_t(scale_(this->spacing) / density_adjusted);

    // align bounding box to a multiple of our grid module
    bb.merge(_align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)));

    // generate pattern
    Polylines polylines = make_gyroid_waves(
        scale_(this->z),
        density_adjusted,
        this->spacing,
        ceil(bb.size()(0) / distance) + 1.,
        ceil(bb.size()(1) / distance) + 1.);

	// shift the polyline to the grid origin
	for (Polyline &pl : polylines)
		pl.translate(bb.min);

	polylines = intersection_pl(polylines, to_polygons(expolygon));

    if (! polylines.empty())
		// remove too small bits (larger than longer)
		polylines.erase(
			//FIXME what is the small size? Removing tiny extrusions disconnects walls!
			std::remove_if(polylines.begin(), polylines.end(), [this](const Polyline &pl) { return pl.length() < scale_(this->spacing * 3); }),
			polylines.end());

	if (! polylines.empty()) {
		// connect lines
		size_t polylines_out_first_idx = polylines_out.size();
		if (params.dont_connect())
        	append(polylines_out, chain_polylines(polylines));
        else
            this->connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params);

	    // new paths must be rotated back
        if (std::abs(infill_angle) >= EPSILON) {
	        for (auto it = polylines_out.begin() + polylines_out_first_idx; it != polylines_out.end(); ++ it)
	        	it->rotate(infill_angle);
	    }
    }
}

} // namespace Slic3r
<commit_msg>Gyroid infill: Lowered the threshold to drop infill line segments.<commit_after>#include "../ClipperUtils.hpp"
#include "../ShortestPath.hpp"
#include "../Surface.hpp"
#include <cmath>
#include <algorithm>
#include <iostream>

#include "FillGyroid.hpp"

namespace Slic3r {

static inline double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
{
    if (vertical) {
        double phase_offset = (z_cos < 0 ? M_PI : 0) + M_PI;
        double a   = sin(x + phase_offset);
        double b   = - z_cos;
        double res = z_sin * cos(x + phase_offset + (flip ? M_PI : 0.));
        double r   = sqrt(sqr(a) + sqr(b));
        return asin(a/r) + asin(res/r) + M_PI;
    }
    else {
        double phase_offset = z_sin < 0 ? M_PI : 0.;
        double a   = cos(x + phase_offset);
        double b   = - z_sin;
        double res = z_cos * sin(x + phase_offset + (flip ? 0 : M_PI));
        double r   = sqrt(sqr(a) + sqr(b));
        return (asin(a/r) + asin(res/r) + 0.5 * M_PI);
    }
}

static inline Polyline make_wave(
    const std::vector<Vec2d>& one_period, double width, double height, double offset, double scaleFactor,
    double z_cos, double z_sin, bool vertical, bool flip)
{
    std::vector<Vec2d> points = one_period;
    double period = points.back()(0);
    if (width != period) // do not extend if already truncated
    {
        points.reserve(one_period.size() * size_t(floor(width / period)));
        points.pop_back();

        size_t n = points.size();
        do {
            points.emplace_back(points[points.size()-n].x() + period, points[points.size()-n].y());
        } while (points.back()(0) < width - EPSILON);

        points.emplace_back(Vec2d(width, f(width, z_sin, z_cos, vertical, flip)));
    }

    // and construct the final polyline to return:
    Polyline polyline;
    polyline.points.reserve(points.size());
    for (auto& point : points) {
        point(1) += offset;
        point(1) = clamp(0., height, double(point(1)));
        if (vertical)
            std::swap(point(0), point(1));
        polyline.points.emplace_back((point * scaleFactor).cast<coord_t>());
    }

    return polyline;
}

static std::vector<Vec2d> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip, double tolerance)
{
    std::vector<Vec2d> points;
    double dx = M_PI_2; // exact coordinates on main inflexion lobes
    double limit = std::min(2*M_PI, width);
    points.reserve(coord_t(ceil(limit / tolerance / 3)));

    for (double x = 0.; x < limit - EPSILON; x += dx) {
        points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
    }
    points.emplace_back(Vec2d(limit, f(limit, z_sin, z_cos, vertical, flip)));

    // piecewise increase in resolution up to requested tolerance
    for(;;)
    {
        size_t size = points.size();
        for (unsigned int i = 1;i < size; ++i) {
            auto& lp = points[i-1]; // left point
            auto& rp = points[i];   // right point
            double x = lp(0) + (rp(0) - lp(0)) / 2;
            double y = f(x, z_sin, z_cos, vertical, flip);
            Vec2d ip = {x, y};
            if (std::abs(cross2(Vec2d(ip - lp), Vec2d(ip - rp))) > sqr(tolerance)) {
                points.emplace_back(std::move(ip));
            }
        }

        if (size == points.size())
            break;
        else
        {
            // insert new points in order
            std::sort(points.begin(), points.end(),
                      [](const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0); });
        }
    }

    return points;
}

static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height)
{
    const double scaleFactor = scale_(line_spacing) / density_adjusted;

    // tolerance in scaled units. clamp the maximum tolerance as there's
    // no processing-speed benefit to do so beyond a certain point
    const double tolerance = std::min(line_spacing / 2, FillGyroid::PatternTolerance) / unscale<double>(scaleFactor);

    //scale factor for 5% : 8 712 388
    // 1z = 10^-6 mm ?
    const double z     = gridZ / scaleFactor;
    const double z_sin = sin(z);
    const double z_cos = cos(z);

    bool vertical = (std::abs(z_sin) <= std::abs(z_cos));
    double lower_bound = 0.;
    double upper_bound = height;
    bool flip = true;
    if (vertical) {
        flip = false;
        lower_bound = -M_PI;
        upper_bound = width - M_PI_2;
        std::swap(width,height);
    }

    std::vector<Vec2d> one_period_odd = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance); // creates one period of the waves, so it doesn't have to be recalculated all the time
    flip = !flip;                                                                   // even polylines are a bit shifted
    std::vector<Vec2d> one_period_even = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance);
    Polylines result;

    for (double y0 = lower_bound; y0 < upper_bound + EPSILON; y0 += M_PI) {
        // creates odd polylines
        result.emplace_back(make_wave(one_period_odd, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
        // creates even polylines
        y0 += M_PI;
        if (y0 < upper_bound + EPSILON) {
            result.emplace_back(make_wave(one_period_even, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
        }
    }

    return result;
}

// FIXME: needed to fix build on Mac on buildserver
constexpr double FillGyroid::PatternTolerance;

void FillGyroid::_fill_surface_single(
    const FillParams                &params,
    unsigned int                     thickness_layers,
    const std::pair<float, Point>   &direction,
    ExPolygon                        expolygon,
    Polylines                       &polylines_out)
{
    auto infill_angle = float(this->angle + (CorrectionAngle * 2*M_PI) / 360.);
    if(std::abs(infill_angle) >= EPSILON)
        expolygon.rotate(-infill_angle);

    BoundingBox bb = expolygon.contour.bounding_box();
    // Density adjusted to have a good %of weight.
    double      density_adjusted = std::max(0., params.density * DensityAdjust);
    // Distance between the gyroid waves in scaled coordinates.
    coord_t     distance = coord_t(scale_(this->spacing) / density_adjusted);

    // align bounding box to a multiple of our grid module
    bb.merge(_align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)));

    // generate pattern
    Polylines polylines = make_gyroid_waves(
        scale_(this->z),
        density_adjusted,
        this->spacing,
        ceil(bb.size()(0) / distance) + 1.,
        ceil(bb.size()(1) / distance) + 1.);

	// shift the polyline to the grid origin
	for (Polyline &pl : polylines)
		pl.translate(bb.min);

	polylines = intersection_pl(polylines, to_polygons(expolygon));

    if (! polylines.empty()) {
		// Remove very small bits, but be careful to not remove infill lines connecting thin walls!
        // The infill perimeter lines should be separated by around a single infill line width.
        const double minlength = scale_(0.8 * this->spacing);
		polylines.erase(
			std::remove_if(polylines.begin(), polylines.end(), [minlength](const Polyline &pl) { return pl.length() < minlength; }),
			polylines.end());
    }

	if (! polylines.empty()) {
		// connect lines
		size_t polylines_out_first_idx = polylines_out.size();
		if (params.dont_connect())
        	append(polylines_out, chain_polylines(polylines));
        else
            this->connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params);

	    // new paths must be rotated back
        if (std::abs(infill_angle) >= EPSILON) {
	        for (auto it = polylines_out.begin() + polylines_out_first_idx; it != polylines_out.end(); ++ it)
	        	it->rotate(infill_angle);
	    }
    }
}

} // namespace Slic3r
<|endoftext|>


<commit_before><commit_msg>BUGFIX: expiration depth<commit_after><|endoftext|>


<commit_before><commit_msg>inherited from TNamed<commit_after><|endoftext|>


<commit_before><commit_msg>Missing include<commit_after><|endoftext|>


<commit_before><commit_msg>added "isConst" and "getType" functions to Expression classes<commit_after><|endoftext|>


<commit_before>#include <Poco/Logger.h>

#include "di/InjectorTarget.h"
#include "rpc/GatewayRPC.h"
#include "dao/DeviceDao.h"
#include "Debug.h"

using namespace Poco;

namespace BeeeOn {

/**
 * Fake RPC with no communication to Ada Server at all.
 */
class FakeGatewayRPC : public GatewayRPC, public AbstractInjectorTarget {
public:
	FakeGatewayRPC();

	void setDeviceDao(DeviceDao *dao);

	void sendListen(const Gateway &gateway) override;
	void unpairDevice(const Gateway &gateway,
			const Device &device) override;
	void pingGateway(const Gateway &gateway) override;

private:
	Logger &m_logger;
	DeviceDao *m_deviceDao;
};

FakeGatewayRPC::FakeGatewayRPC():
	m_logger(LOGGER_CLASS(this)),
	m_deviceDao(&NullDeviceDao::instance())
{
	injector<FakeGatewayRPC, DeviceDao>(
			"deviceDao",
			&FakeGatewayRPC::setDeviceDao
	);
}

void FakeGatewayRPC::setDeviceDao(DeviceDao *dao)
{
	m_deviceDao = dao? dao : &NullDeviceDao::instance();
}

void FakeGatewayRPC::sendListen(const Gateway &gateway)
{
	m_logger.warning("send listen to gateway",
			__FILE__, __LINE__);
}

void FakeGatewayRPC::unpairDevice(const Gateway &gateway,
		const Device &target)
{
	Device device(target);

	if (!m_deviceDao->fetch(device, gateway)) {
		m_logger.warning("failed to fetch device "
				+ device.id().toString(),
				__FILE__, __LINE__);
		return;
	}

	device.setActiveSince(Nullable<DateTime>());

	if (!m_deviceDao->update(device, gateway)) {
		m_logger.warning("failed to update device "
				+ device.id().toString(),
				__FILE__, __LINE__);
	}
}

void FakeGatewayRPC::pingGateway(const Gateway &gateway)
{
	m_logger.warning("ping gateway",
			__FILE__, __LINE__);
}

}

BEEEON_OBJECT(FakeGatewayRPC, BeeeOn::FakeGatewayRPC)
<commit_msg>FakeGatewayRPC: inherit Loggable<commit_after>#include <Poco/Logger.h>

#include "di/InjectorTarget.h"
#include "rpc/GatewayRPC.h"
#include "dao/DeviceDao.h"
#include "util/Loggable.h"

using namespace Poco;

namespace BeeeOn {

/**
 * Fake RPC with no communication to Ada Server at all.
 */
class FakeGatewayRPC : public GatewayRPC, public AbstractInjectorTarget,
		public Loggable {
public:
	FakeGatewayRPC();

	void setDeviceDao(DeviceDao *dao);

	void sendListen(const Gateway &gateway) override;
	void unpairDevice(const Gateway &gateway,
			const Device &device) override;
	void pingGateway(const Gateway &gateway) override;

private:
	DeviceDao *m_deviceDao;
};

FakeGatewayRPC::FakeGatewayRPC():
	m_deviceDao(&NullDeviceDao::instance())
{
	injector<FakeGatewayRPC, DeviceDao>(
			"deviceDao",
			&FakeGatewayRPC::setDeviceDao
	);
}

void FakeGatewayRPC::setDeviceDao(DeviceDao *dao)
{
	m_deviceDao = dao? dao : &NullDeviceDao::instance();
}

void FakeGatewayRPC::sendListen(const Gateway &gateway)
{
	logger().warning("send listen to gateway",
			__FILE__, __LINE__);
}

void FakeGatewayRPC::unpairDevice(const Gateway &gateway,
		const Device &target)
{
	Device device(target);

	if (!m_deviceDao->fetch(device, gateway)) {
		logger().warning("failed to fetch device "
				+ device.id().toString(),
				__FILE__, __LINE__);
		return;
	}

	device.setActiveSince(Nullable<DateTime>());

	if (!m_deviceDao->update(device, gateway)) {
		logger().warning("failed to update device "
				+ device.id().toString(),
				__FILE__, __LINE__);
	}
}

void FakeGatewayRPC::pingGateway(const Gateway &gateway)
{
	logger().warning("ping gateway",
			__FILE__, __LINE__);
}

}

BEEEON_OBJECT(FakeGatewayRPC, BeeeOn::FakeGatewayRPC)
<|endoftext|>


<commit_before><commit_msg>added batch test with random strings<commit_after><|endoftext|>


<commit_before><commit_msg>Save rtm.start result to file for debugging<commit_after><|endoftext|>


<commit_before>/*
 * SchemeSmobAS.c
 *
 * Scheme small objects (SMOBS) for atom spaces.
 *
 * Copyright (c) 2008,2009,2014 Linas Vepstas <linas@linas.org>
 */

#ifdef HAVE_GUILE

#include <cstddef>
#include <libguile.h>

#include <opencog/atomspace/AtomSpace.h>
#include <opencog/guile/SchemeSmob.h>

using namespace opencog;

// Need a lock to protect the map, since multiple threads may be trying
// to update this map.  The map contains a use-count for the number of
// threads that are currently using this atomspace as the current
// attomspace.  When the count drops to zero, the atomspace will be
// reaped if the number of SCM references also drops to zero (and the
// GC runs).
std::mutex SchemeSmob::as_mtx;
std::map<AtomSpace*, int> SchemeSmob::deleteable_as;

/* ============================================================== */

std::string SchemeSmob::as_to_string(const AtomSpace *as)
{
#define BUFLEN 120
	char buff[BUFLEN];

	snprintf(buff, BUFLEN, "#<atomspace %p>", as);
	return buff;
}

/* ============================================================== */
/**
 * Create SCM object wrapping the atomspace.
 * Do NOT take over memory management of it!
 */
SCM SchemeSmob::make_as (AtomSpace *as)
{
	SCM smob;
	SCM_NEWSMOB (smob, cog_misc_tag, as);
	SCM_SET_SMOB_FLAGS(smob, COG_AS);
	return smob;
}

/* ============================================================== */

/**
 * The only place this is called from is the guile garbage collector!
 * Its called when guile thinks it has no pointers to the given
 * atomspace; thus, we should free it. In fact, we only decrement the
 * use count for it; XXX but under what circumstance would that not be
 * zero? If its racing with another thread ?? I'm confused.
 *
 * Note that if the atomspace was given to us externally, e.g. by the
 * cogserver, then it will not even have a use-count (and thus, it's
 * never deleted here.)
 */
void SchemeSmob::release_as (AtomSpace *as)
{
	std::unique_lock<std::mutex> lck(as_mtx);
	auto has = deleteable_as.find(as);
	if (deleteable_as.end() == has) return;
	deleteable_as[as] --;

	if (0 == deleteable_as[as])
	{
		AtomSpace* env = as->get_environ();
		deleteable_as.erase(has);
		lck.unlock();
		scm_gc_unregister_collectable_memory (as,
	                  sizeof(*as), "opencog atomspace");
		delete as;

		// (Recursively) decrement the use count on the parent.
		// XXX This is confusing, I'm not sure its right.
		if (env) release_as(env);
	}
}

/* ============================================================== */
/**
 * Create a new atom space.  The parent argument might not
 * be present -- its optional.
 */
SCM SchemeSmob::ss_new_as (SCM s)
{
	AtomSpace *parent = ss_to_atomspace(s);

	AtomSpace *as = new AtomSpace(parent);

	scm_gc_register_collectable_memory (as,
	                 sizeof(*as), "opencog atomspace");

	// Only the internally-created atomspaces are trackable.
	std::lock_guard<std::mutex> lck(as_mtx);
	deleteable_as[as] = 1;

	// Don't delete the parent, as long as its in use by a child
	// (that guile still has a reference to).
	while (parent and deleteable_as.end() != deleteable_as.find(parent))
	{
		deleteable_as[parent]++;
		parent = parent->get_environ();
	}

#define WORK_AROUND_GUILE_20_GC_BUG
#ifdef WORK_AROUND_GUILE_20_GC_BUG
	// Below is a wrk-around to a bug.  You can trigger this bug
	// with the code below;  if will crash, because the initial
	// AS gets erroneously garbage-collected.  Guile is trying
	// to release the main AS every time through the loop.  I can't
	// tell why.
/******
(use-modules (opencog))
(use-modules (opencog exec))

(define  n 0)
(define (prt)
   (set! n (+ n 1))
   (format #t "yaaaa ~a ~a\n" n cog-initial-as) (usleep 200000)
   (gc)
   (format #t "post-gc ~a\n" cog-initial-as)
   (if (< n 200) (stv 1 1) (stv 0 1)))

(DefineLink
   (DefinedPredicateNode "loopy")
   (SatisfactionLink
      (SequentialAndLink
         (EvaluationLink
            (GroundedPredicateNode "scm:prt") (ListLink))
         (DefinedPredicateNode "loopy")
      )))

(cog-evaluate! (DefinedPredicateNode "loopy"))
*******/
	static bool first = true;
	if (first)
	{
		first = false;
		deleteable_as[as] = 2002001000;
	}
#endif
	return make_as(as);
}

/* ============================================================== */
/**
 * Return true if the scm is an atom space
 */
SCM SchemeSmob::ss_as_p (SCM s)
{
	if (SCM_SMOB_PREDICATE(SchemeSmob::cog_misc_tag, s))
	{
		scm_t_bits misctype = SCM_SMOB_FLAGS(s);
		switch (misctype)
		{
			case COG_AS:
				return SCM_BOOL_T;

			default:
				return SCM_BOOL_F;
		}
	}
	return SCM_BOOL_F;
}

/* ============================================================== */
/* Cast SCM to atomspace */

AtomSpace* SchemeSmob::ss_to_atomspace(SCM sas)
{
	if (not SCM_SMOB_PREDICATE(SchemeSmob::cog_misc_tag, sas))
		return nullptr;

	scm_t_bits misctype = SCM_SMOB_FLAGS(sas);
	if (COG_AS != misctype)
		return nullptr;

	AtomSpace* as = (AtomSpace *) SCM_SMOB_DATA(sas);
	scm_remember_upto_here_1(sas);
	return as;
}

/* ============================================================== */

AtomSpace* SchemeSmob::verify_atomspace(SCM sas, const char * subrname, int pos)
{
   AtomSpace* as = ss_to_atomspace(sas);
   if (nullptr == as)
      scm_wrong_type_arg_msg(subrname, pos, sas, "opencog atomspace");

   return as;
}

/* ============================================================== */
/**
 * Return UUID of the atomspace
 */
SCM SchemeSmob::ss_as_uuid(SCM sas)
{
	AtomSpace* as = ss_to_atomspace(sas);
	if (nullptr == as)
	{
		// Special care for atom whose atomspace was null
		if (scm_is_null(sas))
			return scm_from_ulong(ULONG_MAX);
		scm_wrong_type_arg_msg("cog-atomspace-uuid", 1, sas, "atomspace");
	}

	UUID uuid = as->get_uuid();
	scm_remember_upto_here_1(sas);

	return scm_from_ulong(uuid);
}

/* ============================================================== */
/**
 * Return parent of the atomspace
 */
SCM SchemeSmob::ss_as_env(SCM sas)
{
	AtomSpace* as = ss_to_atomspace(sas);
	if (nullptr == as)
	{
		// Special care for null atomspace
		if (scm_is_null(sas))
			return SCM_EOL;
		scm_wrong_type_arg_msg("cog-atomspace-env", 1, sas, "atomspace");
	}

	AtomSpace* env = as->get_environ();
	scm_remember_upto_here_1(sas);
	return make_as(env);
}

/* ============================================================== */
/**
 * Clear the atomspace
 */
SCM SchemeSmob::ss_as_clear(SCM sas)
{
	AtomSpace* as = ss_to_atomspace(sas);
	if (nullptr == as)
		scm_wrong_type_arg_msg("cog-atomspace-clear", 1, sas, "atomspace");

	as->clear();

	scm_remember_upto_here_1(sas);
	return SCM_BOOL_T;
}

/* ============================================================== */
/**
 * Return the atomspace of an atom.
 */
SCM SchemeSmob::ss_as(SCM satom)
{
	Handle h(scm_to_handle(satom));
	if (nullptr == h)
		scm_wrong_type_arg_msg("cog-as", 1, satom, "opencog atom");

	AtomSpace* as = h->getAtomSpace();
	if (nullptr == as) return SCM_EOL;

	return make_as(as);
}

/* ============================================================== */
/**
 * Return current atomspace for this dynamic state.
 */
SCM SchemeSmob::atomspace_fluid;

SCM SchemeSmob::ss_get_as (void)
{
	return scm_fluid_ref(atomspace_fluid);
}

/// The current atomspace for the current thread must not be deleted
/// under any circumstances. Thus each thread increments a use-count
/// on the atomspace (stored in deleteable_as).  Atomspaces that are
/// not current in any thread, and also have no SCM smobs pointing
/// at them may be deleted by the gc.  This will cause atoms to
/// disappear, if the user is not careful ...
///
/// Oh, wait: only atomspaces that were internally created (i.e.
/// created by a scheme call) are eligible for deletion.  We may
/// also be given atomspaces that magically appeared from the outside
/// world --- we do NOT track those for deletion.
///
void SchemeSmob::as_ref_count(SCM old_as, AtomSpace *nas)
{
	AtomSpace* oas = ss_to_atomspace(old_as);
	if (oas != nas)
	{
		std::lock_guard<std::mutex> lck(as_mtx);
		if (deleteable_as.end() != deleteable_as.find(nas))
			deleteable_as[nas]++;
		if (oas and deleteable_as.end() != deleteable_as.find(oas))
			deleteable_as[oas] --;
	}
}

/**
 * Set the current atomspace for this dynamic state.
 * Return the previous atomspace.
 */
SCM SchemeSmob::ss_set_as (SCM new_as)
{
	if (not SCM_SMOB_PREDICATE(SchemeSmob::cog_misc_tag, new_as))
		return SCM_BOOL_F;

	if (COG_AS != SCM_SMOB_FLAGS(new_as))
		return SCM_BOOL_F;

	SCM old_as = ss_get_as();
	as_ref_count(old_as, ss_to_atomspace(new_as));

	scm_fluid_set_x(atomspace_fluid, new_as);

	return old_as;
}

/* ============================================================== */
/**
 * Set the atomspace into the top-level interaction environment
 * Since its held in a fluid, it can have a different value in each
 * thread, so that different threads can use different atomspaces,
 * all at the same time.
 */

void SchemeSmob::ss_set_env_as(AtomSpace *nas)
{
	as_ref_count(ss_get_as(), nas);

	scm_fluid_set_x(atomspace_fluid, make_as(nas));
}

AtomSpace* SchemeSmob::ss_get_env_as(const char* subr)
{
	SCM ref = scm_fluid_ref(atomspace_fluid);
	AtomSpace* as = ss_to_atomspace(ref);
	// if (NULL == as)
	//	scm_misc_error(subr, "No atomspace was specified!", SCM_BOOL_F);
	return as;
}

/* ============================================================== */

/**
 * Search for an atomspace in a list of values.
 * Return the atomspace if found, else return null.
 * Throw errors if the list is not stictly just key-value pairs
 */
AtomSpace* SchemeSmob::get_as_from_list(SCM slist)
{
	while (scm_is_pair(slist))
	{
		SCM sval = SCM_CAR(slist);
		AtomSpace* as = ss_to_atomspace(sval);
		if (as) return as;
		slist = SCM_CDR(slist);
	}

	return NULL;
}

#endif /* HAVE_GUILE */
/* ===================== END OF FILE ============================ */
<commit_msg>typoes<commit_after>/*
 * SchemeSmobAS.c
 *
 * Scheme small objects (SMOBS) for atom spaces.
 *
 * Copyright (c) 2008,2009,2014 Linas Vepstas <linas@linas.org>
 */

#ifdef HAVE_GUILE

#include <cstddef>
#include <libguile.h>

#include <opencog/atomspace/AtomSpace.h>
#include <opencog/guile/SchemeSmob.h>

using namespace opencog;

// Need a lock to protect the map, since multiple threads may be trying
// to update this map.  The map contains a use-count for the number of
// threads that are currently using this atomspace as the current
// attomspace.  When the count drops to zero, the atomspace will be
// reaped if the number of SCM references also drops to zero (and the
// GC runs).
std::mutex SchemeSmob::as_mtx;
std::map<AtomSpace*, int> SchemeSmob::deleteable_as;

/* ============================================================== */

std::string SchemeSmob::as_to_string(const AtomSpace *as)
{
#define BUFLEN 120
	char buff[BUFLEN];

	snprintf(buff, BUFLEN, "#<atomspace %p>", as);
	return buff;
}

/* ============================================================== */
/**
 * Create SCM object wrapping the atomspace.
 * Do NOT take over memory management of it!
 */
SCM SchemeSmob::make_as (AtomSpace *as)
{
	SCM smob;
	SCM_NEWSMOB (smob, cog_misc_tag, as);
	SCM_SET_SMOB_FLAGS(smob, COG_AS);
	return smob;
}

/* ============================================================== */

/**
 * The only place this is called from is the guile garbage collector!
 * Its called when guile thinks it has no pointers to the given
 * atomspace; thus, we should free it. In fact, we only decrement the
 * use count for it; XXX but under what circumstance would that not be
 * zero? If its racing with another thread ?? I'm confused.
 *
 * Note that if the atomspace was given to us externally, e.g. by the
 * cogserver, then it will not even have a use-count (and thus, it's
 * never deleted here.)
 */
void SchemeSmob::release_as (AtomSpace *as)
{
	std::unique_lock<std::mutex> lck(as_mtx);
	auto has = deleteable_as.find(as);
	if (deleteable_as.end() == has) return;
	deleteable_as[as] --;

	if (0 == deleteable_as[as])
	{
		AtomSpace* env = as->get_environ();
		deleteable_as.erase(has);
		lck.unlock();
		scm_gc_unregister_collectable_memory (as,
	                  sizeof(*as), "opencog atomspace");
		delete as;

		// (Recursively) decrement the use count on the parent.
		// XXX This is confusing, I'm not sure its right.
		if (env) release_as(env);
	}
}

/* ============================================================== */
/**
 * Create a new atom space.  The parent argument might not
 * be present -- its optional.
 */
SCM SchemeSmob::ss_new_as (SCM s)
{
	AtomSpace *parent = ss_to_atomspace(s);

	AtomSpace *as = new AtomSpace(parent);

	scm_gc_register_collectable_memory (as,
	                 sizeof(*as), "opencog atomspace");

	// Only the internally-created atomspaces are trackable.
	std::lock_guard<std::mutex> lck(as_mtx);
	deleteable_as[as] = 1;

	// Don't delete the parent, as long as its in use by a child
	// (that guile still has a reference to).
	while (parent and deleteable_as.end() != deleteable_as.find(parent))
	{
		deleteable_as[parent]++;
		parent = parent->get_environ();
	}

#define WORK_AROUND_GUILE_20_GC_BUG
#ifdef WORK_AROUND_GUILE_20_GC_BUG
	// Below is a work-around to a bug.  You can trigger this bug
	// with the code below;  it will crash, because the initial
	// AS gets erroneously garbage-collected.  Guile is trying
	// to release the main AS every time through the loop.  I can't
	// figure out why.
/******
(use-modules (opencog))
(use-modules (opencog exec))

(define  n 0)
(define (prt)
   (set! n (+ n 1))
   (format #t "yaaaa ~a ~a\n" n cog-initial-as) (usleep 200000)
   (gc)
   (format #t "post-gc ~a\n" cog-initial-as)
   (if (< n 200) (stv 1 1) (stv 0 1)))

(DefineLink
   (DefinedPredicateNode "loopy")
   (SatisfactionLink
      (SequentialAndLink
         (EvaluationLink
            (GroundedPredicateNode "scm:prt") (ListLink))
         (DefinedPredicateNode "loopy")
      )))

(cog-evaluate! (DefinedPredicateNode "loopy"))
*******/
	static bool first = true;
	if (first)
	{
		first = false;
		deleteable_as[as] = 2002001000;
	}
#endif
	return make_as(as);
}

/* ============================================================== */
/**
 * Return true if the scm is an atom space
 */
SCM SchemeSmob::ss_as_p (SCM s)
{
	if (SCM_SMOB_PREDICATE(SchemeSmob::cog_misc_tag, s))
	{
		scm_t_bits misctype = SCM_SMOB_FLAGS(s);
		switch (misctype)
		{
			case COG_AS:
				return SCM_BOOL_T;

			default:
				return SCM_BOOL_F;
		}
	}
	return SCM_BOOL_F;
}

/* ============================================================== */
/* Cast SCM to atomspace */

AtomSpace* SchemeSmob::ss_to_atomspace(SCM sas)
{
	if (not SCM_SMOB_PREDICATE(SchemeSmob::cog_misc_tag, sas))
		return nullptr;

	scm_t_bits misctype = SCM_SMOB_FLAGS(sas);
	if (COG_AS != misctype)
		return nullptr;

	AtomSpace* as = (AtomSpace *) SCM_SMOB_DATA(sas);
	scm_remember_upto_here_1(sas);
	return as;
}

/* ============================================================== */

AtomSpace* SchemeSmob::verify_atomspace(SCM sas, const char * subrname, int pos)
{
   AtomSpace* as = ss_to_atomspace(sas);
   if (nullptr == as)
      scm_wrong_type_arg_msg(subrname, pos, sas, "opencog atomspace");

   return as;
}

/* ============================================================== */
/**
 * Return UUID of the atomspace
 */
SCM SchemeSmob::ss_as_uuid(SCM sas)
{
	AtomSpace* as = ss_to_atomspace(sas);
	if (nullptr == as)
	{
		// Special care for atom whose atomspace was null
		if (scm_is_null(sas))
			return scm_from_ulong(ULONG_MAX);
		scm_wrong_type_arg_msg("cog-atomspace-uuid", 1, sas, "atomspace");
	}

	UUID uuid = as->get_uuid();
	scm_remember_upto_here_1(sas);

	return scm_from_ulong(uuid);
}

/* ============================================================== */
/**
 * Return parent of the atomspace
 */
SCM SchemeSmob::ss_as_env(SCM sas)
{
	AtomSpace* as = ss_to_atomspace(sas);
	if (nullptr == as)
	{
		// Special care for null atomspace
		if (scm_is_null(sas))
			return SCM_EOL;
		scm_wrong_type_arg_msg("cog-atomspace-env", 1, sas, "atomspace");
	}

	AtomSpace* env = as->get_environ();
	scm_remember_upto_here_1(sas);
	return make_as(env);
}

/* ============================================================== */
/**
 * Clear the atomspace
 */
SCM SchemeSmob::ss_as_clear(SCM sas)
{
	AtomSpace* as = ss_to_atomspace(sas);
	if (nullptr == as)
		scm_wrong_type_arg_msg("cog-atomspace-clear", 1, sas, "atomspace");

	as->clear();

	scm_remember_upto_here_1(sas);
	return SCM_BOOL_T;
}

/* ============================================================== */
/**
 * Return the atomspace of an atom.
 */
SCM SchemeSmob::ss_as(SCM satom)
{
	Handle h(scm_to_handle(satom));
	if (nullptr == h)
		scm_wrong_type_arg_msg("cog-as", 1, satom, "opencog atom");

	AtomSpace* as = h->getAtomSpace();
	if (nullptr == as) return SCM_EOL;

	return make_as(as);
}

/* ============================================================== */
/**
 * Return current atomspace for this dynamic state.
 */
SCM SchemeSmob::atomspace_fluid;

SCM SchemeSmob::ss_get_as (void)
{
	return scm_fluid_ref(atomspace_fluid);
}

/// The current atomspace for the current thread must not be deleted
/// under any circumstances. Thus each thread increments a use-count
/// on the atomspace (stored in deleteable_as).  Atomspaces that are
/// not current in any thread, and also have no SCM smobs pointing
/// at them may be deleted by the gc.  This will cause atoms to
/// disappear, if the user is not careful ...
///
/// Oh, wait: only atomspaces that were internally created (i.e.
/// created by a scheme call) are eligible for deletion.  We may
/// also be given atomspaces that magically appeared from the outside
/// world --- we do NOT track those for deletion.
///
void SchemeSmob::as_ref_count(SCM old_as, AtomSpace *nas)
{
	AtomSpace* oas = ss_to_atomspace(old_as);
	if (oas != nas)
	{
		std::lock_guard<std::mutex> lck(as_mtx);
		if (deleteable_as.end() != deleteable_as.find(nas))
			deleteable_as[nas]++;
		if (oas and deleteable_as.end() != deleteable_as.find(oas))
			deleteable_as[oas] --;
	}
}

/**
 * Set the current atomspace for this dynamic state.
 * Return the previous atomspace.
 */
SCM SchemeSmob::ss_set_as (SCM new_as)
{
	if (not SCM_SMOB_PREDICATE(SchemeSmob::cog_misc_tag, new_as))
		return SCM_BOOL_F;

	if (COG_AS != SCM_SMOB_FLAGS(new_as))
		return SCM_BOOL_F;

	SCM old_as = ss_get_as();
	as_ref_count(old_as, ss_to_atomspace(new_as));

	scm_fluid_set_x(atomspace_fluid, new_as);

	return old_as;
}

/* ============================================================== */
/**
 * Set the atomspace into the top-level interaction environment
 * Since its held in a fluid, it can have a different value in each
 * thread, so that different threads can use different atomspaces,
 * all at the same time.
 */

void SchemeSmob::ss_set_env_as(AtomSpace *nas)
{
	as_ref_count(ss_get_as(), nas);

	scm_fluid_set_x(atomspace_fluid, make_as(nas));
}

AtomSpace* SchemeSmob::ss_get_env_as(const char* subr)
{
	SCM ref = scm_fluid_ref(atomspace_fluid);
	AtomSpace* as = ss_to_atomspace(ref);
	// if (NULL == as)
	//	scm_misc_error(subr, "No atomspace was specified!", SCM_BOOL_F);
	return as;
}

/* ============================================================== */

/**
 * Search for an atomspace in a list of values.
 * Return the atomspace if found, else return null.
 * Throw errors if the list is not stictly just key-value pairs
 */
AtomSpace* SchemeSmob::get_as_from_list(SCM slist)
{
	while (scm_is_pair(slist))
	{
		SCM sval = SCM_CAR(slist);
		AtomSpace* as = ss_to_atomspace(sval);
		if (as) return as;
		slist = SCM_CDR(slist);
	}

	return NULL;
}

#endif /* HAVE_GUILE */
/* ===================== END OF FILE ============================ */
<|endoftext|>


<commit_before>/*
 * SchemeSmobMisc.c
 *
 * Scheme small objects (SMOBS) garbage-collection methods
 *
 * Copyright (c) 2008,2009 Linas Vepstas <linas@linas.org>
 */

#ifdef HAVE_GUILE

#include <libguile.h>

#include <opencog/atomspace/TruthValue.h>
#include <opencog/atomspace/VersionHandle.h>
#include <opencog/guile/SchemePrimitive.h>
#include <opencog/guile/SchemeSmob.h>

using namespace opencog;

SCM SchemeSmob::mark_misc(SCM misc_smob)
{
	scm_t_bits misctype = SCM_SMOB_FLAGS(misc_smob);

	switch (misctype)
	{
		case COG_TV: // Nothing to do here ...
		case COG_VH: // Nothing to do here ...
		case COG_AV: // Nothing to do here ...
		case COG_EXTEND: // Nothing to do here ...
			return SCM_BOOL_F;
		default:
			fprintf(stderr, "Error: opencog-guile: "
			        "don't know how to mark this type: %d\n",
			        (int) misctype);
			break;
	}

	return SCM_BOOL_F;
}

/**
 * Free the memory associated with an opencog guile object.
 * This routine is called by the guile garbage collector, from time to
 * time. For testing purposes, you can force the garbage collector to
 * run by saying (gc), while, for stats, try (gc-stats) and
 * (gc-live-object-stats). The later should show both "opencog-handle"
 * and "opencog-misc" stats.
 */
size_t SchemeSmob::free_misc(SCM node)
{
	scm_t_bits misctype = SCM_SMOB_FLAGS(node);

	switch (misctype)
	{
		case COG_AV:
			AttentionValue *av;
			av = (AttentionValue *) SCM_SMOB_DATA(node);
			scm_gc_unregister_collectable_memory (av,
			                  sizeof(*av), "opencog av");
			delete av;
			return 0;

		case COG_TV:
			TruthValue *tv;
			tv = (TruthValue *) SCM_SMOB_DATA(node);
			scm_gc_unregister_collectable_memory (tv,
			                  sizeof(*tv), "opencog tv");
			delete tv;
			return 0;

		case COG_VH:
			VersionHandle *vh;
			vh = (VersionHandle *) SCM_SMOB_DATA(node);
			scm_gc_unregister_collectable_memory (vh,
			                  sizeof(*vh), "opencog vh");
			delete vh;
			return 0;

		case COG_EXTEND:
			PrimitiveEnviron *pe;
			pe = (PrimitiveEnviron *) SCM_SMOB_DATA(node);
			scm_gc_unregister_collectable_memory (pe,
			                  pe->get_size(), "opencog primitive environ");
			delete pe;
			return 0;

		default:
			fprintf(stderr, "Error: opencog-guile: "
			        "don't know how to free this type: %d\n",
			        (int) misctype);
			break;
	}
	return 0;
}

/* ============================================================== */

std::string SchemeSmob::misc_to_string(SCM node)
{
	scm_t_bits misctype = SCM_SMOB_FLAGS(node);
	switch (misctype)
	{
		case COG_AV:
			return av_to_string((AttentionValue *) SCM_SMOB_DATA(node));

		case COG_TV:
			return tv_to_string((TruthValue *) SCM_SMOB_DATA(node));

		case COG_VH:
			return vh_to_string((VersionHandle *) SCM_SMOB_DATA(node));

		case COG_EXTEND:
		{
			// return "#<opencog extension>\n";
			// Hmm. Is this really the right thing to return ?? I'm not sure ..
			PrimitiveEnviron * pe = (PrimitiveEnviron *) SCM_SMOB_DATA(node);
			return pe->get_name();
		}
		default:
			return "#<unknown opencog type>\n";
	}
	return "";
}

int SchemeSmob::print_misc(SCM node, SCM port, scm_print_state * ps)
{
	std::string str = misc_to_string(node);
	scm_puts (str.c_str(), port);
	return 1; //non-zero means success
}

/* ============================================================== */

#endif /* HAVE_GUILE */
/* ===================== END OF FILE ============================ */
<commit_msg>Hush up a strange error message.<commit_after>/*
 * SchemeSmobMisc.c
 *
 * Scheme small objects (SMOBS) garbage-collection methods
 *
 * Copyright (c) 2008,2009 Linas Vepstas <linas@linas.org>
 */

#ifdef HAVE_GUILE

#include <libguile.h>

#include <opencog/atomspace/TruthValue.h>
#include <opencog/atomspace/VersionHandle.h>
#include <opencog/guile/SchemePrimitive.h>
#include <opencog/guile/SchemeSmob.h>

using namespace opencog;

SCM SchemeSmob::mark_misc(SCM misc_smob)
{
	scm_t_bits misctype = SCM_SMOB_FLAGS(misc_smob);

	switch (misctype)
	{
		case COG_TV: // Nothing to do here ...
		case COG_VH: // Nothing to do here ...
		case COG_AV: // Nothing to do here ...
		case COG_EXTEND: // Nothing to do here ...
			return SCM_BOOL_F;

		// I don't get it .. started seeing these recently. I'm just
		// going to silently ignore thse, for now, don't know what
		// they mean. XXX TODO figure out and fix if needed. Or document.
		case 0:
			return SCM_BOOL_F;

		default:
			fprintf(stderr, "Error: opencog-guile: "
			        "don't know how to mark this type: %d\n",
			        (int) misctype);
			break;
	}

	return SCM_BOOL_F;
}

/**
 * Free the memory associated with an opencog guile object.
 * This routine is called by the guile garbage collector, from time to
 * time. For testing purposes, you can force the garbage collector to
 * run by saying (gc), while, for stats, try (gc-stats) and
 * (gc-live-object-stats). The later should show both "opencog-handle"
 * and "opencog-misc" stats.
 */
size_t SchemeSmob::free_misc(SCM node)
{
	scm_t_bits misctype = SCM_SMOB_FLAGS(node);

	switch (misctype)
	{
		case COG_AV:
			AttentionValue *av;
			av = (AttentionValue *) SCM_SMOB_DATA(node);
			scm_gc_unregister_collectable_memory (av,
			                  sizeof(*av), "opencog av");
			delete av;
			return 0;

		case COG_TV:
			TruthValue *tv;
			tv = (TruthValue *) SCM_SMOB_DATA(node);
			scm_gc_unregister_collectable_memory (tv,
			                  sizeof(*tv), "opencog tv");
			delete tv;
			return 0;

		case COG_VH:
			VersionHandle *vh;
			vh = (VersionHandle *) SCM_SMOB_DATA(node);
			scm_gc_unregister_collectable_memory (vh,
			                  sizeof(*vh), "opencog vh");
			delete vh;
			return 0;

		case COG_EXTEND:
			PrimitiveEnviron *pe;
			pe = (PrimitiveEnviron *) SCM_SMOB_DATA(node);
			scm_gc_unregister_collectable_memory (pe,
			                  pe->get_size(), "opencog primitive environ");
			delete pe;
			return 0;

		default:
			fprintf(stderr, "Error: opencog-guile: "
			        "don't know how to free this type: %d\n",
			        (int) misctype);
			break;
	}
	return 0;
}

/* ============================================================== */

std::string SchemeSmob::misc_to_string(SCM node)
{
	scm_t_bits misctype = SCM_SMOB_FLAGS(node);
	switch (misctype)
	{
		case COG_AV:
			return av_to_string((AttentionValue *) SCM_SMOB_DATA(node));

		case COG_TV:
			return tv_to_string((TruthValue *) SCM_SMOB_DATA(node));

		case COG_VH:
			return vh_to_string((VersionHandle *) SCM_SMOB_DATA(node));

		case COG_EXTEND:
		{
			// return "#<opencog extension>\n";
			// Hmm. Is this really the right thing to return ?? I'm not sure ..
			PrimitiveEnviron * pe = (PrimitiveEnviron *) SCM_SMOB_DATA(node);
			return pe->get_name();
		}
		default:
			return "#<unknown opencog type>\n";
	}
	return "";
}

int SchemeSmob::print_misc(SCM node, SCM port, scm_print_state * ps)
{
	std::string str = misc_to_string(node);
	scm_puts (str.c_str(), port);
	return 1; //non-zero means success
}

/* ============================================================== */

#endif /* HAVE_GUILE */
/* ===================== END OF FILE ============================ */
<|endoftext|>