<commit_before>/*
 *
 * Copyright 2016, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <map>

#include "src/compiler/config.h"
#include "src/compiler/generator_helpers.h"
#include "src/compiler/node_generator_helpers.h"

using grpc::protobuf::FileDescriptor;
using grpc::protobuf::ServiceDescriptor;
using grpc::protobuf::MethodDescriptor;
using grpc::protobuf::Descriptor;
using grpc::protobuf::io::Printer;
using grpc::protobuf::io::StringOutputStream;
using std::map;

namespace grpc_node_generator {
namespace {

// Returns the alias we assign to the module of the given .proto filename
// when importing. Copied entirely from
// github:google/protobuf/src/google/protobuf/compiler/js/js_generator.cc#L154
grpc::string ModuleAlias(const grpc::string filename) {
  // This scheme could technically cause problems if a file includes any 2 of:
  //   foo/bar_baz.proto
  //   foo_bar_baz.proto
  //   foo_bar/baz.proto
  //
  // We'll worry about this problem if/when we actually see it.  This name isn't
  // exposed to users so we can change it later if we need to.
  grpc::string basename = grpc_generator::StripProto(filename);
  basename = grpc_generator::StringReplace(basename, "-", "$");
  basename = grpc_generator::StringReplace(basename, "/", "_");
  return basename + "_pb";
}

// Given a filename like foo/bar/baz.proto, returns the corresponding JavaScript
// message file foo/bar/baz.js
grpc::string GetJSMessageFilename(const grpc::string& filename) {
  grpc::string name = filename;
  return grpc_generator::StripProto(name) + "_pb.js";
}

// Given a filename like foo/bar/baz.proto, returns the root directory
// path ../../
grpc::string GetRootPath(const grpc::string& from_filename,
                         const grpc::string& to_filename) {
  if (to_filename.find("google/protobuf") == 0) {
    // Well-known types (.proto files in the google/protobuf directory) are
    // assumed to come from the 'google-protobuf' npm package.  We may want to
    // generalize this exception later by letting others put generated code in
    // their own npm packages.
    return "google-protobuf/";
  }
  size_t slashes = std::count(from_filename.begin(), from_filename.end(), '/');
  if (slashes == 0) {
    return "./";
  }
  grpc::string result = "";
  for (size_t i = 0; i < slashes; i++) {
    result += "../";
  }
  return result;
}

// Return the relative path to load to_file from the directory containing
// from_file, assuming that both paths are relative to the same directory
grpc::string GetRelativePath(const grpc::string& from_file,
                             const grpc::string& to_file) {
  return GetRootPath(from_file, to_file) + to_file;
}

/* Finds all message types used in all services in the file, and returns them
 * as a map of fully qualified message type name to message descriptor */
map<grpc::string, const Descriptor*> GetAllMessages(const FileDescriptor *file) {
  map<grpc::string, const Descriptor*> message_types;
  for (int service_num = 0; service_num < file->service_count(); service_num++) {
    const ServiceDescriptor* service = file->service(service_num);
    for (int method_num = 0; method_num < service->method_count(); method_num++) {
      const MethodDescriptor* method = service->method(method_num);
      const Descriptor* input_type = method->input_type();
      const Descriptor* output_type = method->output_type();
      message_types[input_type->full_name()] = input_type;
      message_types[output_type->full_name()] = output_type;
    }
  }
  return message_types;
}

grpc::string MessageIdentifierName(const grpc::string& name) {
  return grpc_generator::StringReplace(name, ".", "_");
}

grpc::string NodeObjectPath(const Descriptor *descriptor) {
  grpc::string module_alias = ModuleAlias(descriptor->file()->name());
  grpc::string name = descriptor->full_name();
  grpc_generator::StripPrefix(&name, descriptor->file()->package() + ".");
  return module_alias + "." + name;
}

// Prints out the message serializer and deserializer functions
void PrintMessageTransformer(const Descriptor *descriptor, Printer *out) {
  map<grpc::string, grpc::string> template_vars;
  grpc::string full_name = descriptor->full_name();
  template_vars["identifier_name"] = MessageIdentifierName(full_name);
  template_vars["name"] = full_name;
  template_vars["node_name"] = NodeObjectPath(descriptor);
  // Print the serializer
  out->Print(template_vars, "function serialize_$identifier_name$(arg) {\n");
  out->Indent();
  out->Print(template_vars, "if (!(arg instanceof $node_name$)) {\n");
  out->Indent();
  out->Print(template_vars,
             "throw new Error('Expected argument of type $name$');\n");
  out->Outdent();
  out->Print("}\n");
  out->Print("return new Buffer(arg.serializeBinary());\n");
  out->Outdent();
  out->Print("}\n\n");

  // Print the deserializer
  out->Print(template_vars,
             "function deserialize_$identifier_name$(buffer_arg) {\n");
  out->Indent();
  out->Print(
      template_vars,
      "return $node_name$.deserializeBinary(new Uint8Array(buffer_arg));\n");
  out->Outdent();
  out->Print("}\n\n");
}

void PrintMethod(const MethodDescriptor *method, Printer *out) {
  const Descriptor *input_type = method->input_type();
  const Descriptor *output_type = method->output_type();
  map<grpc::string, grpc::string> vars;
  vars["service_name"] = method->service()->full_name();
  vars["name"] = method->name();
  vars["input_type"] = NodeObjectPath(input_type);
  vars["input_type_id"] = MessageIdentifierName(input_type->full_name());
  vars["output_type"] = NodeObjectPath(output_type);
  vars["output_type_id"] = MessageIdentifierName(output_type->full_name());
  vars["client_stream"] = method->client_streaming() ? "true" : "false";
  vars["server_stream"] = method->server_streaming() ? "true" : "false";
  out->Print("{\n");
  out->Indent();
  out->Print(vars, "path: '/$service_name$/$name$',\n");
  out->Print(vars, "requestStream: $client_stream$,\n");
  out->Print(vars, "responseStream: $server_stream$,\n");
  out->Print(vars, "requestType: $input_type$,\n");
  out->Print(vars, "responseType: $output_type$,\n");
  out->Print(vars, "requestSerialize: serialize_$input_type_id$,\n");
  out->Print(vars, "requestDeserialize: deserialize_$input_type_id$,\n");
  out->Print(vars, "responseSerialize: serialize_$output_type_id$,\n");
  out->Print(vars, "responseDeserialize: deserialize_$output_type_id$,\n");
  out->Outdent();
  out->Print("}");
}

// Prints out the service descriptor object
void PrintService(const ServiceDescriptor *service, Printer *out) {
  map<grpc::string, grpc::string> template_vars;
  out->Print(GetNodeComments(service, true).c_str());
  template_vars["name"] = service->name();
  out->Print(template_vars, "var $name$Service = exports.$name$Service = {\n");
  out->Indent();
  for (int i = 0; i < service->method_count(); i++) {
    grpc::string method_name = grpc_generator::LowercaseFirstLetter(
        service->method(i)->name());
    out->Print(GetNodeComments(service->method(i), true).c_str());
    out->Print("$method_name$: ",
               "method_name", method_name);
    PrintMethod(service->method(i), out);
    out->Print(",\n");
    out->Print(GetNodeComments(service->method(i), false).c_str());
  }
  out->Outdent();
  out->Print("};\n\n");
  out->Print(template_vars, "exports.$name$Client = "
             "grpc.makeGenericClientConstructor($name$Service);\n");
  out->Print(GetNodeComments(service, false).c_str());
}

void PrintImports(const FileDescriptor *file, Printer *out) {
  out->Print("var grpc = require('grpc');\n");
  if (file->message_type_count() > 0) {
    grpc::string file_path = GetRelativePath(file->name(),
                                             GetJSMessageFilename(
                                                 file->name()));
    out->Print("var $module_alias$ = require('$file_path$');\n",
               "module_alias", ModuleAlias(file->name()),
               "file_path", file_path);
  }

  for (int i = 0; i < file->dependency_count(); i++) {
    grpc::string file_path = GetRelativePath(
        file->name(), GetJSMessageFilename(file->dependency(i)->name()));
    out->Print("var $module_alias$ = require('$file_path$');\n",
               "module_alias", ModuleAlias(file->dependency(i)->name()),
               "file_path", file_path);
  }
  out->Print("\n");
}

void PrintTransformers(const FileDescriptor *file, Printer *out) {
  map<grpc::string, const Descriptor*> messages = GetAllMessages(file);
  for (std::map<grpc::string, const Descriptor*>::iterator it =
           messages.begin();
       it != messages.end(); it++) {
    PrintMessageTransformer(it->second, out);
  }
  out->Print("\n");
}

void PrintServices(const FileDescriptor *file, Printer *out) {
  for (int i = 0; i < file->service_count(); i++) {
    PrintService(file->service(i), out);
  }
}

}

grpc::string GenerateFile(const FileDescriptor *file) {
  grpc::string output;
  {
    StringOutputStream output_stream(&output);
    Printer out(&output_stream, '$');

    if (file->service_count() == 0) {
      return output;
    }
    out.Print("// GENERATED CODE -- DO NOT EDIT!\n\n");

    grpc::string leading_comments = GetNodeComments(file, true);
    if (!leading_comments.empty()) {
      out.Print("// Original file comments:\n");
      out.Print(leading_comments.c_str());
    }

    out.Print("'use strict';\n");

    PrintImports(file, &out);

    PrintTransformers(file, &out);

    PrintServices(file, &out);

    out.Print(GetNodeComments(file, false).c_str());
  }
  return output;
}

}  // namespace grpc_node_generator
<commit_msg>Clang format code<commit_after>/*
 *
 * Copyright 2016, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <map>

#include "src/compiler/config.h"
#include "src/compiler/generator_helpers.h"
#include "src/compiler/node_generator_helpers.h"

using grpc::protobuf::FileDescriptor;
using grpc::protobuf::ServiceDescriptor;
using grpc::protobuf::MethodDescriptor;
using grpc::protobuf::Descriptor;
using grpc::protobuf::io::Printer;
using grpc::protobuf::io::StringOutputStream;
using std::map;

namespace grpc_node_generator {
namespace {

// Returns the alias we assign to the module of the given .proto filename
// when importing. Copied entirely from
// github:google/protobuf/src/google/protobuf/compiler/js/js_generator.cc#L154
grpc::string ModuleAlias(const grpc::string filename) {
  // This scheme could technically cause problems if a file includes any 2 of:
  //   foo/bar_baz.proto
  //   foo_bar_baz.proto
  //   foo_bar/baz.proto
  //
  // We'll worry about this problem if/when we actually see it.  This name isn't
  // exposed to users so we can change it later if we need to.
  grpc::string basename = grpc_generator::StripProto(filename);
  basename = grpc_generator::StringReplace(basename, "-", "$");
  basename = grpc_generator::StringReplace(basename, "/", "_");
  return basename + "_pb";
}

// Given a filename like foo/bar/baz.proto, returns the corresponding JavaScript
// message file foo/bar/baz.js
grpc::string GetJSMessageFilename(const grpc::string& filename) {
  grpc::string name = filename;
  return grpc_generator::StripProto(name) + "_pb.js";
}

// Given a filename like foo/bar/baz.proto, returns the root directory
// path ../../
grpc::string GetRootPath(const grpc::string& from_filename,
                         const grpc::string& to_filename) {
  if (to_filename.find("google/protobuf") == 0) {
    // Well-known types (.proto files in the google/protobuf directory) are
    // assumed to come from the 'google-protobuf' npm package.  We may want to
    // generalize this exception later by letting others put generated code in
    // their own npm packages.
    return "google-protobuf/";
  }
  size_t slashes = std::count(from_filename.begin(), from_filename.end(), '/');
  if (slashes == 0) {
    return "./";
  }
  grpc::string result = "";
  for (size_t i = 0; i < slashes; i++) {
    result += "../";
  }
  return result;
}

// Return the relative path to load to_file from the directory containing
// from_file, assuming that both paths are relative to the same directory
grpc::string GetRelativePath(const grpc::string& from_file,
                             const grpc::string& to_file) {
  return GetRootPath(from_file, to_file) + to_file;
}

/* Finds all message types used in all services in the file, and returns them
 * as a map of fully qualified message type name to message descriptor */
map<grpc::string, const Descriptor *> GetAllMessages(
    const FileDescriptor *file) {
  map<grpc::string, const Descriptor *> message_types;
  for (int service_num = 0; service_num < file->service_count();
       service_num++) {
    const ServiceDescriptor *service = file->service(service_num);
    for (int method_num = 0; method_num < service->method_count();
         method_num++) {
      const MethodDescriptor *method = service->method(method_num);
      const Descriptor *input_type = method->input_type();
      const Descriptor *output_type = method->output_type();
      message_types[input_type->full_name()] = input_type;
      message_types[output_type->full_name()] = output_type;
    }
  }
  return message_types;
}

grpc::string MessageIdentifierName(const grpc::string& name) {
  return grpc_generator::StringReplace(name, ".", "_");
}

grpc::string NodeObjectPath(const Descriptor *descriptor) {
  grpc::string module_alias = ModuleAlias(descriptor->file()->name());
  grpc::string name = descriptor->full_name();
  grpc_generator::StripPrefix(&name, descriptor->file()->package() + ".");
  return module_alias + "." + name;
}

// Prints out the message serializer and deserializer functions
void PrintMessageTransformer(const Descriptor *descriptor, Printer *out) {
  map<grpc::string, grpc::string> template_vars;
  grpc::string full_name = descriptor->full_name();
  template_vars["identifier_name"] = MessageIdentifierName(full_name);
  template_vars["name"] = full_name;
  template_vars["node_name"] = NodeObjectPath(descriptor);
  // Print the serializer
  out->Print(template_vars, "function serialize_$identifier_name$(arg) {\n");
  out->Indent();
  out->Print(template_vars, "if (!(arg instanceof $node_name$)) {\n");
  out->Indent();
  out->Print(template_vars,
             "throw new Error('Expected argument of type $name$');\n");
  out->Outdent();
  out->Print("}\n");
  out->Print("return new Buffer(arg.serializeBinary());\n");
  out->Outdent();
  out->Print("}\n\n");

  // Print the deserializer
  out->Print(template_vars,
             "function deserialize_$identifier_name$(buffer_arg) {\n");
  out->Indent();
  out->Print(
      template_vars,
      "return $node_name$.deserializeBinary(new Uint8Array(buffer_arg));\n");
  out->Outdent();
  out->Print("}\n\n");
}

void PrintMethod(const MethodDescriptor *method, Printer *out) {
  const Descriptor *input_type = method->input_type();
  const Descriptor *output_type = method->output_type();
  map<grpc::string, grpc::string> vars;
  vars["service_name"] = method->service()->full_name();
  vars["name"] = method->name();
  vars["input_type"] = NodeObjectPath(input_type);
  vars["input_type_id"] = MessageIdentifierName(input_type->full_name());
  vars["output_type"] = NodeObjectPath(output_type);
  vars["output_type_id"] = MessageIdentifierName(output_type->full_name());
  vars["client_stream"] = method->client_streaming() ? "true" : "false";
  vars["server_stream"] = method->server_streaming() ? "true" : "false";
  out->Print("{\n");
  out->Indent();
  out->Print(vars, "path: '/$service_name$/$name$',\n");
  out->Print(vars, "requestStream: $client_stream$,\n");
  out->Print(vars, "responseStream: $server_stream$,\n");
  out->Print(vars, "requestType: $input_type$,\n");
  out->Print(vars, "responseType: $output_type$,\n");
  out->Print(vars, "requestSerialize: serialize_$input_type_id$,\n");
  out->Print(vars, "requestDeserialize: deserialize_$input_type_id$,\n");
  out->Print(vars, "responseSerialize: serialize_$output_type_id$,\n");
  out->Print(vars, "responseDeserialize: deserialize_$output_type_id$,\n");
  out->Outdent();
  out->Print("}");
}

// Prints out the service descriptor object
void PrintService(const ServiceDescriptor *service, Printer *out) {
  map<grpc::string, grpc::string> template_vars;
  out->Print(GetNodeComments(service, true).c_str());
  template_vars["name"] = service->name();
  out->Print(template_vars, "var $name$Service = exports.$name$Service = {\n");
  out->Indent();
  for (int i = 0; i < service->method_count(); i++) {
    grpc::string method_name = grpc_generator::LowercaseFirstLetter(
        service->method(i)->name());
    out->Print(GetNodeComments(service->method(i), true).c_str());
    out->Print("$method_name$: ",
               "method_name", method_name);
    PrintMethod(service->method(i), out);
    out->Print(",\n");
    out->Print(GetNodeComments(service->method(i), false).c_str());
  }
  out->Outdent();
  out->Print("};\n\n");
  out->Print(template_vars, "exports.$name$Client = "
             "grpc.makeGenericClientConstructor($name$Service);\n");
  out->Print(GetNodeComments(service, false).c_str());
}

void PrintImports(const FileDescriptor *file, Printer *out) {
  out->Print("var grpc = require('grpc');\n");
  if (file->message_type_count() > 0) {
    grpc::string file_path = GetRelativePath(file->name(),
                                             GetJSMessageFilename(
                                                 file->name()));
    out->Print("var $module_alias$ = require('$file_path$');\n",
               "module_alias", ModuleAlias(file->name()),
               "file_path", file_path);
  }

  for (int i = 0; i < file->dependency_count(); i++) {
    grpc::string file_path = GetRelativePath(
        file->name(), GetJSMessageFilename(file->dependency(i)->name()));
    out->Print("var $module_alias$ = require('$file_path$');\n",
               "module_alias", ModuleAlias(file->dependency(i)->name()),
               "file_path", file_path);
  }
  out->Print("\n");
}

void PrintTransformers(const FileDescriptor *file, Printer *out) {
  map<grpc::string, const Descriptor*> messages = GetAllMessages(file);
  for (std::map<grpc::string, const Descriptor*>::iterator it =
           messages.begin();
       it != messages.end(); it++) {
    PrintMessageTransformer(it->second, out);
  }
  out->Print("\n");
}

void PrintServices(const FileDescriptor *file, Printer *out) {
  for (int i = 0; i < file->service_count(); i++) {
    PrintService(file->service(i), out);
  }
}

}

grpc::string GenerateFile(const FileDescriptor *file) {
  grpc::string output;
  {
    StringOutputStream output_stream(&output);
    Printer out(&output_stream, '$');

    if (file->service_count() == 0) {
      return output;
    }
    out.Print("// GENERATED CODE -- DO NOT EDIT!\n\n");

    grpc::string leading_comments = GetNodeComments(file, true);
    if (!leading_comments.empty()) {
      out.Print("// Original file comments:\n");
      out.Print(leading_comments.c_str());
    }

    out.Print("'use strict';\n");

    PrintImports(file, &out);

    PrintTransformers(file, &out);

    PrintServices(file, &out);

    out.Print(GetNodeComments(file, false).c_str());
  }
  return output;
}

}  // namespace grpc_node_generator
<|endoftext|>


<commit_before>inline ll orientation(point p, point q, point r){
	return (q.second-p.second)*(r.first-p.first) - (q.first-p.first)*(r.second-p.second);
}

set<point> pts;
vector<point> up, dn;

void convexHull(){
	up.assign(pts.size(),point());
	dn.assign(pts.size(),point());
	int i = 0, j = 0;

	for(set<point>::iterator it = pts.begin(); it != pts.end(); ++it){
		while(i > 1 && orientation(up[i-2], up[i-1], *it) <= 0) i--;
		while(j > 1 && orientation(dn[j-2], dn[j-1], *it) >= 0) j--;

		up[i++] = *it;
		dn[j++] = *it;
	}
	up.resize(i);
	dn.resize(j);
}
<commit_msg>Diminuindo impl do monotone chain (usando funcoes do CP3)<commit_after>set<point> pts;
vector<point> up, dn;

void convexHull(){
	up.assign(pts.size(),point());
	dn.assign(pts.size(),point());
	int i = 0, j = 0;

	for(set<point>::iterator it = pts.begin(); it != pts.end(); ++it){
		while(i > 1 && ccw(up[i-2], up[i-1], *it)) i--;
		while(j > 1 && !ccw(dn[j-2], dn[j-1], *it)) j--;

		up[i++] = *it;
		dn[j++] = *it;
	}
	up.resize(i);
	dn.resize(j);
}
<|endoftext|>


<commit_before>/*
This file is part of cphVB and copyright (c) 2012 the cphVB team:
http://cphvb.bitbucket.org

cphVB is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3
of the License, or (at your option) any later version.

cphVB is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the
GNU Lesser General Public License along with cphVB.

If not, see <http://www.gnu.org/licenses/>.
*/

#include <cassert>
#include <cstring>
#include <iostream>
#include <vector>
#include <set>
#include <cphvb.h>

#include "cphvb_vem_cluster.h"
#include "mapping.h"
#include "array.h"
#include "pgrid.h"
#include "dispatch.h"
#include "comm.h"
#include "except.h"
#include "ufunc_reduce.h"

//Function pointers to the Node VEM.
static cphvb_init vem_init;
static cphvb_shutdown vem_shutdown;
static cphvb_reg_func vem_reg_func;
//Public function pointer to the Node VEM
cphvb_execute exec_vem_execute;

//The VE components
static cphvb_component **my_components;

//Our self
static cphvb_component *myself;

//Number of user-defined functions registered.
static cphvb_intp userfunc_count = 0;
//User-defined function IDs.
static cphvb_userfunc_impl reduce_impl = NULL;
static cphvb_intp reduce_impl_id = 0;

/* Initialize the VEM
 *
 * @return Error codes (CPHVB_SUCCESS)
 */
cphvb_error exec_init(const char *component_name)
{
    cphvb_intp children_count;
    cphvb_error err;
    myself = cphvb_component_setup(component_name);
    if(myself == NULL)
        return CPHVB_ERROR;

    err = cphvb_component_children(myself, &children_count, &my_components);
    if (children_count != 1)
    {
		std::cerr << "Unexpected number of child nodes for VEM, must be 1" << std::endl;
		return CPHVB_ERROR;
    }

    if (err != CPHVB_SUCCESS)
	    return err;

    vem_init = my_components[0]->init;
    exec_vem_execute = my_components[0]->execute;
    vem_shutdown = my_components[0]->shutdown;
    vem_reg_func = my_components[0]->reg_func;

    //Let us initiate the Node VEM.
    if((err = vem_init(my_components[0])) != 0)
        return err;

    return CPHVB_SUCCESS;
}

/* Shutdown the VEM, which include a instruction flush
 *
 * @return Error codes (CPHVB_SUCCESS)
 */
cphvb_error exec_shutdown(void)
{
    cphvb_error err;
    if((err = vem_shutdown()) != CPHVB_SUCCESS)
        return err;
    cphvb_component_free(my_components[0]);//Only got one child.
    vem_init     = NULL;
    exec_vem_execute  = NULL;
    vem_shutdown = NULL;
    vem_reg_func = NULL;
    cphvb_component_free_ptr(my_components);
    my_components = NULL;

    //Finalize the process grid
    pgrid_finalize();

    //Finalize the process grid
    dispatch_finalize();

    return CPHVB_SUCCESS;
}

/* Register a new user-defined function.
 *
 * @lib Name of the shared library e.g. libmyfunc.so
 *      When NULL the default library is used.
 * @fun Name of the function e.g. myfunc
 * @id Identifier for the new function. The bridge should set the
 *     initial value to Zero. (in/out-put)
 * @return Error codes (CPHVB_SUCCESS)
 */
cphvb_error exec_reg_func(char *fun, cphvb_intp *id)
{
    cphvb_error e;

    if(*id == 0)//Only if parent didn't set the ID.
    {
        *id = ++userfunc_count;
        assert(pgrid_myrank == 0);
    }

    if((e = vem_reg_func(fun, id)) != CPHVB_SUCCESS)
    {
        *id = 0;
        return e;
    }

    if(strcmp("cphvb_reduce", fun) == 0)
    {
        if(reduce_impl == NULL)
        {
            cphvb_component_get_func(myself, fun, &reduce_impl);
            if (reduce_impl == NULL)
                return CPHVB_USERFUNC_NOT_SUPPORTED;

            reduce_impl_id = *id;
            return CPHVB_SUCCESS;
        }
    }

    return CPHVB_SUCCESS;
}

/* Execute one instruction locally.
 *
 * @opcode   The opcode of the instruction
 * @operands The local operands in the instruction
 * @ufunc  The user-defined function struct when opcode is CPHVB_USERFUNC.
 */
void exec_local_inst(cphvb_opcode opcode, cphvb_array *operands[],
                     cphvb_userfunc *ufunc)
{
    cphvb_error e;
    cphvb_instruction new_inst;
    new_inst.opcode = opcode;
    new_inst.status = CPHVB_INST_PENDING;
    new_inst.userfunc = ufunc;
    if(ufunc == NULL)
    {
        assert(opcode != CPHVB_USERFUNC);
        memcpy(new_inst.operand, operands, cphvb_operands(opcode)
                                           * sizeof(cphvb_array*));
    }

    if((e = exec_vem_execute(1, &new_inst)) != CPHVB_SUCCESS)
        EXCEPT_INST(opcode, e, new_inst.status);
}

/* Execute to instruction locally at the master-process
 *
 * @instruction The instructionto execute
 * @return Error codes (CPHVB_SUCCESS)
 */
static cphvb_error fallback_exec(cphvb_instruction *inst)
{
    cphvb_error e;
    int nop = cphvb_operands_in_instruction(inst);
    std::set<cphvb_array*> arys2discard;

    //Gather all data at the master-process
    cphvb_array **oprands = cphvb_inst_operands(inst);
    for(cphvb_intp o=0; o < nop; ++o)
    {
        cphvb_array *op = oprands[o];
        if(cphvb_is_constant(op))
            continue;

        cphvb_array *base = cphvb_base_array(op);
        comm_slaves2master(base);
    }

    //Do global instruction
    if(pgrid_myrank == 0)
    {
        if((e = exec_vem_execute(1, inst)) != CPHVB_SUCCESS)
            return e;
    }

    //Scatter all data back to all processes
    for(cphvb_intp o=0; o < nop; ++o)
    {
        cphvb_array *op = oprands[o];
        if(cphvb_is_constant(op))
            continue;
        cphvb_array *base = cphvb_base_array(op);

        //We have to make sure that the master-process has allocated memory
        //because the slaves cannot determine it.
        if(pgrid_myrank == 0)
        {
            if((e = cphvb_data_malloc(base)) != CPHVB_SUCCESS)
                return e;
        }

        comm_master2slaves(base);

        //All local arrays should be discarded
        arys2discard.insert(op);
        arys2discard.insert(base);
    }
    //Discard all local views
    for(std::set<cphvb_array*>::iterator it=arys2discard.begin();
        it != arys2discard.end(); ++it)
    {
        if((*it)->base != NULL)
        {
            cphvb_array *ops[1] = {*it};
            exec_local_inst(CPHVB_DISCARD, ops, NULL);
        }
    }
    //Free and discard all local base arrays
    for(std::set<cphvb_array*>::iterator it=arys2discard.begin();
        it != arys2discard.end(); ++it)
    {
        if((*it)->base == NULL)
        {
            cphvb_array *ops[1] = {*it};
            exec_local_inst(CPHVB_FREE, ops, NULL);
            exec_local_inst(CPHVB_DISCARD, ops, NULL);
        }
    }
    return CPHVB_SUCCESS;
}

/* Execute a regular computation instruction
 *
 * @instruction The regular computation instruction
 * @return Error codes (CPHVB_SUCCESS)
 */
static cphvb_error execute_regular(cphvb_instruction *inst)
{
    cphvb_error e;
    std::vector<ary_chunk> chunks;
    int nop = cphvb_operands_in_instruction(inst);
    cphvb_array **operands = cphvb_inst_operands(inst);

    mapping_chunks(nop, operands, chunks);
    assert(chunks.size() > 0);

    //Handle one chunk at a time.
    for(std::vector<ary_chunk>::size_type c=0; c < chunks.size();c += nop)
    {
        assert(cphvb_nelements(chunks[0].ary.ndim, chunks[0].ary.shape) > 0);

        //The process where the output chunk is located will do the computation.
        int owner_rank = chunks[0+c].rank;

        //Create a local instruction based on the array-chunks
        cphvb_instruction local_inst = *inst;
        for(cphvb_intp k=0; k < nop; ++k)
        {
            if(!cphvb_is_constant(inst->operand[k]))
            {
                ary_chunk *chunk = &chunks[k+c];
                local_inst.operand[k] = &chunk->ary;
                comm_array_data(chunk, owner_rank);
            }
        }

        //Check if we should do the computation
        if(pgrid_myrank != owner_rank)
            continue;

        //Apply the local computation
        local_inst.status = CPHVB_INST_PENDING;
        e = exec_vem_execute(1, &local_inst);
        inst->status = local_inst.status;
        if(e != CPHVB_SUCCESS)
            return e;

        //Free and discard all local chunk arrays
        for(cphvb_intp k=0; k < nop; ++k)
        {
            if(cphvb_is_constant(inst->operand[k]))
                continue;

            cphvb_array *ary = &chunks[k+c].ary;
            if(ary->base == NULL)
                exec_local_inst(CPHVB_FREE, &ary, NULL);
            exec_local_inst(CPHVB_DISCARD, &ary, NULL);
        }
    }
    return CPHVB_SUCCESS;
}

/* Execute a list of instructions where all operands are global arrays
 *
 * @instruction A list of instructions to execute
 * @return Error codes
 */
cphvb_error exec_execute(cphvb_intp count, cphvb_instruction inst_list[])
{
    cphvb_error e;
    if(count <= 0)
        return CPHVB_SUCCESS;

//    cphvb_pprint_instr_list(inst_list, count, "GLOBAL");

    for(cphvb_intp i=0; i < count; ++i)
    {
        cphvb_instruction* inst = &inst_list[i];
        assert(inst->opcode >= 0);
        switch(inst->opcode)
        {
            case CPHVB_USERFUNC:
            {
                if (inst->userfunc->id == reduce_impl_id)
                {
                    //TODO: the cphvb_reduce is hardcoded for now.
                    inst->status = cphvb_reduce(inst->userfunc, NULL);
                    if(inst->status == CPHVB_ERROR)
                        return CPHVB_ERROR;
                    if(inst->status != CPHVB_SUCCESS)
                        return CPHVB_PARTIAL_SUCCESS;
                }
                else
                {
                    if((e = fallback_exec(inst)) != CPHVB_SUCCESS)
                        return e;
                }
                break;
            }
            case CPHVB_DISCARD:
            {
                cphvb_array *g_ary = inst->operand[0];
                cphvb_array *l_ary = array_get_existing_local(g_ary);
                if(g_ary->base == NULL)
                {
                    if(l_ary != NULL)
                        exec_local_inst(CPHVB_DISCARD, &l_ary, NULL);
                    array_rm_local(g_ary);
                }
                dispatch_slave_known_remove(g_ary);
                break;
            }
            case CPHVB_FREE:
            {
                cphvb_array *g_ary = cphvb_base_array(inst->operand[0]);
                cphvb_array *l_ary = array_get_existing_local(g_ary);
                cphvb_data_free(g_ary);
                if(l_ary != NULL)
                {
                    exec_local_inst(CPHVB_FREE, &l_ary, NULL);
                }
                break;
            }
            case CPHVB_SYNC:
            {
                cphvb_array *base = cphvb_base_array(inst->operand[0]);
                comm_slaves2master(base);
                break;
            }
            case CPHVB_NONE:
            {
                break;
            }
            default:
            {
                if((e = execute_regular(inst)) != CPHVB_SUCCESS)
                    return e;
            }
        }
    }
    return CPHVB_SUCCESS;
}

cphvb_error cphvb_reduce( cphvb_userfunc *arg, void* ve_arg)
{
    cphvb_reduce_type *a = (cphvb_reduce_type *) arg;   // Grab function arguments
    cphvb_opcode opcode = a->opcode;                    // Opcode
    cphvb_index axis    = a->axis;                      // The axis to reduce

    return ufunc_reduce(opcode, axis, a->operand, reduce_impl_id);
}
<commit_msg>Now using exceptions in fallback_exec() and regular_exec()<commit_after>/*
This file is part of cphVB and copyright (c) 2012 the cphVB team:
http://cphvb.bitbucket.org

cphVB is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3
of the License, or (at your option) any later version.

cphVB is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the
GNU Lesser General Public License along with cphVB.

If not, see <http://www.gnu.org/licenses/>.
*/

#include <cassert>
#include <cstring>
#include <iostream>
#include <vector>
#include <set>
#include <cphvb.h>

#include "cphvb_vem_cluster.h"
#include "mapping.h"
#include "array.h"
#include "pgrid.h"
#include "dispatch.h"
#include "comm.h"
#include "except.h"
#include "ufunc_reduce.h"

//Function pointers to the Node VEM.
static cphvb_init vem_init;
static cphvb_shutdown vem_shutdown;
static cphvb_reg_func vem_reg_func;
//Public function pointer to the Node VEM
cphvb_execute exec_vem_execute;

//The VE components
static cphvb_component **my_components;

//Our self
static cphvb_component *myself;

//Number of user-defined functions registered.
static cphvb_intp userfunc_count = 0;
//User-defined function IDs.
static cphvb_userfunc_impl reduce_impl = NULL;
static cphvb_intp reduce_impl_id = 0;

/* Initialize the VEM
 *
 * @return Error codes (CPHVB_SUCCESS)
 */
cphvb_error exec_init(const char *component_name)
{
    cphvb_intp children_count;
    cphvb_error err;
    myself = cphvb_component_setup(component_name);
    if(myself == NULL)
        return CPHVB_ERROR;

    err = cphvb_component_children(myself, &children_count, &my_components);
    if (children_count != 1)
    {
		std::cerr << "Unexpected number of child nodes for VEM, must be 1" << std::endl;
		return CPHVB_ERROR;
    }

    if (err != CPHVB_SUCCESS)
	    return err;

    vem_init = my_components[0]->init;
    exec_vem_execute = my_components[0]->execute;
    vem_shutdown = my_components[0]->shutdown;
    vem_reg_func = my_components[0]->reg_func;

    //Let us initiate the Node VEM.
    if((err = vem_init(my_components[0])) != 0)
        return err;

    return CPHVB_SUCCESS;
}

/* Shutdown the VEM, which include a instruction flush
 *
 * @return Error codes (CPHVB_SUCCESS)
 */
cphvb_error exec_shutdown(void)
{
    cphvb_error err;
    if((err = vem_shutdown()) != CPHVB_SUCCESS)
        return err;
    cphvb_component_free(my_components[0]);//Only got one child.
    vem_init     = NULL;
    exec_vem_execute  = NULL;
    vem_shutdown = NULL;
    vem_reg_func = NULL;
    cphvb_component_free_ptr(my_components);
    my_components = NULL;

    //Finalize the process grid
    pgrid_finalize();

    //Finalize the process grid
    dispatch_finalize();

    return CPHVB_SUCCESS;
}

/* Register a new user-defined function.
 *
 * @lib Name of the shared library e.g. libmyfunc.so
 *      When NULL the default library is used.
 * @fun Name of the function e.g. myfunc
 * @id Identifier for the new function. The bridge should set the
 *     initial value to Zero. (in/out-put)
 * @return Error codes (CPHVB_SUCCESS)
 */
cphvb_error exec_reg_func(char *fun, cphvb_intp *id)
{
    cphvb_error e;

    if(*id == 0)//Only if parent didn't set the ID.
    {
        *id = ++userfunc_count;
        assert(pgrid_myrank == 0);
    }

    if((e = vem_reg_func(fun, id)) != CPHVB_SUCCESS)
    {
        *id = 0;
        return e;
    }

    if(strcmp("cphvb_reduce", fun) == 0)
    {
        if(reduce_impl == NULL)
        {
            cphvb_component_get_func(myself, fun, &reduce_impl);
            if (reduce_impl == NULL)
                return CPHVB_USERFUNC_NOT_SUPPORTED;

            reduce_impl_id = *id;
            return CPHVB_SUCCESS;
        }
    }

    return CPHVB_SUCCESS;
}

/* Execute one instruction locally.
 *
 * @opcode   The opcode of the instruction
 * @operands The local operands in the instruction
 * @ufunc  The user-defined function struct when opcode is CPHVB_USERFUNC.
 */
void exec_local_inst(cphvb_opcode opcode, cphvb_array *operands[],
                     cphvb_userfunc *ufunc)
{
    cphvb_error e;
    cphvb_instruction new_inst;
    new_inst.opcode = opcode;
    new_inst.status = CPHVB_INST_PENDING;
    new_inst.userfunc = ufunc;
    if(ufunc == NULL)
    {
        assert(opcode != CPHVB_USERFUNC);
        memcpy(new_inst.operand, operands, cphvb_operands(opcode)
                                           * sizeof(cphvb_array*));
    }

    if((e = exec_vem_execute(1, &new_inst)) != CPHVB_SUCCESS)
        EXCEPT_INST(opcode, e, new_inst.status);
}

/* Execute to instruction locally at the master-process
 *
 * @instruction The instructionto execute
 */
static void fallback_exec(cphvb_instruction *inst)
{
    cphvb_error e;
    int nop = cphvb_operands_in_instruction(inst);
    std::set<cphvb_array*> arys2discard;

    //Gather all data at the master-process
    cphvb_array **oprands = cphvb_inst_operands(inst);
    for(cphvb_intp o=0; o < nop; ++o)
    {
        cphvb_array *op = oprands[o];
        if(cphvb_is_constant(op))
            continue;

        cphvb_array *base = cphvb_base_array(op);
        comm_slaves2master(base);
    }

    //Do global instruction
    if(pgrid_myrank == 0)
    {
        if((e = exec_vem_execute(1, inst)) != CPHVB_SUCCESS)
            EXCEPT_INST(inst->opcode, e, inst->status);
    }

    //Scatter all data back to all processes
    for(cphvb_intp o=0; o < nop; ++o)
    {
        cphvb_array *op = oprands[o];
        if(cphvb_is_constant(op))
            continue;
        cphvb_array *base = cphvb_base_array(op);

        //We have to make sure that the master-process has allocated memory
        //because the slaves cannot determine it.
        if(pgrid_myrank == 0)
            cphvb_data_malloc(base);

        comm_master2slaves(base);

        //All local arrays should be discarded
        arys2discard.insert(op);
        arys2discard.insert(base);
    }
    //Discard all local views
    for(std::set<cphvb_array*>::iterator it=arys2discard.begin();
        it != arys2discard.end(); ++it)
    {
        if((*it)->base != NULL)
        {
            cphvb_array *ops[1] = {*it};
            exec_local_inst(CPHVB_DISCARD, ops, NULL);
        }
    }
    //Free and discard all local base arrays
    for(std::set<cphvb_array*>::iterator it=arys2discard.begin();
        it != arys2discard.end(); ++it)
    {
        if((*it)->base == NULL)
        {
            cphvb_array *ops[1] = {*it};
            exec_local_inst(CPHVB_FREE, ops, NULL);
            exec_local_inst(CPHVB_DISCARD, ops, NULL);
        }
    }
}

/* Execute a regular computation instruction
 *
 * @instruction The regular computation instruction
 */
static void execute_regular(cphvb_instruction *inst)
{
    cphvb_error e;
    std::vector<ary_chunk> chunks;
    int nop = cphvb_operands_in_instruction(inst);
    cphvb_array **operands = cphvb_inst_operands(inst);

    mapping_chunks(nop, operands, chunks);
    assert(chunks.size() > 0);

    //Handle one chunk at a time.
    for(std::vector<ary_chunk>::size_type c=0; c < chunks.size();c += nop)
    {
        assert(cphvb_nelements(chunks[0].ary.ndim, chunks[0].ary.shape) > 0);

        //The process where the output chunk is located will do the computation.
        int owner_rank = chunks[0+c].rank;

        //Create a local instruction based on the array-chunks
        cphvb_instruction local_inst = *inst;
        for(cphvb_intp k=0; k < nop; ++k)
        {
            if(!cphvb_is_constant(inst->operand[k]))
            {
                ary_chunk *chunk = &chunks[k+c];
                local_inst.operand[k] = &chunk->ary;
                comm_array_data(chunk, owner_rank);
            }
        }

        //Check if we should do the computation
        if(pgrid_myrank != owner_rank)
            continue;

        //Apply the local computation
        local_inst.status = CPHVB_INST_PENDING;
        if((e = exec_vem_execute(1, &local_inst)) != CPHVB_SUCCESS)
            EXCEPT_INST(local_inst.opcode, e, local_inst.status);

        //Free and discard all local chunk arrays
        for(cphvb_intp k=0; k < nop; ++k)
        {
            if(cphvb_is_constant(inst->operand[k]))
                continue;

            cphvb_array *ary = &chunks[k+c].ary;
            if(ary->base == NULL)
                exec_local_inst(CPHVB_FREE, &ary, NULL);
            exec_local_inst(CPHVB_DISCARD, &ary, NULL);
        }
    }
}

/* Execute a list of instructions where all operands are global arrays
 *
 * @instruction A list of instructions to execute
 * @return Error codes
 */
cphvb_error exec_execute(cphvb_intp count, cphvb_instruction inst_list[])
{
    if(count <= 0)
        return CPHVB_SUCCESS;

//    cphvb_pprint_instr_list(inst_list, count, "GLOBAL");

    for(cphvb_intp i=0; i < count; ++i)
    {
        cphvb_instruction* inst = &inst_list[i];
        assert(inst->opcode >= 0);
        switch(inst->opcode)
        {
            case CPHVB_USERFUNC:
            {
                if (inst->userfunc->id == reduce_impl_id)
                {
                    //TODO: the cphvb_reduce is hardcoded for now.
                    inst->status = cphvb_reduce(inst->userfunc, NULL);
                    if(inst->status == CPHVB_ERROR)
                        return CPHVB_ERROR;
                    if(inst->status != CPHVB_SUCCESS)
                        return CPHVB_PARTIAL_SUCCESS;
                }
                else
                {
                    fallback_exec(inst);
                }
                break;
            }
            case CPHVB_DISCARD:
            {
                cphvb_array *g_ary = inst->operand[0];
                cphvb_array *l_ary = array_get_existing_local(g_ary);
                if(g_ary->base == NULL)
                {
                    if(l_ary != NULL)
                        exec_local_inst(CPHVB_DISCARD, &l_ary, NULL);
                    array_rm_local(g_ary);
                }
                dispatch_slave_known_remove(g_ary);
                break;
            }
            case CPHVB_FREE:
            {
                cphvb_array *g_ary = cphvb_base_array(inst->operand[0]);
                cphvb_array *l_ary = array_get_existing_local(g_ary);
                cphvb_data_free(g_ary);
                if(l_ary != NULL)
                {
                    exec_local_inst(CPHVB_FREE, &l_ary, NULL);
                }
                break;
            }
            case CPHVB_SYNC:
            {
                cphvb_array *base = cphvb_base_array(inst->operand[0]);
                comm_slaves2master(base);
                break;
            }
            case CPHVB_NONE:
            {
                break;
            }
            default:
            {
                execute_regular(inst);
            }
        }
    }
    return CPHVB_SUCCESS;
}

cphvb_error cphvb_reduce( cphvb_userfunc *arg, void* ve_arg)
{
    cphvb_reduce_type *a = (cphvb_reduce_type *) arg;   // Grab function arguments
    cphvb_opcode opcode = a->opcode;                    // Opcode
    cphvb_index axis    = a->axis;                      // The axis to reduce

    return ufunc_reduce(opcode, axis, a->operand, reduce_impl_id);
}
<|endoftext|>


<commit_before>/** \file    patch_up_ppns_for_k10plus.cc
 *  \brief   Swaps out all persistent old PPN's with new PPN's.
 *  \author  Dr. Johannes Ruscheinski
 */

/*
    Copyright (C) 2019, Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <cstdlib>
#include <cstring>
#include <kchashdb.h>
#include "Compiler.h"
#include "ControlNumberGuesser.h"
#include "DbConnection.h"
#include "DbResultSet.h"
#include "DbRow.h"
#include "FileUtil.h"
#include "MARC.h"
#include "StringUtil.h"
#include "UBTools.h"
#include "util.h"
#include "VuFind.h"

namespace {

const std::string OLD_PPN_LIST_FILE(UBTools::GetTuelibPath() + "alread_replaced_old_ppns.blob");

constexpr size_t OLD_PPN_LENGTH(9);

void LoadAlreadyProcessedPPNs(std::unordered_set<std::string> * const alread_processed_ppns) {
    if (not FileUtil::Exists(OLD_PPN_LIST_FILE))
        return;

    std::string blob;
    FileUtil::ReadStringOrDie(OLD_PPN_LIST_FILE, &blob);
    if (unlikely((blob.length() % OLD_PPN_LENGTH) != 0))
        LOG_ERROR("fractional PPN's are not possible!");

    const size_t count(blob.length() / OLD_PPN_LENGTH);
    alread_processed_ppns->reserve(count);

    for (size_t i(0); i < count; ++i)
        alread_processed_ppns->emplace(blob.substr(i * OLD_PPN_LENGTH, OLD_PPN_LENGTH));
}

void LoadMapping(MARC::Reader * const marc_reader, const std::unordered_set<std::string> &alread_processed_ppns,
                 std::unordered_map<std::string, std::string> * const old_to_new_map)
{
    while (const auto record = marc_reader->read()) {
        for (const auto &field : record.getTagRange("035")) {
            const auto subfield_a(field.getFirstSubfieldWithCode('a'));
            if (StringUtil::StartsWith(subfield_a, "(DE-576)")) {
                const auto old_ppn(subfield_a.substr(__builtin_strlen("(DE-576)")));
                if (alread_processed_ppns.find(old_ppn) == alread_processed_ppns.cend())
                    old_to_new_map->emplace(old_ppn, record.getControlNumber());
            }
        }
    }

    LOG_INFO("Found " + std::to_string(old_to_new_map->size()) + " new mappings of old PPN's to new PPN's in \"" + marc_reader->getPath()
             + "\".\n");
}

void PatchTable(DbConnection * const db_connection, const std::string &table, const std::string &column,
                const std::unordered_map<std::string, std::string> &old_to_new_map)
{
    db_connection->queryOrDie("SELECT DISTINCT " + column + " FROM " + table);
    auto result_set(db_connection->getLastResultSet());
    unsigned replacement_count(0);
    while (const DbRow row = result_set.getNextRow()) {
        const auto old_and_new(old_to_new_map.find(row[column]));
        if (old_and_new != old_to_new_map.cend()) {
            db_connection->queryOrDie("UPDATE IGNORE " + table + " SET " + column + "='" + old_and_new->second
                                      + "' WHERE " + column + "='" + old_and_new->first + "'");
            ++replacement_count;
        }
    }

    LOG_INFO("Replaced " + std::to_string(replacement_count) + " PPN's in ixtheo.keyword_translations.");
}

void StoreNewAlreadyProcessedPPNs(const std::unordered_set<std::string> &alread_processed_ppns,
                                  const std::unordered_map<std::string, std::string> &old_to_new_map)
{
    std::string blob;
    blob.reserve(alread_processed_ppns.size() * OLD_PPN_LENGTH + old_to_new_map.size() * OLD_PPN_LENGTH);

    for (const auto &alread_processed_ppn : alread_processed_ppns)
        blob += alread_processed_ppn;
    for (const auto &old_and_new_ppns : old_to_new_map)
        blob += old_and_new_ppns.first;

    FileUtil::WriteStringOrDie(OLD_PPN_LIST_FILE, blob);
}

void PatchNotifiedDB(const std::string &user_type, const std::unordered_map<std::string, std::string> &old_to_new_map) {
    const std::string DB_FILENAME(UBTools::GetTuelibPath() + user_type + "_notified.db");
    std::unique_ptr<kyotocabinet::HashDB> db(new kyotocabinet::HashDB());
    if (not (db->open(DB_FILENAME,
                      kyotocabinet::HashDB::OWRITER | kyotocabinet::HashDB::OREADER | kyotocabinet::HashDB::OCREATE)))
    {
        LOG_INFO("\"" + DB_FILENAME + "\" not found!");
        return;
    }

    unsigned updated_count(0);
    for (const auto &old_and_new : old_to_new_map) {
        std::string value;
        if (db->get(old_and_new.first, &value)) {
            if (unlikely(not db->remove(old_and_new.first)))
                LOG_ERROR("failed to remove key \"" + old_and_new.first + "\" from \"" + DB_FILENAME + "\"!");
            if (unlikely(not db->add(old_and_new.second, value)))
                LOG_ERROR("failed to add key \"" + old_and_new.second + "\" from \"" + DB_FILENAME + "\"!");
            ++updated_count;
        }
    }

    LOG_INFO("Updated " + std::to_string(updated_count) + " entries in \"" + DB_FILENAME + "\".");
}

} // unnamed namespace

int Main(int argc, char **argv) {
    ::progname = argv[0];

    if (argc < 2)
        ::Usage("marc_input1 [marc_input2 .. marc_inputN]");

    std::unordered_set<std::string> alread_processed_ppns;
    LoadAlreadyProcessedPPNs(&alread_processed_ppns);

    std::unordered_map<std::string, std::string> old_to_new_map;
    for (int arg_no(1); arg_no < argc; ++arg_no) {
        const auto marc_reader(MARC::Reader::Factory(argv[arg_no]));
        LoadMapping(marc_reader.get(), alread_processed_ppns, &old_to_new_map);
    }
    if (old_to_new_map.empty()) {
        LOG_INFO("nothing to do!");
        return EXIT_SUCCESS;
    }

    PatchNotifiedDB("ixtheo", old_to_new_map);
    PatchNotifiedDB("relbib", old_to_new_map);

    ControlNumberGuesser control_number_guesser;
    control_number_guesser.swapControlNumbers(old_to_new_map);

    std::string mysql_url;
    VuFind::GetMysqlURL(&mysql_url);
    DbConnection db_connection(mysql_url);

    PatchTable(&db_connection, "vufind.resource", "record_id", old_to_new_map);
    PatchTable(&db_connection, "vufind.record", "record_id", old_to_new_map);
    PatchTable(&db_connection, "vufind.change_tracker", "id", old_to_new_map);
    if (VuFind::GetTueFindFlavour() == "ixtheo") {
        PatchTable(&db_connection, "ixtheo.keyword_translations", "ppn", old_to_new_map);
        PatchTable(&db_connection, "vufind.ixtheo_journal_subscriptions", "journal_control_number_or_bundle_name", old_to_new_map);
        PatchTable(&db_connection, "vufind.ixtheo_pda_subscriptions", "book_ppn", old_to_new_map);
        PatchTable(&db_connection, "vufind.relbib_ids", "record_id", old_to_new_map);
        PatchTable(&db_connection, "vufind.bibstudies_ids", "record_id", old_to_new_map);
    } else {
        PatchTable(&db_connection, "vufind.full_text_cache", "id", old_to_new_map);
        PatchTable(&db_connection, "vufind.full_text_cache_urls", "id", old_to_new_map);
    }

    StoreNewAlreadyProcessedPPNs(alread_processed_ppns, old_to_new_map);

    return EXIT_SUCCESS;
}
<commit_msg>Obvious fix.<commit_after>/** \file    patch_up_ppns_for_k10plus.cc
 *  \brief   Swaps out all persistent old PPN's with new PPN's.
 *  \author  Dr. Johannes Ruscheinski
 */

/*
    Copyright (C) 2019, Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <cstdlib>
#include <cstring>
#include <kchashdb.h>
#include "Compiler.h"
#include "ControlNumberGuesser.h"
#include "DbConnection.h"
#include "DbResultSet.h"
#include "DbRow.h"
#include "FileUtil.h"
#include "MARC.h"
#include "StringUtil.h"
#include "UBTools.h"
#include "util.h"
#include "VuFind.h"

namespace {

const std::string OLD_PPN_LIST_FILE(UBTools::GetTuelibPath() + "alread_replaced_old_ppns.blob");

constexpr size_t OLD_PPN_LENGTH(9);

void LoadAlreadyProcessedPPNs(std::unordered_set<std::string> * const alread_processed_ppns) {
    if (not FileUtil::Exists(OLD_PPN_LIST_FILE))
        return;

    std::string blob;
    FileUtil::ReadStringOrDie(OLD_PPN_LIST_FILE, &blob);
    if (unlikely((blob.length() % OLD_PPN_LENGTH) != 0))
        LOG_ERROR("fractional PPN's are not possible!");

    const size_t count(blob.length() / OLD_PPN_LENGTH);
    alread_processed_ppns->reserve(count);

    for (size_t i(0); i < count; ++i)
        alread_processed_ppns->emplace(blob.substr(i * OLD_PPN_LENGTH, OLD_PPN_LENGTH));
}

void LoadMapping(MARC::Reader * const marc_reader, const std::unordered_set<std::string> &alread_processed_ppns,
                 std::unordered_map<std::string, std::string> * const old_to_new_map)
{
    while (const auto record = marc_reader->read()) {
        for (const auto &field : record.getTagRange("035")) {
            const auto subfield_a(field.getFirstSubfieldWithCode('a'));
            if (StringUtil::StartsWith(subfield_a, "(DE-576)")) {
                const auto old_ppn(subfield_a.substr(__builtin_strlen("(DE-576)")));
                if (alread_processed_ppns.find(old_ppn) == alread_processed_ppns.cend())
                    old_to_new_map->emplace(old_ppn, record.getControlNumber());
            }
        }
    }

    LOG_INFO("Found " + std::to_string(old_to_new_map->size()) + " new mappings of old PPN's to new PPN's in \"" + marc_reader->getPath()
             + "\".\n");
}

void PatchTable(DbConnection * const db_connection, const std::string &table, const std::string &column,
                const std::unordered_map<std::string, std::string> &old_to_new_map)
{
    db_connection->queryOrDie("SELECT DISTINCT " + column + " FROM " + table);
    auto result_set(db_connection->getLastResultSet());
    unsigned replacement_count(0);
    while (const DbRow row = result_set.getNextRow()) {
        const auto old_and_new(old_to_new_map.find(row[column]));
        if (old_and_new != old_to_new_map.cend()) {
            db_connection->queryOrDie("UPDATE IGNORE " + table + " SET " + column + "='" + old_and_new->second
                                      + "' WHERE " + column + "='" + old_and_new->first + "'");
            ++replacement_count;
        }
    }

    LOG_INFO("Replaced " + std::to_string(replacement_count) + " PPN's in ixtheo.keyword_translations.");
}

void StoreNewAlreadyProcessedPPNs(const std::unordered_set<std::string> &alread_processed_ppns,
                                  const std::unordered_map<std::string, std::string> &old_to_new_map)
{
    std::string blob;
    blob.reserve(alread_processed_ppns.size() * OLD_PPN_LENGTH + old_to_new_map.size() * OLD_PPN_LENGTH);

    for (const auto &alread_processed_ppn : alread_processed_ppns)
        blob += alread_processed_ppn;
    for (const auto &old_and_new_ppns : old_to_new_map)
        blob += old_and_new_ppns.first;

    FileUtil::WriteStringOrDie(OLD_PPN_LIST_FILE, blob);
}

void PatchNotifiedDB(const std::string &user_type, const std::unordered_map<std::string, std::string> &old_to_new_map) {
    const std::string DB_FILENAME(UBTools::GetTuelibPath() + user_type + "_notified.db");
    std::unique_ptr<kyotocabinet::HashDB> db(new kyotocabinet::HashDB());
    if (not (db->open(DB_FILENAME, kyotocabinet::HashDB::OWRITER | kyotocabinet::HashDB::OREADER))) {
        LOG_INFO("\"" + DB_FILENAME + "\" not found!");
        return;
    }

    unsigned updated_count(0);
    for (const auto &old_and_new : old_to_new_map) {
        std::string value;
        if (db->get(old_and_new.first, &value)) {
            if (unlikely(not db->remove(old_and_new.first)))
                LOG_ERROR("failed to remove key \"" + old_and_new.first + "\" from \"" + DB_FILENAME + "\"!");
            if (unlikely(not db->add(old_and_new.second, value)))
                LOG_ERROR("failed to add key \"" + old_and_new.second + "\" from \"" + DB_FILENAME + "\"!");
            ++updated_count;
        }
    }

    LOG_INFO("Updated " + std::to_string(updated_count) + " entries in \"" + DB_FILENAME + "\".");
}

} // unnamed namespace

int Main(int argc, char **argv) {
    ::progname = argv[0];

    if (argc < 2)
        ::Usage("marc_input1 [marc_input2 .. marc_inputN]");

    std::unordered_set<std::string> alread_processed_ppns;
    LoadAlreadyProcessedPPNs(&alread_processed_ppns);

    std::unordered_map<std::string, std::string> old_to_new_map;
    for (int arg_no(1); arg_no < argc; ++arg_no) {
        const auto marc_reader(MARC::Reader::Factory(argv[arg_no]));
        LoadMapping(marc_reader.get(), alread_processed_ppns, &old_to_new_map);
    }
    if (old_to_new_map.empty()) {
        LOG_INFO("nothing to do!");
        return EXIT_SUCCESS;
    }

    PatchNotifiedDB("ixtheo", old_to_new_map);
    PatchNotifiedDB("relbib", old_to_new_map);

    ControlNumberGuesser control_number_guesser;
    control_number_guesser.swapControlNumbers(old_to_new_map);

    std::string mysql_url;
    VuFind::GetMysqlURL(&mysql_url);
    DbConnection db_connection(mysql_url);

    PatchTable(&db_connection, "vufind.resource", "record_id", old_to_new_map);
    PatchTable(&db_connection, "vufind.record", "record_id", old_to_new_map);
    PatchTable(&db_connection, "vufind.change_tracker", "id", old_to_new_map);
    if (VuFind::GetTueFindFlavour() == "ixtheo") {
        PatchTable(&db_connection, "ixtheo.keyword_translations", "ppn", old_to_new_map);
        PatchTable(&db_connection, "vufind.ixtheo_journal_subscriptions", "journal_control_number_or_bundle_name", old_to_new_map);
        PatchTable(&db_connection, "vufind.ixtheo_pda_subscriptions", "book_ppn", old_to_new_map);
        PatchTable(&db_connection, "vufind.relbib_ids", "record_id", old_to_new_map);
        PatchTable(&db_connection, "vufind.bibstudies_ids", "record_id", old_to_new_map);
    } else {
        PatchTable(&db_connection, "vufind.full_text_cache", "id", old_to_new_map);
        PatchTable(&db_connection, "vufind.full_text_cache_urls", "id", old_to_new_map);
    }

    StoreNewAlreadyProcessedPPNs(alread_processed_ppns, old_to_new_map);

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before><commit_msg>update system path<commit_after><|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#ifndef TAC_OPERATOR_H
#define TAC_OPERATOR_H

#include "ast/Operator.hpp"

namespace eddic {

namespace tac {

enum class Operator : unsigned int {
    /* Assignment operators  */
    ASSIGN,
    FASSIGN,

    /* Integer operators */
    ADD,
    SUB,
    MUL,
    DIV,
    MOD,

    /* Float operators  */
    FADD,
    FSUB,
    FMUL,
    FDIV,

    /* relational operators */
    EQUALS,
    NOT_EQUALS,
    GREATER,
    GREATER_EQUALS,
    LESS,
    LESS_EQUALS,

    MINUS,          //result = -arg1

    DOT,            //result = (arg1)+arg2
    DOT_ASSIGN,     //result+arg1=arg2

    ARRAY,          //result=arg1[arg2]
    ARRAY_ASSIGN,   //result[arg1]=arg2

    PARAM,          //push a single value

    RETURN          //return from a function
};

tac::Operator toOperator(ast::Operator op);
tac::Operator toFloatOperator(ast::Operator op);

} //end of tac

} //end of eddic

#endif
<commit_msg>Add new TAC operators for comparisons on float<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#ifndef TAC_OPERATOR_H
#define TAC_OPERATOR_H

#include "ast/Operator.hpp"

namespace eddic {

namespace tac {

enum class Operator : unsigned int {
    /* Assignment operators  */
    ASSIGN,
    FASSIGN,

    /* Integer operators */
    ADD,
    SUB,
    MUL,
    DIV,
    MOD,

    /* Float operators  */
    FADD,
    FSUB,
    FMUL,
    FDIV,

    /* relational operators */
    EQUALS,
    NOT_EQUALS,
    GREATER,
    GREATER_EQUALS,
    LESS,
    LESS_EQUALS,

    /* float relational operators */
    FE,
    FNE,
    FG,
    FGE,
    FLE,
    FL,

    MINUS,          //result = -arg1

    DOT,            //result = (arg1)+arg2
    DOT_ASSIGN,     //result+arg1=arg2

    ARRAY,          //result=arg1[arg2]
    ARRAY_ASSIGN,   //result[arg1]=arg2

    PARAM,          //push a single value

    RETURN          //return from a function
};

tac::Operator toOperator(ast::Operator op);
tac::Operator toFloatOperator(ast::Operator op);

} //end of tac

} //end of eddic

#endif
<|endoftext|>


<commit_before>#include <iostream>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <sstream>
#include <algorithm>

#include <getopt.h>

// This is the JSON header
#include "jsoncpp/json/json.h"

#include <mosquittopp.h>

#include "sysfs_gpio.h"
#include "common/utils.h"
#include "common/mqtt_wrapper.h"
#include<chrono>
#include<thread>

using namespace std;
using  std::chrono::duration_cast;
using  std::chrono::milliseconds;
using  std::chrono::steady_clock;

enum class TGpioDirection {
    Input,
    Output
};

struct TGpioDesc
{
    int Gpio;
    bool Inverted = false;
    string Name = "";
    TGpioDirection Direction = TGpioDirection::Output;
    string InterruptEdge = "";
    string Type = "";
    int Multiplier;
    int Order;
};

class THandlerConfig
{
    public:
        vector<TGpioDesc> Gpios;
        void AddGpio(TGpioDesc& gpio_desc) { Gpios.push_back(gpio_desc); };

        string DeviceName;

};

typedef pair<TGpioDesc, std::shared_ptr<TSysfsGpio>> TChannelDesc;

bool FuncComp( const TChannelDesc& a, const TChannelDesc& b)
    { return (a.first.Order < b.first.Order); }
class TMQTTGpioHandler : public TMQTTWrapper
{

	public:
        TMQTTGpioHandler(const TMQTTGpioHandler::TConfig& mqtt_config, const THandlerConfig& handler_config);
		~TMQTTGpioHandler();

		void OnConnect(int rc);
		void OnMessage(const struct mosquitto_message *message);
		void OnSubscribe(int mid, int qos_count, const int *granted_qos);

        void UpdateChannelValues();
        void InitInterrupts(int epfd);// look through all gpios and select Interrupt supporting ones
        string GetChannelTopic(const TGpioDesc& gpio_desc);
        void CatchInterrupts(int count, struct epoll_event* events);
        void PublishValue(const TGpioDesc& gpio_desc, std::shared_ptr<TSysfsGpio> gpio_handler);
        bool FirstTime = true;

    private:
        THandlerConfig Config;
        vector<TChannelDesc> Channels;

        void UpdateValue(const TGpioDesc& gpio_desc,std::shared_ptr<TSysfsGpio> gpio_handler);

};

TMQTTGpioHandler::TMQTTGpioHandler(const TMQTTGpioHandler::TConfig& mqtt_config, const THandlerConfig& handler_config)
    : TMQTTWrapper(mqtt_config)
    , Config(handler_config)
{
    // init gpios
    for (const TGpioDesc& gpio_desc : handler_config.Gpios) {
        std::shared_ptr<TSysfsGpio> gpio_handler(nullptr);
        if (gpio_desc.Type == "")
                gpio_handler.reset( new TSysfsGpio(gpio_desc.Gpio, gpio_desc.Inverted, gpio_desc.InterruptEdge));
        else
            gpio_handler.reset( new TSysfsGpioBaseCounter(gpio_desc.Gpio, gpio_desc.Inverted, gpio_desc.InterruptEdge,  gpio_desc.Type, gpio_desc.Multiplier));
        gpio_handler->Export();
        if (gpio_handler->IsExported()) {
            if (gpio_desc.Direction == TGpioDirection::Input)
                gpio_handler->SetInput();
            else
                gpio_handler->SetOutput();
            Channels.emplace_back(gpio_desc, gpio_handler);
            } else {
                    cerr << "ERROR: unable to export gpio " << gpio_desc.Gpio << endl;
            }
    }
    sort(Channels.begin(), Channels.end(), FuncComp);
	Connect();
}

TMQTTGpioHandler::~TMQTTGpioHandler() {}

void TMQTTGpioHandler::OnConnect(int rc)
{
	printf("Connected with code %d.\n", rc);
	if(rc == 0){
		/* Only attempt to Subscribe on a successful connect. */
        string prefix = string("/devices/") + MQTTConfig.Id + "/";

        // Meta
        Publish(NULL, prefix + "/meta/name", Config.DeviceName, 0, true);

        for (const auto& channel_desc : Channels) {
            const auto& gpio_desc = channel_desc.first;

            //~ cout << "GPIO: " << gpio_desc.Name << endl;
            string control_prefix = prefix + "controls/" + gpio_desc.Name;
            std::shared_ptr<TSysfsGpio> gpio_handler = channel_desc.second;
            Publish(NULL, control_prefix + "/meta/order", to_string(gpio_desc.Order), 0, true);
            vector<TPublishPair> what_to_publish (gpio_handler->MetaType());
            for (TPublishPair tmp : what_to_publish)
                Publish(NULL, control_prefix + tmp.first + "/meta/type", tmp.second, 0, true);
            if (gpio_desc.Direction == TGpioDirection::Input)
                Publish(NULL, control_prefix + "/meta/readonly", "1", 0, true);
            else
                Subscribe(NULL, control_prefix + "/on");
        }
//~ /devices/293723-demo/controls/Demo-Switch 0
//~ /devices/293723-demo/controls/Demo-Switch/on 1
//~ /devices/293723-demo/controls/Demo-Switch/meta/type switch

	}
}

void TMQTTGpioHandler::OnMessage(const struct mosquitto_message *message)
{
    string topic = message->topic;
    string payload = static_cast<const char *>(message->payload);

    const vector<string>& tokens = StringSplit(topic, '/');

    if (  (tokens.size() == 6) &&
          (tokens[0] == "") && (tokens[1] == "devices") &&
          (tokens[2] == MQTTConfig.Id) && (tokens[3] == "controls") &&
          (tokens[5] == "on") )
    {
        for (TChannelDesc& channel_desc : Channels) {
            const auto& gpio_desc = channel_desc.first;
            if (gpio_desc.Direction != TGpioDirection::Output)
                continue;

            if (tokens[4] == gpio_desc.Name) {
                auto& gpio_handler = *channel_desc.second;

                int val = payload == "0" ? 0 : 1;
                if (gpio_handler.SetValue(val) == 0) {
                    // echo, retained
                    Publish(NULL, GetChannelTopic(gpio_desc), payload, 0, true);
                }else {
                    cerr << "DEBUG : couldn't set value" << endl;
                    }
            }
        }
    }
}

void TMQTTGpioHandler::OnSubscribe(int mid, int qos_count, const int *granted_qos)
{
	printf("Subscription succeeded.\n");
}

string TMQTTGpioHandler::GetChannelTopic(const TGpioDesc& gpio_desc)
{
    static string controls_prefix = string("/devices/") + MQTTConfig.Id + "/controls/";
    return (controls_prefix + gpio_desc.Name);
}

void TMQTTGpioHandler::UpdateValue(const TGpioDesc& gpio_desc, std::shared_ptr<TSysfsGpio> gpio_handler)
{
        // look at previous value and compare it with current
        int cached = gpio_handler->GetCachedValue();
        int value = gpio_handler->GetValue();
        if (value >= 0) {
            // Buggy GPIO driver may yield any non-zero number instead of 1,
            // so make sure it's either 1 or 0 here.
            // See https://github.com/torvalds/linux/commit/25b35da7f4cce82271859f1b6eabd9f3bd41a2bb
            value = !!value;
            if ((cached < 0) || (cached != value)){
                gpio_handler->SetCachedValue(cached);
                PublishValue(gpio_desc, gpio_handler);
                }
            }
}
void TMQTTGpioHandler::PublishValue(const TGpioDesc& gpio_desc, std::shared_ptr<TSysfsGpio> gpio_handler)
{
    vector<TPublishPair> what_to_publish(gpio_handler->GpioPublish()); //gets nessesary to publish with updating value
    for (TPublishPair& publish_element: what_to_publish){
        string to_topic = publish_element.first;
        string value = publish_element.second;
        Publish(NULL, GetChannelTopic(gpio_desc) + to_topic, value, 0, true); // Publish current value (make retained)
        }
}
void TMQTTGpioHandler::UpdateChannelValues()
{
    for (TChannelDesc& channel_desc : Channels) {
        const auto& gpio_desc = channel_desc.first;
        std::shared_ptr<TSysfsGpio> gpio_handler = channel_desc.second;
        UpdateValue(gpio_desc,gpio_handler);
    }
}

void TMQTTGpioHandler::InitInterrupts(int epfd)
{
    int n;
    for ( TChannelDesc& channel_desc : Channels) {
        const auto& gpio_desc = channel_desc.first;
        auto& gpio_handler = *channel_desc.second;
        // check if file edge exists and is direction input
        gpio_handler.InterruptUp();
        if (gpio_handler.GetInterruptSupport()) {
             n = epoll_ctl(epfd,EPOLL_CTL_ADD,gpio_handler.GetFileDes(),&gpio_handler.GetEpollStruct());// adding new instance to epoll
            if (n != 0 ) {
                cerr<<"epoll_ctl gained error with GPIO"<<gpio_desc.Gpio<<endl;
            }
        }
    }

}

void TMQTTGpioHandler::CatchInterrupts(int count, struct epoll_event* events)
{
    int i;
    for ( auto& channel_desc : Channels) {
        const auto& gpio_desc = channel_desc.first;
        std::shared_ptr<TSysfsGpio> gpio_handler = channel_desc.second;
        for (i=0; i < count; i++){
            if (gpio_handler->GetFileDes() == events[i].data.fd) {
                if (!gpio_handler->IsDebouncing()){
                    PublishValue(gpio_desc, gpio_handler);
                }
            }
        }
    }
    //std::this_thread::sleep_for(std::chrono::milliseconds(10));//avoid debouncing

}

int main(int argc, char *argv[])
{
	int rc;
    THandlerConfig handler_config;
    TMQTTGpioHandler::TConfig mqtt_config;
    mqtt_config.Host = "localhost";
    mqtt_config.Port = 1883;
    string config_fname;
    int epfd;
    struct epoll_event events[20];

    int c,n;
    //~ int digit_optind = 0;
    //~ int aopt = 0, bopt = 0;
    //~ char *copt = 0, *dopt = 0;
    while ( (c = getopt(argc, argv, "c:h:p:")) != -1) {
        //~ int this_option_optind = optind ? optind : 1;
        switch (c) {
        case 'c':
            printf ("option c with value '%s'\n", optarg);
            config_fname = optarg;
            break;
        case 'p':
            printf ("option p with value '%s'\n", optarg);
            mqtt_config.Port = stoi(optarg);
            break;
        case 'h':
            printf ("option h with value '%s'\n", optarg);
            mqtt_config.Host = optarg;
            break;
        case '?':
            break;
        default:
            printf ("?? Getopt returned character code 0%o ??\n", c);
        }
    }
    //~ if (optind < argc) {
        //~ printf ("non-option ARGV-elements: ");
        //~ while (optind < argc)
            //~ printf ("%s ", argv[optind++]);
        //~ printf ("\n");
    //~ }

    {
        // Let's parse it
        Json::Value root;
        Json::Reader reader;

        if (config_fname.empty()) {
            cerr << "Please specify config file with -c option" << endl;
            return 1;
        }

        ifstream myfile (config_fname);

        bool parsedSuccess = reader.parse(myfile,
                                       root,
                                       false);

        if(not parsedSuccess)
        {
            // Report failures and their locations
            // in the document.
            cerr << "Failed to parse JSON" << endl
               << reader.getFormatedErrorMessages()
               << endl;
            return 1;
        }

        handler_config.DeviceName = root["device_name"].asString();

         // Let's extract the array contained
         // in the root object
        const auto& array = root["channels"];

         // Iterate over sequence elements and
         // print its values
        for(unsigned int index=0; index<array.size();
             ++index)
        {
            const auto& item = array[index];
            TGpioDesc gpio_desc;
            gpio_desc.Gpio = item["gpio"].asInt();
            gpio_desc.Name = item["name"].asString();
            if (item.isMember("inverted"))
                gpio_desc.Inverted = item["inverted"].asBool();
            if (item.isMember("direction") && item["direction"].asString() == "input")
                gpio_desc.Direction = TGpioDirection::Input;
            if (item.isMember("type"))
                    gpio_desc.Type = item["type"].asString();
            if (item.isMember("multiplier"))
                gpio_desc.Multiplier = item["multiplier"].asInt();
            if (item.isMember("edge"))
                gpio_desc.InterruptEdge = item["edge"].asString();
            gpio_desc.Order = index;
            handler_config.AddGpio(gpio_desc);

        }
    }

	mosqpp::lib_init();

    mqtt_config.Id = "wb-gpio";
    std::shared_ptr<TMQTTGpioHandler> mqtt_handler(new TMQTTGpioHandler(mqtt_config, handler_config));
    mqtt_handler->Init();

    rc= mqtt_handler->loop_start();
    if (rc != 0 ) {
        cerr << "couldn't start mosquitto_loop_start ! " << rc << endl;
    }else {
        epfd = epoll_create(1);// creating epoll for Interrupts
        mqtt_handler->InitInterrupts(epfd);
        steady_clock::time_point start;
        int interval;
        start = steady_clock::now();
        while(1){
            n = epoll_wait(epfd,events,20,500);
            interval = duration_cast<milliseconds>(steady_clock::now() - start).count() ;
            if (interval >= 500 ) {  //checking is it time to look through all gpios
                mqtt_handler->UpdateChannelValues();
                start = steady_clock::now();
            }else {
                if (mqtt_handler->FirstTime && interval == 0) {
                    mqtt_handler->FirstTime = false;
                    continue;
                }
                mqtt_handler->CatchInterrupts( n, events );
            }
        }
	}

	mosqpp::lib_cleanup();

	return 0;
}
//build-dep libmosquittopp-dev libmosquitto-dev
// dep: libjsoncpp0 libmosquittopp libmosquitto

// 2420 2032
// 6008 2348 1972
<commit_msg>added two orders per control<commit_after>#include <iostream>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <sstream>
#include <algorithm>

#include <getopt.h>

// This is the JSON header
#include "jsoncpp/json/json.h"

#include <mosquittopp.h>

#include "sysfs_gpio.h"
#include "common/utils.h"
#include "common/mqtt_wrapper.h"
#include<chrono>
#include<thread>

using namespace std;
using  std::chrono::duration_cast;
using  std::chrono::milliseconds;
using  std::chrono::steady_clock;

enum class TGpioDirection {
    Input,
    Output
};

struct TGpioDesc
{
    int Gpio;
    bool Inverted = false;
    string Name = "";
    TGpioDirection Direction = TGpioDirection::Output;
    string InterruptEdge = "";
    string Type = "";
    int Multiplier;
    int Order;
};

class THandlerConfig
{
    public:
        vector<TGpioDesc> Gpios;
        void AddGpio(TGpioDesc& gpio_desc) { Gpios.push_back(gpio_desc); };

        string DeviceName;

};

typedef pair<TGpioDesc, std::shared_ptr<TSysfsGpio>> TChannelDesc;

bool FuncComp( const TChannelDesc& a, const TChannelDesc& b)
    { return (a.first.Order < b.first.Order); }
class TMQTTGpioHandler : public TMQTTWrapper
{

	public:
        TMQTTGpioHandler(const TMQTTGpioHandler::TConfig& mqtt_config, const THandlerConfig& handler_config);
		~TMQTTGpioHandler();

		void OnConnect(int rc);
		void OnMessage(const struct mosquitto_message *message);
		void OnSubscribe(int mid, int qos_count, const int *granted_qos);

        void UpdateChannelValues();
        void InitInterrupts(int epfd);// look through all gpios and select Interrupt supporting ones
        string GetChannelTopic(const TGpioDesc& gpio_desc);
        void CatchInterrupts(int count, struct epoll_event* events);
        void PublishValue(const TGpioDesc& gpio_desc, std::shared_ptr<TSysfsGpio> gpio_handler);
        bool FirstTime = true;

    private:
        THandlerConfig Config;
        vector<TChannelDesc> Channels;

        void UpdateValue(const TGpioDesc& gpio_desc,std::shared_ptr<TSysfsGpio> gpio_handler);

};

TMQTTGpioHandler::TMQTTGpioHandler(const TMQTTGpioHandler::TConfig& mqtt_config, const THandlerConfig& handler_config)
    : TMQTTWrapper(mqtt_config)
    , Config(handler_config)
{
    // init gpios
    for (const TGpioDesc& gpio_desc : handler_config.Gpios) {
        std::shared_ptr<TSysfsGpio> gpio_handler(nullptr);
        if (gpio_desc.Type == "")
                gpio_handler.reset( new TSysfsGpio(gpio_desc.Gpio, gpio_desc.Inverted, gpio_desc.InterruptEdge));
        else
            gpio_handler.reset( new TSysfsGpioBaseCounter(gpio_desc.Gpio, gpio_desc.Inverted, gpio_desc.InterruptEdge,  gpio_desc.Type, gpio_desc.Multiplier));
        gpio_handler->Export();
        if (gpio_handler->IsExported()) {
            if (gpio_desc.Direction == TGpioDirection::Input)
                gpio_handler->SetInput();
            else
                gpio_handler->SetOutput();
            Channels.emplace_back(gpio_desc, gpio_handler);
            } else {
                    cerr << "ERROR: unable to export gpio " << gpio_desc.Gpio << endl;
            }
    }
    sort(Channels.begin(), Channels.end(), FuncComp);
	Connect();
}

TMQTTGpioHandler::~TMQTTGpioHandler() {}

void TMQTTGpioHandler::OnConnect(int rc)
{
	printf("Connected with code %d.\n", rc);
	if(rc == 0){
		/* Only attempt to Subscribe on a successful connect. */
        string prefix = string("/devices/") + MQTTConfig.Id + "/";

        // Meta
        Publish(NULL, prefix + "/meta/name", Config.DeviceName, 0, true);

        for (const auto& channel_desc : Channels) {
            const auto& gpio_desc = channel_desc.first;

            //~ cout << "GPIO: " << gpio_desc.Name << endl;
            string control_prefix = prefix + "controls/" + gpio_desc.Name;
            std::shared_ptr<TSysfsGpio> gpio_handler = channel_desc.second;
            vector<TPublishPair> what_to_publish (gpio_handler->MetaType());
            int order = gpio_desc.Order * 2;
            for (TPublishPair tmp : what_to_publish) {
                Publish(NULL, control_prefix + "/meta/order", to_string(order), 0, true);
                Publish(NULL, control_prefix + tmp.first + "/meta/type", tmp.second, 0, true);
                order++;
            }
            if (gpio_desc.Direction == TGpioDirection::Input)
                Publish(NULL, control_prefix + "/meta/readonly", "1", 0, true);
            else
                Subscribe(NULL, control_prefix + "/on");
        }
//~ /devices/293723-demo/controls/Demo-Switch 0
//~ /devices/293723-demo/controls/Demo-Switch/on 1
//~ /devices/293723-demo/controls/Demo-Switch/meta/type switch

	}
}

void TMQTTGpioHandler::OnMessage(const struct mosquitto_message *message)
{
    string topic = message->topic;
    string payload = static_cast<const char *>(message->payload);

    const vector<string>& tokens = StringSplit(topic, '/');

    if (  (tokens.size() == 6) &&
          (tokens[0] == "") && (tokens[1] == "devices") &&
          (tokens[2] == MQTTConfig.Id) && (tokens[3] == "controls") &&
          (tokens[5] == "on") )
    {
        for (TChannelDesc& channel_desc : Channels) {
            const auto& gpio_desc = channel_desc.first;
            if (gpio_desc.Direction != TGpioDirection::Output)
                continue;

            if (tokens[4] == gpio_desc.Name) {
                auto& gpio_handler = *channel_desc.second;

                int val = payload == "0" ? 0 : 1;
                if (gpio_handler.SetValue(val) == 0) {
                    // echo, retained
                    Publish(NULL, GetChannelTopic(gpio_desc), payload, 0, true);
                }else {
                    cerr << "DEBUG : couldn't set value" << endl;
                    }
            }
        }
    }
}

void TMQTTGpioHandler::OnSubscribe(int mid, int qos_count, const int *granted_qos)
{
	printf("Subscription succeeded.\n");
}

string TMQTTGpioHandler::GetChannelTopic(const TGpioDesc& gpio_desc)
{
    static string controls_prefix = string("/devices/") + MQTTConfig.Id + "/controls/";
    return (controls_prefix + gpio_desc.Name);
}

void TMQTTGpioHandler::UpdateValue(const TGpioDesc& gpio_desc, std::shared_ptr<TSysfsGpio> gpio_handler)
{
        // look at previous value and compare it with current
        int cached = gpio_handler->GetCachedValue();
        int value = gpio_handler->GetValue();
        if (value >= 0) {
            // Buggy GPIO driver may yield any non-zero number instead of 1,
            // so make sure it's either 1 or 0 here.
            // See https://github.com/torvalds/linux/commit/25b35da7f4cce82271859f1b6eabd9f3bd41a2bb
            value = !!value;
            if ((cached < 0) || (cached != value)){
                gpio_handler->SetCachedValue(cached);
                PublishValue(gpio_desc, gpio_handler);
                }
            }
}
void TMQTTGpioHandler::PublishValue(const TGpioDesc& gpio_desc, std::shared_ptr<TSysfsGpio> gpio_handler)
{
    vector<TPublishPair> what_to_publish(gpio_handler->GpioPublish()); //gets nessesary to publish with updating value
    for (TPublishPair& publish_element: what_to_publish){
        string to_topic = publish_element.first;
        string value = publish_element.second;
        Publish(NULL, GetChannelTopic(gpio_desc) + to_topic, value, 0, true); // Publish current value (make retained)
        }
}
void TMQTTGpioHandler::UpdateChannelValues()
{
    for (TChannelDesc& channel_desc : Channels) {
        const auto& gpio_desc = channel_desc.first;
        std::shared_ptr<TSysfsGpio> gpio_handler = channel_desc.second;
        UpdateValue(gpio_desc,gpio_handler);
    }
}

void TMQTTGpioHandler::InitInterrupts(int epfd)
{
    int n;
    for ( TChannelDesc& channel_desc : Channels) {
        const auto& gpio_desc = channel_desc.first;
        auto& gpio_handler = *channel_desc.second;
        // check if file edge exists and is direction input
        gpio_handler.InterruptUp();
        if (gpio_handler.GetInterruptSupport()) {
             n = epoll_ctl(epfd,EPOLL_CTL_ADD,gpio_handler.GetFileDes(),&gpio_handler.GetEpollStruct());// adding new instance to epoll
            if (n != 0 ) {
                cerr<<"epoll_ctl gained error with GPIO"<<gpio_desc.Gpio<<endl;
            }
        }
    }

}

void TMQTTGpioHandler::CatchInterrupts(int count, struct epoll_event* events)
{
    int i;
    for ( auto& channel_desc : Channels) {
        const auto& gpio_desc = channel_desc.first;
        std::shared_ptr<TSysfsGpio> gpio_handler = channel_desc.second;
        for (i=0; i < count; i++){
            if (gpio_handler->GetFileDes() == events[i].data.fd) {
                if (!gpio_handler->IsDebouncing()){
                    PublishValue(gpio_desc, gpio_handler);
                }
            }
        }
    }
    //std::this_thread::sleep_for(std::chrono::milliseconds(10));//avoid debouncing

}

int main(int argc, char *argv[])
{
	int rc;
    THandlerConfig handler_config;
    TMQTTGpioHandler::TConfig mqtt_config;
    mqtt_config.Host = "localhost";
    mqtt_config.Port = 1883;
    string config_fname;
    int epfd;
    struct epoll_event events[20];

    int c,n;
    //~ int digit_optind = 0;
    //~ int aopt = 0, bopt = 0;
    //~ char *copt = 0, *dopt = 0;
    while ( (c = getopt(argc, argv, "c:h:p:")) != -1) {
        //~ int this_option_optind = optind ? optind : 1;
        switch (c) {
        case 'c':
            printf ("option c with value '%s'\n", optarg);
            config_fname = optarg;
            break;
        case 'p':
            printf ("option p with value '%s'\n", optarg);
            mqtt_config.Port = stoi(optarg);
            break;
        case 'h':
            printf ("option h with value '%s'\n", optarg);
            mqtt_config.Host = optarg;
            break;
        case '?':
            break;
        default:
            printf ("?? Getopt returned character code 0%o ??\n", c);
        }
    }
    //~ if (optind < argc) {
        //~ printf ("non-option ARGV-elements: ");
        //~ while (optind < argc)
            //~ printf ("%s ", argv[optind++]);
        //~ printf ("\n");
    //~ }

    {
        // Let's parse it
        Json::Value root;
        Json::Reader reader;

        if (config_fname.empty()) {
            cerr << "Please specify config file with -c option" << endl;
            return 1;
        }

        ifstream myfile (config_fname);

        bool parsedSuccess = reader.parse(myfile,
                                       root,
                                       false);

        if(not parsedSuccess)
        {
            // Report failures and their locations
            // in the document.
            cerr << "Failed to parse JSON" << endl
               << reader.getFormatedErrorMessages()
               << endl;
            return 1;
        }

        handler_config.DeviceName = root["device_name"].asString();

         // Let's extract the array contained
         // in the root object
        const auto& array = root["channels"];

         // Iterate over sequence elements and
         // print its values
        for(unsigned int index=0; index<array.size();
             ++index)
        {
            const auto& item = array[index];
            TGpioDesc gpio_desc;
            gpio_desc.Gpio = item["gpio"].asInt();
            gpio_desc.Name = item["name"].asString();
            if (item.isMember("inverted"))
                gpio_desc.Inverted = item["inverted"].asBool();
            if (item.isMember("direction") && item["direction"].asString() == "input")
                gpio_desc.Direction = TGpioDirection::Input;
            if (item.isMember("type"))
                    gpio_desc.Type = item["type"].asString();
            if (item.isMember("multiplier"))
                gpio_desc.Multiplier = item["multiplier"].asInt();
            if (item.isMember("edge"))
                gpio_desc.InterruptEdge = item["edge"].asString();
            gpio_desc.Order = index;
            handler_config.AddGpio(gpio_desc);

        }
    }

	mosqpp::lib_init();

    mqtt_config.Id = "wb-gpio";
    std::shared_ptr<TMQTTGpioHandler> mqtt_handler(new TMQTTGpioHandler(mqtt_config, handler_config));
    mqtt_handler->Init();

    rc= mqtt_handler->loop_start();
    if (rc != 0 ) {
        cerr << "couldn't start mosquitto_loop_start ! " << rc << endl;
    }else {
        epfd = epoll_create(1);// creating epoll for Interrupts
        mqtt_handler->InitInterrupts(epfd);
        steady_clock::time_point start;
        int interval;
        start = steady_clock::now();
        while(1){
            n = epoll_wait(epfd,events,20,500);
            interval = duration_cast<milliseconds>(steady_clock::now() - start).count() ;
            if (interval >= 500 ) {  //checking is it time to look through all gpios
                mqtt_handler->UpdateChannelValues();
                start = steady_clock::now();
            }else {
                if (mqtt_handler->FirstTime && interval == 0) {
                    mqtt_handler->FirstTime = false;
                    continue;
                }
                mqtt_handler->CatchInterrupts( n, events );
            }
        }
	}

	mosqpp::lib_cleanup();

	return 0;
}
//build-dep libmosquittopp-dev libmosquitto-dev
// dep: libjsoncpp0 libmosquittopp libmosquitto

// 2420 2032
// 6008 2348 1972
<|endoftext|>