ecflow-5.16.0/0000775000175000017500000000000015161437336013257 5ustar alastairalastairecflow-5.16.0/NOTICE0000664000175000017500000001154715161437256014174 0ustar alastairalastairApache [ecflow] Copyright [2009-2022] The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). Boost Software License - Version 1.0 - August 17th, 2003 ======================================================== Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The MIT License (MIT) - EOS-Portable-library ============================================ Copyright (c) 2012 EOS GmbH Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Certificate.hpp =============== Much of the code there is taken from https://gist.github.com/nathan-osman/5041136, provided with the MIT License: The MIT License (MIT) Copyright (c) 2022 Nathan Osman Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Base64.hpp ========== Distributed under the BSD License - see the source file for the full text. cpp-httplib =========== Distributed under the MIT License see the source directory for the full text. json ==== Distributed under the MIT License see the source directory for the full text. LazyTextEdit ================================================ LazyTextEdit is a Qt based text editor that lazily loads data from disk when necessary. It tries to keep memory usage as low as possible and only stores chunks of data that have been modified. Its APIs resembles that of QText(Edit|Document|Cursor). It supports basic formatting options using TextSections and SyntaxHighlighter. LazyTextEdit is licensed under the Apache 2.0 license. Some of the source code was modified for inclusion in ecFlowUI. spinning_wheel.gif ================================================ This was generated from the website: http://www.ajaxload.info/ and is freely licensed as described there. ecflow-5.16.0/bin/0000775000175000017500000000000015161437256014030 5ustar alastairalastairecflow-5.16.0/bin/ecbuild0000775000175000017500000003165315161437256015375 0ustar alastairalastair#!/bin/bash --noprofile # (C) Copyright 2011- ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation nor # does it submit to any jurisdiction. set -eua CMAKE_MIN_REQUIRED=3.11 CMAKE_BUILD_VERSION=3.18.3 usage() { echo "Usage: ecbuild [--help] [--version]" exit $1 } help() { cat < ecbuild [option...] [--] [cmake-argument...] DESCRIPTION: ecbuild is a build system based on CMake, but providing a lot of macro's to make it easier to work with. Upon execution, the equivalent cmake command is printed. ecbuild/cmake must be called from an out-of-source build directory and forbids in-source builds. SYNOPSIS: --help Display this help --version Display ecbuild version Available values for "option": --cmakebin= Set which cmake binary to use. Default is 'cmake' --prefix= Set the install path to . Equivalent to cmake argument "-DCMAKE_INSTALL_PREFIX=" --build= Set the build-type to . Equivalent to cmake argument "-DCMAKE_BUILD_TYPE=" can be any of: - debug : Lowest optimization level, useful for debugging - release : Highest optimization level, for best performance - bit : Highest optimization level while staying bit-reproducible - ...others depending on project --log= Set the ecbuild log-level Equivalent to "-DECBUILD_LOG_LEVEL=" can be any of: - DEBUG - INFO - WARN - ERROR - CRITICAL - OFF Every choice outputs also the log-levels listed below itself --static Build static libraries. Equivalent to "-DBUILD_SHARED_LIBS=OFF" --dynamic, --shared Build dynamic libraries (usually the default). Equivalent to "-DBUILD_SHARED_LIBS=ON" --config= Configuration file using CMake syntax that gets included Equivalent to cmake argument "-DECBUILD_CONFIG=" --toolchain= Use a platform specific toolchain, containing settings such as compilation flags, locations of commonly used dependencies. should be the path to a custom toolchain file. Equivalent to cmake argument "-DCMAKE_TOOLCHAIN_FILE=" --cache= (advanced) A file called "ecbuild-cache.cmake" is generated during configuration. This file can be moved to a safe location, and specified for future builds to speed up checking of compiler/platform capabilities. Note that this is only accelerating fresh builds, as cmake internally caches also. Therefore this option is *not* recommended. --get-cmake[=] Automatically download CMake binaries from version $CMAKE_BUILD_VERSION. Requires an internet connection. If no prefix is given, install into $PWD/.local/. --build-cmake[=] Automatically download and build CMake version $CMAKE_BUILD_VERSION. Requires an internet connection and may take a while. If no prefix is given, install into $PWD/.local/. --dryrun Don't actually execute the cmake call, just print what would have been executed. Available values for "cmake-argument": Any value that can be usually passed to cmake to (re)configure the build. Typically these values start with "-D". example: -DENABLE_TESTS=ON -DENABLE_MPI=OFF -DECKIT_PATH=... They can be explicitly separated from [option...] with a "--", for the case there is a conflicting option with the "cmake" executable, and the latter's option is requested. ------------------------------------------------------------------------ NOTE: When reconfiguring a build, it is only necessary to change the relevant options, as everything stays cached. For example: > ecbuild --prefix=PREFIX . > ecbuild -DENABLE_TESTS=ON . ------------------------------------------------------------------------ Compiling: To compile the project with threads: > make -j To get verbose compilation/linking output: > make VERBOSE=1 Testing: To run the project's tests > ctest Also check the ctest manual/help for more options on running tests Installing: To install the project in location PREFIX with "--prefix=PREFIX" or "-DCMAKE_INSTALL_PREFIX=PREFIX" > make install ------------------------------------------------------------------------ ECMWF" EOF exit $1 } INSTALL_DIR="$( cd $( dirname "${BASH_SOURCE[0]}" ) && pwd -P )" export ecbuild_ROOT="$( cd "$INSTALL_DIR/.." && pwd -P )" export ecbuild_DIR=$ecbuild_ROOT # for versions of CMake < 3.12 ECBUILD_MODULE_PATH="" # If there is a directory share/ecbuild/cmake relative to the parent directory # (as in an install tree), add it to CMAKE_MODULE_PATH if [ -d $INSTALL_DIR/../share/ecbuild/cmake ]; then ECBUILD_MODULE_PATH="$( cd "$INSTALL_DIR/../share/ecbuild/cmake" && pwd -P )" # If there is a cmake subdirectory relative to the script directory (as in a # tarball), add it to CMAKE_MODULE_PATH elif [ -d $INSTALL_DIR/../cmake ]; then ECBUILD_MODULE_PATH="$( cd "$INSTALL_DIR/../cmake" && pwd -P )" fi # Fail if we couldn't find ecBuild modules if [ ! -f "$ECBUILD_MODULE_PATH/ecbuild_system.cmake" ]; then echo "FATAL: ecBuild modules could not be found in either $INSTALL_DIR/../share/ecbuild/cmake or $INSTALL_DIR/../cmake" >&2 exit 1 fi ADD_ECBUILD_OPTIONS="-DCMAKE_MODULE_PATH=$ECBUILD_MODULE_PATH" version() { ecbuild_version=$(cat ${ECBUILD_MODULE_PATH}/VERSION) echo "ecbuild version ${ecbuild_version}" command -v cmake >/dev/null 2>&1 || { exit 0; } cmake --version | head -1 exit 0 } log() { log_level=$(tr "[a-z]" "[A-Z]" <<< "$1") ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DECBUILD_LOG_LEVEL=${log_level}" } prefix() { ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DCMAKE_INSTALL_PREFIX=${1/#\~\//$HOME/}" } config() { arg=${1/#\~\//$HOME/} if [ -f $arg ]; then config_file=$arg config_file="$( cd $( dirname "${config_file}" ) && pwd -P )/$( basename ${config_file} )" else echo "Error:" echo " Config file [$arg] is not found or is not a file." exit 1 fi ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DECBUILD_CONFIG=${config_file}" } toolchain() { arg=${1/#\~\//$HOME/} if [ -f $arg ]; then toolchain_file=$arg fi if [ -z ${toolchain_file+x} ]; then echo "Error toolchain $arg is not valid" exit 1 else ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DCMAKE_TOOLCHAIN_FILE=${toolchain_file}" fi } cache() { arg=$1 if [ -f $arg ]; then cache_file=$arg cache_file="$( cd $( dirname "${cache_file}" ) && pwd -P )/$( basename ${cache_file} )" else echo "Error:" echo " Cache file [$arg] is not found or is not a file." exit 1 fi ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DECBUILD_CACHE=${cache_file}" } if test $# -eq 0; then usage 1 fi while test $# -gt 0; do # Split --option=value in $opt="--option" and $val="value" opt="" val="" case "$1" in --*=*) opt=`echo "$1" | sed 's/=.*//'` val=`echo "$1" | sed 's/--[_a-zA-Z0-9-]*=//'` ;; --*) opt=$1 ;; # -D*) # ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS $1" # ;; *) break ;; esac # echo "debug opt: $opt $val" # Parse options case "$opt" in --help) help 0 ;; --version) version ;; --dryrun) dryrun="yes" ;; --cmakebin) cmakebin="$val" ;; --prefix) prefix "$val" ;; --build) ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DCMAKE_BUILD_TYPE=$val" ;; --log) log $val ;; --static) ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DBUILD_SHARED_LIBS=OFF" ;; --dynamic) ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DBUILD_SHARED_LIBS=ON" ;; --shared) ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DBUILD_SHARED_LIBS=ON" ;; --toolchain) toolchain $val ;; --config) config $val ;; --cache) cache $val ;; --get-cmake) get_cmake="bin" if [[ -n $val ]]; then cmake_prefix="$val" fi ;; --build-cmake) get_cmake="src" if [[ -n $val ]]; then cmake_prefix="$val" fi ;; --) shift break ;; *) echo "unknown option: $opt" usage 1 ;; esac shift done # If no arguments remain, set srcARG to "." if [ $# -eq 0 ]; then srcARG="." fi if [ -z ${toolchain_file+x} ]; then if [ -z ${ECBUILD_TOOLCHAIN+x} ]; then : else toolchain ${ECBUILD_TOOLCHAIN} echo "ecbuild toolchain set using environment variable ECBUILD_TOOLCHAIN" fi fi src=${srcARG:=""} cmake=${cmakebin:=cmake} dryrun=${dryrun:=no} get_cmake=${get_cmake:=""} cmake_prefix=${cmake_prefix:=$PWD/.local} cmake_found="" cmake_version_sufficient="" # Check that version $1 satisfies $2 # CMake versions have no more than 4 fields # Version sort (sort -V) is not available on all platforms version_gte() { [ "$2" = "$(echo -e "$1\n$2" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -k 4,4 -g | head -n1)" ] } # Download a CMake tarball # $1: version # $2: suffix (optional) download_cmake() { tarball=cmake-$1${2:-""}.tar.gz if [[ ! -r $tarball ]]; then shortver=$(echo $1 | cut -d. -f1-2) url=http://www.cmake.org/files/v$shortver/$tarball # -N Download only if the remote version of the file is newer # --continue Continue an interrupted download # -T 60 Time out a download attempt after 60 seconds # -t 3 Only make 3 download attempts wget -N --continue -T 60 -t 3 $url || { echo "Failed to download CMake release $1." >&2 echo "Please download from $url" >&2 echo "and place $tarball in $PWD" >&2 exit 1 } fi echo $tarball } # Use already built CMake if any if [[ -x "${cmake_prefix}/bin/cmake" ]]; then echo "Using already built CMake in ${cmake_prefix}/bin/cmake" >&2 cmake="${cmake_prefix}/bin/cmake" # Get a CMake binary if requested and no sufficient version found elif [[ $get_cmake = "bin" ]]; then plat=$(uname -s) arch=$(uname -m) if [[ "${plat}" != "Linux" ]] && [[ "${plat}" != "Darwin" ]] ; then echo "Cannot download CMake binaries for this platform." >&2 echo "Please use --build-cmake to build from source." >&2 exit 1 fi if [[ "${arch}" != "x86_64" ]] ; then echo "Cannot download CMake binaries for this architecture." >&2 echo "Please use --build-cmake to build from source." >&2 exit 1 fi echo "Downloading CMake version ${CMAKE_BUILD_VERSION} binaries and installing into ${cmake_prefix} ..." >&2 tarball=$(download_cmake "${CMAKE_BUILD_VERSION}" "-${plat}-${arch}") mkdir -p "${cmake_prefix}" tar xzf $tarball -C "${cmake_prefix}" --strip-components=1 cmake="${cmake_prefix}/bin/cmake" # Build CMake from source if requested and no sufficient version found elif [[ $get_cmake = "src" ]]; then echo "Building CMake version ${CMAKE_BUILD_VERSION} and installing into ${cmake_prefix} ..." >&2 tarball=$(download_cmake "${CMAKE_BUILD_VERSION}") tar xzf $tarball ( mkdir -p build_cmake cd build_cmake ../cmake-${CMAKE_BUILD_VERSION}/bootstrap --prefix="${cmake_prefix}" && make && make install ) cmake="${cmake_prefix}/bin/cmake" fi # Check if the cmake version is sufficient if $(command -v $cmake >/dev/null 2>&1); then cmake_found="yes" cmake_version=$($cmake --version | head -n1 | awk '{ print $3 }') echo "Found CMake version $cmake_version" >& 2 if version_gte $cmake_version $CMAKE_MIN_REQUIRED; then cmake_version_sufficient="yes" fi fi # Fail if we don't have a sufficient CMake if [[ ! $cmake_version_sufficient ]]; then if [[ ! $cmake_found ]]; then echo "CMake is required and cannot be found in the PATH." >&2 else echo "CMake version $CMAKE_MIN_REQUIRED is required but only $cmake_version was found." >&2 fi echo "" >&2 echo " Try 'module load cmake', specify a CMake binary with --cmakebin=/path/to/cmake" >&2 echo " or let ecbuild download and build CMake with the --build-cmake option." >&2 exit 1 fi echo "" echo "$cmake ${ADD_ECBUILD_OPTIONS} $@ $src" echo "" if [ ${dryrun} == "yes" ]; then echo "[DRYRUN] -- not executing" exit 0 fi $cmake ${ADD_ECBUILD_OPTIONS} "$@" $src ecflow-5.16.0/bin/CMakeLists.txt0000664000175000017500000000007315161437256016570 0ustar alastairalastairinstall( PROGRAMS ecbuild DESTINATION ${INSTALL_BIN_DIR} ) ecflow-5.16.0/libs/0000775000175000017500000000000015161437256014211 5ustar alastairalastairecflow-5.16.0/libs/attribute/0000775000175000017500000000000015161437256016214 5ustar alastairalastairecflow-5.16.0/libs/attribute/src/0000775000175000017500000000000015161437256017003 5ustar alastairalastairecflow-5.16.0/libs/attribute/src/ecflow/0000775000175000017500000000000015161437256020262 5ustar alastairalastairecflow-5.16.0/libs/attribute/src/ecflow/attribute/0000775000175000017500000000000015161437256022265 5ustar alastairalastairecflow-5.16.0/libs/attribute/src/ecflow/attribute/DayAttr.hpp0000664000175000017500000001257415161437256024357 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #ifndef ecflow_attribute_DayAttr_HPP #define ecflow_attribute_DayAttr_HPP #include #include "ecflow/core/Chrono.hpp" namespace cereal { class access; } namespace ecf { class Calendar; } // namespace ecf // Use default copy constructor, assignment operator, destructor class DayAttr { public: enum Day_t { SUNDAY = 0, MONDAY = 1, TUESDAY = 2, WEDNESDAY = 3, THURSDAY = 4, FRIDAY = 5, SATURDAY = 6 }; DayAttr() = default; explicit DayAttr(Day_t day) : day_(day) {} explicit DayAttr(const std::string& str) : day_(DayAttr::getDay(str)) {} explicit DayAttr(const boost::gregorian::date& date) : day_(static_cast(date.day_of_week().as_number())), date_(date) {} bool operator==(const DayAttr& rhs) const; bool operator<(const DayAttr& rhs) const { return day_ < rhs.day_; } bool structureEquals(const DayAttr& rhs) const; /// called when definition is restored from a file/checkpoint. handle reset of date_ void handle_migration(const ecf::Calendar&); // called at begin time. void reset(); void reset(const ecf::Calendar& c); // called when we need a requeue based on a time attribute. Should *NOT* clear expired flag. void requeue_time(); // called when re-queueing because of: // - automatic re-queue due to repeat increment // - manual re-queue // Clears expired flag, and sets day attribute to a next matching *FUTURE* day or current day void requeue_manual(const ecf::Calendar& c); // can match today if today is match day void requeue_repeat_increment(const ecf::Calendar& c); // match *FUTURE* day // Called after a node has completed, if calendar day corresponds to *THIS* day. *Expire* it // This should be called just before: checkForRequeue. // This day attribute should be treated as being deleted. returns false from // - isFree() stops re-queue on expired day // - calendarChanged() stops clearing of free. // Expired flag is RESET only by: // - void requeue_manual(const ecf::Calendar& c); // - void requeue_repeat_increment(const ecf::Calendar& c); void check_for_expiration(const ecf::Calendar&); // We must use a real date, using enum is not sufficient as in ecflow4 // 0,1,2,3,4,5,6 // ^ ^ // sunday saturday // old/buggy: check_for_requeue() { return (day_ > calendar.day_of_week() );} // The old check for re-queue would determine if this day is in the future, with reference to calendar day. // Hence day_(Saturday) is would always re-queue, day_(Sunday) is would never re-queue // When multiple days were involved, it would get even buggier. // By using a real date, we fix the issue, the real date is updated during manual re-queue, or automatically vi // repeat increment. bool checkForRequeue(const ecf::Calendar&) const; void setFree(); // ensures that isFree() always returns true bool isSetFree() const { return free_; } void calendarChanged(const ecf::Calendar& c, bool clear_at_midnight = true); // can set attribute free bool isFree(const ecf::Calendar&) const; bool validForHybrid(const ecf::Calendar&) const; bool why(const ecf::Calendar&, std::string& theReasonWhy) const; // The state_change_no is never reset. Must be incremented if it can affect equality unsigned int state_change_no() const { return state_change_no_; } std::string name() const; // for display/gui only std::string toString() const; std::string dump() const; std::string as_simple_string() const; // return the days, if input is not valid will throw a runtime_error static DayAttr create(const std::string& dayStr); static DayAttr create(const std::vector& lineTokens, bool read_state); static DayAttr::Day_t getDay(const std::string&); static std::vector allDays(); // access DayAttr::Day_t day() const { return day_; } const boost::gregorian::date& date() const { return date_; } boost::gregorian::date next_matching_date(const ecf::Calendar& c) const; void set_expired(); // ********* TREAT this Day Attribute as deleted ********** bool expired() const { return expired_; } void read_state(const std::vector& lineTokens); private: void clearFree(); // resets the free flag void clear_expired(); bool is_free(const ecf::Calendar&) const; // ignores free_ boost::gregorian::date matching_date(const ecf::Calendar& c) const; public: void write(std::string&) const; private: DayAttr::Day_t day_{DayAttr::SUNDAY}; unsigned int state_change_no_{0}; // *not* persisted, only used on server side bool free_{false}; // persisted for use by why() on client side bool expired_{false}; // added for ecflow 5.4.0 boost::gregorian::date date_; // corresponding to day_ friend class cereal::access; template void serialize(Archive& ar); }; #endif /* ecflow_attribute_DayAttr_HPP */ ecflow-5.16.0/libs/attribute/src/ecflow/attribute/LateAttr.cpp0000664000175000017500000002115315161437256024513 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "ecflow/attribute/LateAttr.hpp" #include #include "ecflow/core/Calendar.hpp" #include "ecflow/core/Ecf.hpp" #include "ecflow/core/NState.hpp" #include "ecflow/core/Serialization.hpp" #include "ecflow/core/Str.hpp" #include "ecflow/core/TimeSeries.hpp" namespace ecf { LateAttr::LateAttr() = default; std::string LateAttr::toString() const { std::string ret; write(ret); return ret; } void LateAttr::write(std::string& ret) const { ret += "late"; if (!s_.isNULL()) { ret += " -s +"; s_.write(ret); } if (!a_.isNULL()) { ret += " -a "; a_.write(ret); } if (!c_.isNULL()) { ret += " -c "; if (c_is_rel_) { ret += "+"; } c_.write(ret); } } bool LateAttr::operator==(const LateAttr& rhs) const { if (c_is_rel_ != rhs.c_is_rel_) { return false; } if (s_ != rhs.s_) { return false; } if (a_ != rhs.a_) { return false; } if (c_ != rhs.c_) { return false; } if (isLate_ != rhs.isLate_) { return false; } return true; } bool LateAttr::isNull() const { return (s_.isNULL() && a_.isNULL() && c_.isNULL()); } void LateAttr::checkForLateness(const std::pair& state, const ecf::Calendar& calendar) { if (isLate_ || isNull()) { return; } if (check_for_lateness(state, calendar)) { setLate(true); } } bool LateAttr::check_for_lateness(const std::pair& state, const ecf::Calendar& calendar) const { if (isNull()) { return false; } if (state.first == NState::SUBMITTED || state.first == NState::QUEUED) { // Submitted is always relative, ASSUME this means relative to suite start if (state.first == NState::SUBMITTED && !s_.isNULL()) { // ECFLOW-322 // The late attr check for being late in submitted state, is always *RELATIVE* // Previously we had: // // if ( calendar.duration() >= s_.duration() ) { // setLate(true); // return; // } // This is incorrect since calendar.duration() is essentially duration until // the last call to Calendar::init() i.e suite duration time. Hence, late was // set straight away. // // To check for submitted, we need the duration *after* state went into submitted state. // `state.second` is when state went SUBMITTED, relative to suite start auto time_in_submitted_state = calendar.duration() - state.second; if (time_in_submitted_state >= s_.duration()) { return true; } } // In Submitted or queued state, check for active, in REAL time if (!a_.isNULL() && calendar.suiteTime().time_of_day() >= a_.duration()) { return true; } } else if (state.first == NState::ACTIVE && !c_.isNULL()) { if (c_is_rel_) { // To check for complete, we need the duration when state went into active state. // `state.second` is when state went ACTIVE, relative to suite start auto runtime = calendar.duration() - state.second; if (runtime >= c_.duration()) { return true; } } else { // Real time if (calendar.suiteTime().time_of_day() >= c_.duration()) { return true; } } } return false; } void LateAttr::setLate(bool f) { if (f != isLate_) { // minimise changes to state_change_no_ isLate_ = f; state_change_no_ = Ecf::incr_state_change_no(); } #ifdef DEBUG_STATE_CHANGE_NO std::cout << "LateAttr::setLate changed=" << (f != isLate_) << "\n"; #endif } void LateAttr::override_with(LateAttr* in_late) { if (in_late) { if (!in_late->submitted().isNULL()) { s_ = in_late->submitted(); } if (!in_late->active().isNULL()) { a_ = in_late->active(); } if (!in_late->complete().isNULL()) { c_ = in_late->complete(); } c_is_rel_ = in_late->complete_is_relative(); // DO NOT override isLate_, because if the parent is late, we do not want *ALL* children to be set late // isLate_ = in_late->isLate(); } } void LateAttr::parse(LateAttr& lateAttr, const std::string& line, const std::vector& lineTokens, size_t index) { // late -s +00:15 -a 20:00 -c +02:00 #The option can be in any order // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 // late -s +00:15 -c +02:00 # not all options are needed // 0 1 2 3 4 5 assert(lateAttr.isNull()); size_t line_token_size = lineTokens.size(); for (size_t i = index; i < line_token_size; i += 1) { if (lineTokens[i][0] == '#') { break; } if (lineTokens[i] == "-s") { if (!lateAttr.submitted().isNULL()) { throw std::runtime_error("LateParser::doParse: Invalid late, submitted specified twice :" + line); } if (i + 1 < line_token_size) { int hour = -1; int min = -1; TimeSeries::getTime(lineTokens[i + 1], hour, min); lateAttr.addSubmitted(TimeSlot(hour, min)); i++; } else { throw std::runtime_error("LateParser::doParse: Invalid late, submitted time not specified :" + line); } } else if (lineTokens[i] == "-a") { if (!lateAttr.active().isNULL()) { throw std::runtime_error("LateParser::doParse: Invalid late, active specified twice :" + line); } if (i + 1 < line_token_size) { int hour = -1; int min = -1; TimeSeries::getTime(lineTokens[i + 1], hour, min); lateAttr.addActive(TimeSlot(hour, min)); i++; } else { throw std::runtime_error("LateParser::doParse: Invalid late, active time not specified :" + line); } } else if (lineTokens[i] == "-c") { if (!lateAttr.complete().isNULL()) { throw std::runtime_error("LateParser::doParse: Invalid late, complete specified twice :" + line); } if (i + 1 < line_token_size) { int hour = -1; int min = -1; bool relative = TimeSeries::getTime(lineTokens[i + 1], hour, min); lateAttr.addComplete(TimeSlot(hour, min), relative); i++; } else { throw std::runtime_error("LateParser::doParse: Invalid late, active time not specified :" + line); } } else { throw std::runtime_error("LateParser::doParse:5: Invalid late :" + line); } } if (lateAttr.isNull()) { throw std::runtime_error("LateParser::doParse:6: Invalid late :" + line); } } LateAttr LateAttr::create(const std::string& lateString) { std::vector lineTokens; Str::split(lateString, lineTokens); if (lineTokens.empty()) { throw std::runtime_error("LateParser::create: empty string no late specified ?" + lateString); } // adjust the index size_t index = 0; if (lineTokens[0] == "late") { index = 1; } LateAttr lateAttr; parse(lateAttr, lateString, lineTokens, index); return lateAttr; } template void LateAttr::serialize(Archive& ar) { CEREAL_OPTIONAL_NVP(ar, s_, [this]() { return !s_.isNULL(); }); CEREAL_OPTIONAL_NVP(ar, a_, [this]() { return !a_.isNULL(); }); CEREAL_OPTIONAL_NVP(ar, c_, [this]() { return !c_.isNULL(); }); CEREAL_OPTIONAL_NVP(ar, c_is_rel_, [this]() { return c_is_rel_; }); CEREAL_OPTIONAL_NVP(ar, isLate_, [this]() { return isLate_; }); } CEREAL_TEMPLATE_SPECIALIZE(LateAttr); } // namespace ecf ecflow-5.16.0/libs/attribute/src/ecflow/attribute/QueueAttr.hpp0000664000175000017500000000575315161437256024727 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #ifndef ecflow_attribute_QueueAttr_HPP #define ecflow_attribute_QueueAttr_HPP #include #include "ecflow/core/NState.hpp" ///////////////////////////////////////////////////////////////////////// class QueueAttr { public: QueueAttr(const std::string& name, const std::vector& theQueue); QueueAttr() = default; ~QueueAttr(); bool operator==(const QueueAttr& rhs) const; bool operator<(const QueueAttr& rhs) const { return name_ < rhs.name(); } /// Accessor const std::string& name() const { return name_; } std::string value() const; int index_or_value() const; int index() const { return currentIndex_; } bool empty() const { return name_.empty(); } const std::vector& list() const { return theQueue_; } const std::vector& state_vec() const { return state_vec_; } NState::State state(const std::string& step) const; std::string toString() const; std::string dump() const; // Mutators void requeue(); std::string active(); // return current index and value, make active, increment index void complete(const std::string& step); // step is more index:value void aborted(const std::string& step); std::string no_of_aborted() const; void reset_index_to_first_queued_or_aborted(); void set_used_in_trigger(bool f) { used_in_trigger_ = f; } // used by simulator only bool used_in_trigger() const { return used_in_trigger_; } static void parse(QueueAttr&, const std::string& line, std::vector& lineTokens, bool parse_state); void set_name(const std::string& name); void set_queue(const std::vector& theQueue, int index, const std::vector& state_vec); void set_state_vec(const std::vector& state_vec); void set_index(int index) { currentIndex_ = index; }; // Added to support return by reference static const QueueAttr& EMPTY(); static QueueAttr& EMPTY1(); unsigned int state_change_no() const { return state_change_no_; } private: void incr_state_change_no(); public: void write(std::string&) const; private: std::vector theQueue_; std::vector state_vec_; std::string name_; int currentIndex_{0}; unsigned int state_change_no_{0}; // *not* persisted, only used on server side bool used_in_trigger_{false}; // *not* persisted, used by simulator only private: friend class cereal::access; template void serialize(Archive& ar, std::uint32_t const version); }; #endif /* ecflow_attribute_QueueAttr_HPP */ ecflow-5.16.0/libs/attribute/src/ecflow/attribute/ZombieAttr.cpp0000664000175000017500000002621315161437256025055 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "ecflow/attribute/ZombieAttr.hpp" #include #include #include #include "ecflow/core/Converter.hpp" #include "ecflow/core/Serialization.hpp" #include "ecflow/core/Str.hpp" #include "ecflow/core/ZombieCtrlAction.hpp" using namespace ecf; const ZombieAttr& ZombieAttr::EMPTY() { static const ZombieAttr ZOMBIEATTR = ZombieAttr(); return ZOMBIEATTR; } // Constructor ============================================================================== ZombieAttr::ZombieAttr(ecf::Child::ZombieType t, const std::vector& c, ZombieCtrlAction a, int zombie_lifetime) : child_cmds_(c), zombie_type_(t), action_(a), zombie_lifetime_(zombie_lifetime) { /// Server typically checks every 60 seconds, hence this is lowest valid value for if (zombie_lifetime_ <= 0) { // default constructor. Set defaults switch (zombie_type_) { case Child::USER: zombie_lifetime_ = default_user_zombie_life_time(); break; case Child::PATH: zombie_lifetime_ = default_path_zombie_life_time(); break; case Child::ECF: zombie_lifetime_ = default_ecf_zombie_life_time(); break; case Child::ECF_PID: zombie_lifetime_ = default_ecf_zombie_life_time(); break; case Child::ECF_PID_PASSWD: zombie_lifetime_ = default_ecf_zombie_life_time(); break; case Child::ECF_PASSWD: zombie_lifetime_ = default_ecf_zombie_life_time(); break; case Child::NOT_SET: assert(false); break; } } else if (zombie_lifetime_ < minimum_zombie_life_time()) { zombie_lifetime_ = minimum_zombie_life_time(); } } bool ZombieAttr::operator==(const ZombieAttr& rhs) const { if (child_cmds_ != rhs.child_cmds_) { return false; } if (zombie_type_ != rhs.zombie_type_) { return false; } if (action_ != rhs.action_) { return false; } if (zombie_lifetime_ != rhs.zombie_lifetime_) { return false; } return true; } std::string ZombieAttr::toString() const { /// format is zombie_type : child_cmds(optional) : action : zombie_lifetime_(optional) std::string ret; write(ret); return ret; } void ZombieAttr::write(std::string& ret) const { /// format is zombie_type : child_cmds(optional) : action : zombie_lifetime_(optional) ret += "zombie "; ret += Child::to_string(zombie_type_); ret += Str::COLON(); ret += ecf::to_string(action_); ret += Str::COLON(); ret += Child::to_string(child_cmds_); ret += Str::COLON(); ret += ecf::convert_to(zombie_lifetime_); } bool ZombieAttr::fob(ecf::Child::CmdType child_cmd) const { if (action_ != ZombieCtrlAction::FOB) { return false; } if (child_cmds_.empty()) { return true; } // If we have child commands specified, then the action is only applicable for that child cmd // for all other child cmds we block for (auto i : child_cmds_) { if (i == child_cmd) { return true; } } return false; } bool ZombieAttr::fail(ecf::Child::CmdType child_cmd) const { if (action_ != ZombieCtrlAction::FAIL) { return false; } if (child_cmds_.empty()) { return true; } // If we have child commands specified, then the action is only applicable for that child cmd // for all other child cmds we block for (auto i : child_cmds_) { if (i == child_cmd) { return true; } } return false; } bool ZombieAttr::adopt(ecf::Child::CmdType child_cmd) const { if (action_ != ZombieCtrlAction::ADOPT) { return false; } if (child_cmds_.empty()) { return true; } // If we have child commands specified, then the action is only applicable for that child cmd // for all other child cmds we block for (auto i : child_cmds_) { if (i == child_cmd) { return true; } } return false; } bool ZombieAttr::remove(ecf::Child::CmdType child_cmd) const { if (action_ != ZombieCtrlAction::REMOVE) { return false; } if (child_cmds_.empty()) { return true; } // If we have child commands specified, then the action is only applicable for that child cmd // for all other child cmds we block for (auto i : child_cmds_) { if (i == child_cmd) { return true; } } return false; } bool ZombieAttr::block(ecf::Child::CmdType child_cmd) const { if (action_ != ZombieCtrlAction::BLOCK) { return false; } if (child_cmds_.empty()) { return true; } // If we have child commands specified, then the action is only applicable for that child cmd // for all other child cmds we block for (auto i : child_cmds_) { if (i == child_cmd) { return true; } } return false; } bool ZombieAttr::kill(ecf::Child::CmdType child_cmd) const { if (action_ != ZombieCtrlAction::KILL) { return false; } if (child_cmds_.empty()) { return true; } // If we have child commands specified, then the action is only applicable for that child cmd // for all other child cmds we block for (auto i : child_cmds_) { if (i == child_cmd) { return true; } } return false; } ZombieAttr ZombieAttr::create(const std::string& string_to_parse) { /// Use boost tokenizer instead of Str::split, as it allows preservation of empty tokens boost::char_separator sep(":", "", boost::keep_empty_tokens); using tokenizer = boost::tokenizer>; tokenizer tokenise(string_to_parse, sep); std::vector tokens; std::copy(tokenise.begin(), tokenise.end(), back_inserter(tokens)); if (tokens.size() < 2) { throw std::runtime_error("ZombieAttr::create failed: Invalid zombie type " + string_to_parse); } /// expects ::child_cmds:zombie_lifetime std::string str_zombie_type; std::string action_str; std::string child_cmds; std::string lifetime; size_t tokens_size = tokens.size(); for (size_t i = 0; i < tokens_size; i++) { // cout << " token " << i << ": '" << tokens[i] << "'\n"; if (i == 0) { str_zombie_type = tokens[i]; continue; } if (i == 1) { action_str = tokens[i]; continue; } if (i == 2) { child_cmds = tokens[i]; continue; } if (i == 3) { lifetime = tokens[i]; continue; } throw std::runtime_error("ZombieAttr::create failed: Invalid zombie tokens " + string_to_parse); } // std::cout << " zombie_type = " << str_zombie_type << " user_action = " << action_str << " child_cmds = " // << child_cmds<< " zombie_lifetime = " << lifetime << "\n"; if (!Child::valid_zombie_type(str_zombie_type)) { throw std::runtime_error("ZombieAttr::create failed: Invalid zombie type, expected one of [ user | ecf | " "ecf_pid | ecf_pid_passed | ecf_passwd | path ] but found " + str_zombie_type + std::string(":") + string_to_parse); } if (!action_str.empty() && !ecf::Enumerate::is_valid(action_str)) { throw std::runtime_error("ZombieAttr::create failed: Invalid user action, expected one of [ fob | fail | " "remove | block | adopt | kill ] but found " + action_str + std::string(":") + string_to_parse); } if (!child_cmds.empty() && !Child::valid_child_cmds(child_cmds)) { throw std::runtime_error("ZombieAttr::create failed: Invalid child type, expected one or more of [ " "init,event,meter,label,wait,queue,abort,complete] but found " + tokens[2] + std::string(":") + string_to_parse); } int zombie_lifetime = -1; if (!lifetime.empty()) { try { zombie_lifetime = ecf::convert_to(lifetime); } catch (const ecf::bad_conversion&) { throw std::runtime_error("ZombieAttr::create failed: Zombie life time must be convertible to an integer " + lifetime + std::string(":") + string_to_parse); } } if (action_str.empty() && zombie_lifetime == -1) { throw std::runtime_error( "ZombieAttr::create failed: User Action(fob,fail,remove,adopt,block) or lifetime must be specified: " + string_to_parse); } auto zombie_type = Child::zombie_type(str_zombie_type); auto action = Enumerate::to_enum(action_str).value_or(ZombieCtrlAction::BLOCK); auto childVec = Child::child_cmds(child_cmds); /// If zombie_lifetime is still -1 constructor will reset to standard defaults return ZombieAttr(zombie_type, childVec, action, zombie_lifetime); } ZombieAttr ZombieAttr::get_default_attr(ecf::Child::ZombieType zt) { switch (zt) { case Child::USER: return ZombieAttr( zt, std::vector(), ZombieCtrlAction::BLOCK, default_user_zombie_life_time()); case Child::PATH: return ZombieAttr( zt, std::vector(), ZombieCtrlAction::BLOCK, default_path_zombie_life_time()); case Child::ECF: return ZombieAttr( zt, std::vector(), ZombieCtrlAction::BLOCK, default_ecf_zombie_life_time()); case Child::ECF_PID: return ZombieAttr( zt, std::vector(), ZombieCtrlAction::BLOCK, default_ecf_zombie_life_time()); case Child::ECF_PID_PASSWD: return ZombieAttr( zt, std::vector(), ZombieCtrlAction::BLOCK, default_ecf_zombie_life_time()); case Child::ECF_PASSWD: return ZombieAttr( zt, std::vector(), ZombieCtrlAction::BLOCK, default_ecf_zombie_life_time()); case Child::NOT_SET: break; } return ZombieAttr( Child::ECF, std::vector(), ZombieCtrlAction::BLOCK, default_ecf_zombie_life_time()); } template void ZombieAttr::serialize(Archive& ar, std::uint32_t const version) { ar(CEREAL_NVP(child_cmds_), CEREAL_NVP(zombie_type_), CEREAL_NVP(action_), CEREAL_NVP(zombie_lifetime_)); } CEREAL_TEMPLATE_SPECIALIZE_V(ZombieAttr); ecflow-5.16.0/libs/attribute/src/ecflow/attribute/DateAttr.cpp0000664000175000017500000002604515161437256024510 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "ecflow/attribute/DateAttr.hpp" #include #include #include "ecflow/core/Calendar.hpp" #include "ecflow/core/Chrono.hpp" #include "ecflow/core/Converter.hpp" #include "ecflow/core/Ecf.hpp" #include "ecflow/core/Extract.hpp" #include "ecflow/core/Message.hpp" #include "ecflow/core/Serialization.hpp" using namespace ecf; //========================================================================================== DateAttr::DateAttr(int day, int month, int year) : day_(day), month_(month), year_(year) { checkDate(day_, month_, year_, true /* allow wild cards */); } DateAttr::DateAttr(const std::string& str) { DateAttr::getDate(str, day_, month_, year_); checkDate(day_, month_, year_, true /* allow wild cards */); } bool DateAttr::operator<(const DateAttr& rhs) const { if (year_ < rhs.year_) { return true; } if (year_ == rhs.year_) { if (month_ < rhs.month_) { return true; } if (month_ == rhs.month_) { return day_ < rhs.day_; } } return false; } void DateAttr::checkDate(int day, int month, int year, bool allow_wild_cards) { if (allow_wild_cards) { if (day != 0 && (day < 1 || day > 31)) { throw std::out_of_range( "Invalid Date(day,month,year) : the day >= 0 and day < 31, where 0 means wild card "); } if (month != 0 && (month < 1 || month > 12)) { throw std::out_of_range( "Invalid Date(day,month,year): the month >=0 and month <= 12, where 0 means wild card"); } if (year < 0) { throw std::out_of_range("Invalid Date(day,month,year): the year >=0, where 0 means wild card"); } } else { if (day < 1 || day > 31) { throw std::out_of_range("Invalid date attribute : the day >= 1 and day < 31"); } if (month < 1 || month > 12) { throw std::out_of_range("Invalid date attribute: the month >=1 and month <= 12"); } if (year <= 0) { throw std::out_of_range("Invalid date attribute: the year >0"); } } if (day != 0 && month != 0 && year != 0) { // The following validates the given date // -- i.e. an bad_year/_month/_day_of_month exception is thrown in case the date is invalid boost::gregorian::date(year, month, day); } } void DateAttr::calendarChanged(const ecf::Calendar& c, bool clear_at_midnight) { // See ECFLOW-337 versus ECFLOW-1550 if (c.dayChanged()) { if (clear_at_midnight) { clearFree(); } } if (free_) { return; } if (is_free(c)) { setFree(); } } void DateAttr::reset() { free_ = false; state_change_no_ = Ecf::incr_state_change_no(); } void DateAttr::requeue() { free_ = false; state_change_no_ = Ecf::incr_state_change_no(); } void DateAttr::setFree() { free_ = true; state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_STATE_CHANGE_NO std::cout << "DateAttr::setFree()\n"; #endif } void DateAttr::clearFree() { free_ = false; state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_STATE_CHANGE_NO std::cout << "DateAttr::clearFree()\n"; #endif } bool DateAttr::isFree(const ecf::Calendar& calendar) const { // The FreeDepCmd can be used to free the dates, if (free_) { return true; } return is_free(calendar); } bool DateAttr::is_free(const ecf::Calendar& calendar) const { bool dayMatches = true; bool monthMatches = true; bool yearMatches = true; if (day_ != 0) { dayMatches = calendar.day_of_month() == day_; } if (month_ != 0) { monthMatches = calendar.month() == month_; } if (year_ != 0) { yearMatches = calendar.year() == year_; } return (dayMatches && monthMatches && yearMatches); } bool DateAttr::checkForRequeue(const ecf::Calendar& calendar) const { // if calendar is hybrid, we can't re-queue if (calendar.hybrid()) { return false; } // checkForRequeue is called when we are deciding whether to re-queue the node. // If this date is in the future, we should re-queue if (day_ != 0 && month_ != 0 && year_ != 0) { return boost::gregorian::date(year_, month_, day_) > calendar.date(); } bool futureDayMatches = true; bool futureMonthMatches = true; bool futureYearMatches = true; if (day_ != 0) { futureDayMatches = day_ > calendar.day_of_month(); } if (month_ != 0) { futureMonthMatches = month_ > calendar.month(); } if (year_ != 0) { futureYearMatches = year_ > calendar.year(); } return (futureDayMatches || futureMonthMatches || futureYearMatches); } bool DateAttr::validForHybrid(const ecf::Calendar& calendar) const { if (day_ == 0) { return false; // relies on day change i.e. date *.10.2009 } if (month_ == 0) { return false; // relies on day change i.e. date 12.*.2009 } if (year_ == 0) { return false; // relies on day change i.e. date 12.10.* } // if the date matches exactly for today return (day_ == calendar.day_of_month() && month_ == calendar.month() && year_ == calendar.year()); } bool DateAttr::why(const ecf::Calendar& c, std::string& theReasonWhy) const { if (isFree(c)) { return false; } theReasonWhy += MESSAGE(" is date dependent ( next run on " << boost::gregorian::to_simple_string(next_matching_date(c)) << " the current date is " << c.day_of_month() << "/" << c.month() << "/" << c.year() << " )"); return true; } std::string DateAttr::name() const { std::string os; write(os); if (free_) { os += " # free"; } return os; } std::string DateAttr::toString() const { std::string ret; write(ret); return ret; } void DateAttr::write(std::string& ret) const { ret += "date "; if (day_ == 0) { ret += "*."; } else { ret += ecf::convert_to(day_); ret += "."; } if (month_ == 0) { ret += "*."; } else { ret += ecf::convert_to(month_); ret += "."; } if (year_ == 0) { ret += "*"; } else { ret += ecf::convert_to(year_); } } std::string DateAttr::dump() const { return MESSAGE(toString() << (free_ ? " (free)" : " (holding)")); } bool DateAttr::operator==(const DateAttr& rhs) const { if (free_ != rhs.free_) { return false; } return structureEquals(rhs); } bool DateAttr::structureEquals(const DateAttr& rhs) const { if (day_ != rhs.day_) { return false; } if (month_ != rhs.month_) { return false; } if (year_ != rhs.year_) { return false; } return true; } DateAttr DateAttr::create(const std::string& dateString) { int day = -1, month = -1, year = -1; getDate(dateString, day, month, year); return {day, month, year}; } DateAttr DateAttr::create(const std::vector& lineTokens, bool read_state) { // date 15.11.2009 # free // with PersistStyle::STATE & MIGRATE // date 15.*.* # // date *.1.* // for(size_t i =0; i < lineTokens.size() ; i++) { // cout << "lineTokens[" << i << "] = '" << lineTokens[i] << "'\n"; // } DateAttr date = DateAttr::create(lineTokens[1]); if (read_state) { for (size_t i = 3; i < lineTokens.size(); i++) { if (lineTokens[i] == "free") { date.setFree(); } } } return date; } void DateAttr::getDate(const std::string& date, int& day, int& month, int& year) { size_t firstDotPos = date.find_first_of('.'); size_t lastDotPos = date.find_first_of('.', firstDotPos + 1); if (firstDotPos == std::string::npos) { throw std::runtime_error("DateAttr::getDate Invalid date missing first dot :" + date); } if (lastDotPos == std::string::npos) { throw std::runtime_error("DateAttr::getDate: Invalid date missing second dot :" + date); } if (firstDotPos == lastDotPos) { throw std::runtime_error("DateAttr::getDate: Invalid date :" + date); } std::string theDay = date.substr(0, firstDotPos); std::string theMonth = date.substr(firstDotPos + 1, (lastDotPos - firstDotPos) - 1); std::string theYear = date.substr(lastDotPos + 1); if (theDay == "*") { day = 0; } else { day = Extract::value(theDay, "DateAttr::getDate: Invalid day :" + date); if (day < 1 || day > 31) { throw std::runtime_error("DateAttr::getDate: Invalid clock date: " + date); } } if (theMonth == "*") { month = 0; } else { month = Extract::value(theMonth, "DateAttr::getDate: Invalid month :" + date); if (month < 1 || month > 12) { throw std::runtime_error("DateAttr::getDate Invalid clock date: " + date); } } if (theYear == "*") { year = 0; } else { year = Extract::value(theYear, "DateAttr::getDate: Invalid year :" + date); } if (day == -1 || month == -1 || year == -1) { throw std::runtime_error("DateAttr::getDate: Invalid clock date:" + date); } if (day != 0 && month != 0 && year != 0) { // The following validates the given date // -- i.e. an bad_year/_month/_day_of_month exception is thrown in case the date is invalid boost::gregorian::date(year, month, day); } } boost::gregorian::date DateAttr::next_matching_date(const ecf::Calendar& c) const { auto next_matching_date = c.date(); // today's date auto one_day = boost::gregorian::date_duration(1); bool day_matches = (day_ == 0) ? true : false; bool month_matches = (month_ == 0) ? true : false; bool year_matches = (year_ == 0) ? true : false; for (int i = 0; i < 365; i++) { next_matching_date += one_day; if (day_ != 0 && next_matching_date.day() == day_) { day_matches = true; } if (month_ != 0 && next_matching_date.month() == month_) { month_matches = true; } if (year_ != 0 && next_matching_date.year() == year_) { year_matches = true; } if (day_matches && month_matches && year_matches) { return next_matching_date; } } return c.date(); } template void DateAttr::serialize(Archive& ar) { ar(CEREAL_NVP(day_), CEREAL_NVP(month_), CEREAL_NVP(year_)); CEREAL_OPTIONAL_NVP(ar, free_, [this]() { return free_; }); // conditionally save } CEREAL_TEMPLATE_SPECIALIZE(DateAttr); ecflow-5.16.0/libs/attribute/src/ecflow/attribute/AutoCancelAttr.hpp0000664000175000017500000000342415161437256025652 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #ifndef ecflow_attribute_AutoCancelAttr_HPP #define ecflow_attribute_AutoCancelAttr_HPP #include #include "ecflow/core/TimeSlot.hpp" namespace ecf { class Calendar; } // namespace ecf namespace ecf { // Use compiler , destructor, assignment, copy constructor class AutoCancelAttr { public: AutoCancelAttr() = default; AutoCancelAttr(int hour, int minute, bool relative) : time_(hour, minute), relative_(relative) {} AutoCancelAttr(const TimeSlot& ts, bool relative) : time_(ts), relative_(relative) {} explicit AutoCancelAttr(int days) : time_(TimeSlot(days * 24, 0)), days_(true) {} bool operator==(const AutoCancelAttr& rhs) const; bool operator<(const AutoCancelAttr& rhs) const { return time_ < rhs.time(); } bool isFree(const ecf::Calendar&, const boost::posix_time::time_duration& suiteDurationAtComplete) const; std::string toString() const; const TimeSlot& time() const { return time_; } bool relative() const { return relative_; } bool days() const { return days_; } public: void write(std::string& ret) const; private: TimeSlot time_; bool relative_{true}; bool days_{false}; friend class cereal::access; template void serialize(Archive& ar, std::uint32_t const /*version*/); }; } // namespace ecf #endif /* ecflow_attribute_AutoCancelAttr_HPP */ ecflow-5.16.0/libs/attribute/src/ecflow/attribute/LateAttr.hpp0000664000175000017500000001102615161437256024516 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #ifndef ecflow_attribute_LateAttr_HPP #define ecflow_attribute_LateAttr_HPP #include #include // for pair #include #include "ecflow/core/Chrono.hpp" #include "ecflow/core/TimeSlot.hpp" class NState; namespace ecf { class Calendar; } // namespace ecf namespace ecf { /// ======================================================================== /// Use compiler, destructor, assignment, copy constructor, /// /// ************************************************************************ /// late attribute: Only applies to a task, it can be set on suite/family /// but this is treated as an inherited attribute. /// late attribute lower down the hierarchy overrides it. /// ************************************************************************ /// The late _late_ attribute will not work correctly when the suites clock /// start and stops with the server. Since the late relies on real time /// for some of its functionality. /// -s submitted: The time node can stay submitted (format [+]hh:mm). submitted is always /// relative, so + is simply ignored, if present. If the node stays submitted /// longer than the time specified, the late flag is set /// -a Active : The time of day the node must have become active (format hh:mm). If the node /// is still queued or submitted, the late flag is set /// -c Complete : The time node must become complete (format {+}hh:mm). If relative, time is /// taken from the time the node became active, otherwise node must be complete by /// the time given. /// ======================================================================== class LateAttr { public: LateAttr(); void print(std::string&) const; bool operator==(const LateAttr& rhs) const; void addSubmitted(const TimeSlot& s) { s_ = s; } void add_submitted(int hour, int minute) { s_ = TimeSlot(hour, minute); } void addActive(const TimeSlot& s) { a_ = s; } void add_active(int hour, int minute) { a_ = TimeSlot(hour, minute); } void addComplete(const TimeSlot& s, bool relative) { c_ = s; c_is_rel_ = relative; } void add_complete(int hour, int minute, bool relative) { c_ = TimeSlot(hour, minute); c_is_rel_ = relative; } const TimeSlot& submitted() const { return s_; } const TimeSlot& active() const { return a_; } const TimeSlot& complete() const { return c_; } bool complete_is_relative() const { return c_is_rel_; } /// i.e no time structs specified bool isNull() const; /// Given the state and time of state change, and calendar work out if we are late /// if we are sets the late flag void checkForLateness(const std::pair& state, const ecf::Calendar& c); bool check_for_lateness(const std::pair& state, const ecf::Calendar& c) const; /// To be used by GUI to inform used that a node is late bool isLate() const { return isLate_; } /// To be called at begin and re-queue time void reset() { setLate(false); } // Override this late attributes with the settings form the input. void override_with(LateAttr*); // The state_change_no is never reset. Must be incremented if it can affect equality unsigned int state_change_no() const { return state_change_no_; } /// set flag to be late void setLate(bool f); std::string toString() const; std::string name() const { return toString(); } static void parse(LateAttr&, const std::string& line, const std::vector& lineTokens, size_t index); static LateAttr create(const std::string& lateString); public: void write(std::string&) const; private: TimeSlot s_; // relative by default TimeSlot a_; TimeSlot c_; unsigned int state_change_no_{0}; // *not* persisted, only used on server side bool c_is_rel_{false}; bool isLate_{false}; private: friend class cereal::access; template void serialize(Archive& ar); }; } // namespace ecf #endif /* ecflow_attribute_LateAttr_HPP */ ecflow-5.16.0/libs/attribute/src/ecflow/attribute/RepeatAttr.hpp0000664000175000017500000007044015161437256025056 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #ifndef ecflow_attribute_RepeatAttr_HPP #define ecflow_attribute_RepeatAttr_HPP /// /// \brief Repeat Attribute. Please note that for repeat string, enumeration /// the positional index is used for evaluation. /// /// Simulation: Simulation must not affect the real job submission in the server. /// o Infinite repeats cause problems with simulation, hence we have /// a mechanism to stop this, when reset is called, via server this is disabled /// #include #include #include #include #include #include #include #include "ecflow/attribute/Variable.hpp" #include "ecflow/core/Chrono.hpp" ///////////////////////////////////////////////////////////////////////// // Node can only have one repeat. // class RepeatBase { public: explicit RepeatBase(const std::string& name) : name_(name) {} RepeatBase() = default; virtual ~RepeatBase(); /// make non-virtual so that it can be in-lined. Called millions of times const std::string& name() const { return name_; } virtual int start() const = 0; virtual int end() const = 0; virtual int step() const = 0; // Handle generated variables virtual void gen_variables(std::vector& vec) const { vec.push_back(var_); } virtual const Variable& find_gen_variable(const std::string& name) const { return name == name_ ? var_ : Variable::EMPTY(); } virtual void update_repeat_genvar() const; // After Repeat expiration the last call to increment() can cause // value to be beyond the last valid value // Depending on the kind of repeat the returned can be value or the current index // RepeatDate -> value // RepeatDateTime -> value // RepeatDateList -> value // RepeatString -> index into array of strings // RepeatInteger -> value // RepeatEnumerated -> index | value return value at index if cast-able to integer, otherwise return index ****** // RepeatDay -> value virtual long value() const = 0; // Depending on the kind of repeat the returned can be value or *current* index // RepeatDate -> value // RepeatDateTime -> value // RepeatDateList -> value // RepeatString -> index ( will always return an index) // RepeatInteger -> value // RepeatEnumerated -> index ( will always return an index) // RepeatDay -> value virtual long index_or_value() const = 0; virtual void increment() = 0; // returns a value with in the range start/end // Hence at Repeat expiration will return value associated with end() virtual long last_valid_value() const = 0; virtual long last_valid_value_minus(int val) const { return last_valid_value() - val; } virtual long last_valid_value_plus(int val) const { return last_valid_value() + val; } virtual RepeatBase* clone() const = 0; virtual bool compare(RepeatBase*) const = 0; virtual bool valid() const = 0; virtual void setToLastValue() = 0; virtual std::string valueAsString() const = 0; // uses last_valid_value virtual std::string value_as_string(int index) const = 0; // used in test only virtual std::string next_value_as_string() const = 0; virtual std::string prev_value_as_string() const = 0; virtual void reset() = 0; virtual void change(const std::string& newValue) = 0; // can throw std::runtime_error virtual void changeValue(long newValue) = 0; // can throw std::runtime_error virtual void set_value(long new_value_or_index) = 0; // will NOT throw, allows any value std::string toString() const; virtual std::string dump() const = 0; unsigned int state_change_no() const { return state_change_no_; } /// Simulator functions: virtual bool isInfinite() const = 0; virtual bool is_repeat_day() const { return false; } virtual bool isDate() const { return false; } virtual bool isDateTime() const { return false; } virtual bool isDateList() const { return false; } virtual bool isInteger() const { return false; } virtual bool isEnumerated() const { return false; } virtual bool isString() const { return false; } virtual bool isDay() const { return false; } protected: void incr_state_change_no(); mutable Variable var_; // *not* persisted std::string name_; unsigned int state_change_no_{0}; // *not* persisted, only used on server side private: friend class cereal::access; template void serialize(Archive& ar); }; /// /// The date has no meaning in a physical sense, only used as a for loop over dates class RepeatDate final : public RepeatBase { public: RepeatDate(const std::string& variable, int start, int end, int delta = 1 /* always in days*/); RepeatDate() = default; void gen_variables(std::vector& vec) const override; const Variable& find_gen_variable(const std::string& name) const override; void update_repeat_genvar() const override; int start() const override { return start_; } int end() const override { return end_; } int step() const override { return delta_; } long value() const override { return value_; } long index_or_value() const override { return value_; } long last_valid_value() const override; long last_valid_value_minus(int value) const override; long last_valid_value_plus(int value) const override; void delta(int d) { delta_ = d; } int delta() const { return delta_; } bool operator==(const RepeatDate& rhs) const; bool operator<(const RepeatDate& rhs) const { return name() < rhs.name(); } RepeatDate* clone() const override { return new RepeatDate(name_, start_, end_, delta_, value_); } bool compare(RepeatBase*) const override; bool valid() const override { return (delta_ > 0) ? (value_ <= end_) : (value_ >= end_); } std::string valueAsString() const override; std::string value_as_string(int index) const override; std::string next_value_as_string() const override; std::string prev_value_as_string() const override; void setToLastValue() override; void reset() override; void increment() override; void change(const std::string& newValue) override; // can throw std::runtime_error void changeValue(long newValue) override; // can throw std::runtime_error void set_value(long newValue) override; // will NOT throw, allows any value std::string dump() const override; bool isDate() const override { return true; } /// Simulator functions: bool isInfinite() const override { return false; } private: long valid_value(long value) const; RepeatDate(const std::string& name, int start, int end, int delta, long value) : RepeatBase(name), start_(start), end_(end), delta_(delta), value_(value) {} void update_repeat_genvar_value() const; private: int start_{0}; int end_{0}; int delta_{0}; long value_{0}; mutable Variable yyyy_; // *not* persisted mutable Variable mm_; // *not* persisted mutable Variable dom_; // *not* persisted mutable Variable dow_; // *not* persisted mutable Variable julian_; // *not* persisted friend class cereal::access; template void serialize(Archive& ar, std::uint32_t const version); }; class RepeatDateTime final : public RepeatBase { public: RepeatDateTime(const std::string& variable, const std::string& start, const std::string& end, const std::string& delta = "24:00:00"); RepeatDateTime(const std::string& variable, ecf::Instant start, ecf::Instant end, ecf::Duration delta); RepeatDateTime() = default; void gen_variables(std::vector& vec) const override; const Variable& find_gen_variable(const std::string& name) const override; void update_repeat_genvar() const override; const ecf::Instant& start_instant() const { return start_; } const ecf::Instant& end_instant() const { return end_; } const ecf::Duration& step_duration() const { return delta_; } const ecf::Instant& value_instant() const { return value_; } int start() const override { return coerce_from_instant_into_seconds(start_); } int end() const override { return coerce_from_instant_into_seconds(end_); } int step() const override { return delta_.as_seconds().count(); } long value() const override { return coerce_from_instant_into_seconds(value_); } long index_or_value() const override { return coerce_from_instant_into_seconds(value_); } long last_valid_value() const override; long last_valid_value_minus(int value) const override; long last_valid_value_plus(int value) const override; void delta(const ecf::Duration& d) { delta_ = d; } bool operator==(const RepeatDateTime& rhs) const; bool operator<(const RepeatDateTime& rhs) const { return name() < rhs.name(); } RepeatDateTime* clone() const override { return new RepeatDateTime(name_, start_, end_, delta_, value_); } bool compare(RepeatBase*) const override; bool valid() const override { return (delta_ > ecf::Duration{std::chrono::seconds{0}}) ? (value_ <= end_) : (value_ >= end_); } std::string valueAsString() const override; std::string value_as_string(int index) const override; std::string next_value_as_string() const override; std::string prev_value_as_string() const override; void setToLastValue() override; void reset() override; void increment() override; void change(const std::string& newValue) override; // can throw std::runtime_error void changeValue(long newValue) override; // can throw std::runtime_error void set_value(long newValue) override; // will NOT throw, allows any value std::string dump() const override; bool isDateTime() const override { return true; } /// Simulator functions: bool isInfinite() const override { return false; } private: ecf::Instant valid_value(const ecf::Instant& value) const; RepeatDateTime(const std::string& name, ecf::Instant start, ecf::Instant end, ecf::Duration delta, ecf::Instant value) : RepeatBase(name), start_(start), end_(end), delta_(delta), value_(value) {} void update_repeat_genvar_value() const; private: ecf::Instant start_; ecf::Instant end_; ecf::Duration delta_; ecf::Instant value_; // *not* persisted mutable VariableMap generated_{ // clang-format off // Date Variable(name_ + "_DATE", ""), // Date Components Variable(name_ + "_YYYY", ""), Variable(name_ + "_MM", ""), Variable(name_ + "_DD", ""), Variable(name_ + "_JULIAN", ""), // Time Variable(name_ + "_TIME", ""), // Time Components4 Variable(name_ + "_HOURS", ""), Variable(name_ + "_MINUTES", ""), Variable(name_ + "_SECONDS", "") // clang-format on }; friend class cereal::access; template void serialize(Archive& ar, std::uint32_t const version); }; class RepeatDateList final : public RepeatBase { public: RepeatDateList(const std::string& variable, const std::vector&); // will throw for empty list RepeatDateList() = default; void gen_variables(std::vector& vec) const override; const Variable& find_gen_variable(const std::string& name) const override; void update_repeat_genvar() const override; bool operator==(const RepeatDateList& rhs) const; bool operator<(const RepeatDateList& rhs) const { return name() < rhs.name(); } int start() const override; int end() const override; int step() const override { return 1; } long value() const override; // return value at index otherwise return 0 long value_at(size_t i) const { assert(i < list_.size()); return list_[i]; }; long index_or_value() const override { return currentIndex_; } long last_valid_value() const override; long last_valid_value_minus(int value) const override; long last_valid_value_plus(int value) const override; RepeatBase* clone() const override { return new RepeatDateList(name_, list_, currentIndex_); } bool compare(RepeatBase*) const override; bool valid() const override { return (currentIndex_ >= 0 && currentIndex_ < static_cast(list_.size())); } std::string valueAsString() const override; std::string value_as_string(int index) const override; std::string next_value_as_string() const override; std::string prev_value_as_string() const override; const std::vector& values() const { return list_; } void setToLastValue() override; void reset() override; void increment() override; void change(const std::string& newValue) override; // can throw std::runtime_error void changeValue(long newValue) override; // can throw std::runtime_error void set_value(long newValue) override; // will NOT throw, allows any value std::string dump() const override; bool isDateList() const override { return true; } int indexNum() const { return static_cast(list_.size()); } /// Simulator functions: bool isInfinite() const override { return false; } private: RepeatDateList(const std::string& variable, const std::vector& l, int index) : RepeatBase(variable), currentIndex_(index), list_(l) {} void update_repeat_genvar_value() const; private: int currentIndex_{0}; std::vector list_; mutable Variable yyyy_; // *not* persisted mutable Variable mm_; // *not* persisted mutable Variable dom_; // *not* persisted mutable Variable dow_; // *not* persisted mutable Variable julian_; // *not* persisted friend class cereal::access; template void serialize(Archive& ar, std::uint32_t const version); }; class RepeatInteger final : public RepeatBase { public: RepeatInteger(const std::string& variable, int start, int end, int delta = 1); RepeatInteger(); bool operator==(const RepeatInteger& rhs) const; bool operator<(const RepeatInteger& rhs) const { return name() < rhs.name(); } int start() const override { return start_; } int end() const override { return end_; } int step() const override { return delta_; } long value() const override { return value_; } long index_or_value() const override { return value_; } long last_valid_value() const override; int delta() const { return delta_; } RepeatInteger* clone() const override { return new RepeatInteger(name_, start_, end_, delta_, value_); } bool compare(RepeatBase*) const override; bool valid() const override { return (delta_ > 0) ? (value_ <= end_) : (value_ >= end_); } std::string valueAsString() const override; std::string value_as_string(int index) const override; std::string next_value_as_string() const override; std::string prev_value_as_string() const override; void setToLastValue() override; void reset() override; void increment() override; void change(const std::string& newValue) override; // can throw std::runtime_error void changeValue(long newValue) override; // can throw std::runtime_error void set_value(long newValue) override; // will NOT throw, allows any value std::string dump() const override; bool isInteger() const override { return true; } /// Simulator functions: bool isInfinite() const override { return false; } private: long valid_value(long value) const; RepeatInteger(const std::string& name, int start, int end, int delta, long value) : RepeatBase(name), start_(start), end_(end), delta_(delta), value_(value) {} private: int start_{0}; int end_{0}; int delta_{0}; long value_{0}; friend class cereal::access; template void serialize(Archive& ar, std::uint32_t const version); }; // Note:: Difference between RepeatEnumerated and RepeatString, is that // RepeatEnumerated::value() will return the value at the index if cast-able to integer, // whereas RepeatString::value() will always return the index. class RepeatEnumerated final : public RepeatBase { public: RepeatEnumerated(const std::string& variable, const std::vector& theEnums); RepeatEnumerated() = default; bool operator==(const RepeatEnumerated& rhs) const; bool operator<(const RepeatEnumerated& rhs) const { return name() < rhs.name(); } int start() const override { return 0; } int end() const override; int step() const override { return 1; } long value() const override; // return value at index if cast-able to integer, otherwise return index long index_or_value() const override { return currentIndex_; } long last_valid_value() const override; const std::vector& values() const { return theEnums_; } RepeatBase* clone() const override { return new RepeatEnumerated(name_, theEnums_, currentIndex_); } bool compare(RepeatBase*) const override; bool valid() const override { return (currentIndex_ >= 0 && currentIndex_ < static_cast(theEnums_.size())); } std::string valueAsString() const override; std::string value_as_string(int index) const override; std::string next_value_as_string() const override; std::string prev_value_as_string() const override; void setToLastValue() override; void reset() override; void increment() override; void change(const std::string& newValue) override; // can throw std::runtime_error void changeValue(long newValue) override; // can throw std::runtime_error void set_value(long newValue) override; // will NOT throw, allows any value std::string dump() const override; bool isEnumerated() const override { return true; } int indexNum() const { return static_cast(theEnums_.size()); } /// Simulator functions: bool isInfinite() const override { return false; } private: RepeatEnumerated(const std::string& variable, const std::vector& theEnums, int index) : RepeatBase(variable), currentIndex_(index), theEnums_(theEnums) {} private: int currentIndex_{0}; std::vector theEnums_; friend class cereal::access; template void serialize(Archive& ar, std::uint32_t const version); }; class RepeatString final : public RepeatBase { public: RepeatString(const std::string& variable, const std::vector& theEnums); RepeatString() = default; bool operator==(const RepeatString& rhs) const; bool operator<(const RepeatString& rhs) const { return name() < rhs.name(); } int start() const override { return 0; } int end() const override; int step() const override { return 1; } long value() const override { return currentIndex_; } long index_or_value() const override { return currentIndex_; } long last_valid_value() const override; // returns the index RepeatBase* clone() const override { return new RepeatString(name_, theStrings_, currentIndex_); } bool compare(RepeatBase*) const override; bool valid() const override { return (currentIndex_ >= 0 && currentIndex_ < static_cast(theStrings_.size())); } std::string valueAsString() const override; std::string value_as_string(int index) const override; std::string next_value_as_string() const override; std::string prev_value_as_string() const override; const std::vector& values() const { return theStrings_; } void setToLastValue() override; void reset() override; void increment() override; void change(const std::string& newValue) override; // can throw std::runtime_error void changeValue(long newValue) override; // can throw std::runtime_error void set_value(long newValue) override; // will NOT throw, allows any value std::string dump() const override; bool isString() const override { return true; } int indexNum() const { return static_cast(theStrings_.size()); } /// Simulator functions: bool isInfinite() const override { return false; } private: RepeatString(const std::string& variable, const std::vector& theEnums, int index) : RepeatBase(variable), currentIndex_(index), theStrings_(theEnums) {} private: int currentIndex_{0}; std::vector theStrings_; friend class cereal::access; template void serialize(Archive& ar, std::uint32_t const version); }; /// The current repeat day is not that well-defined or deterministic. /// **** Currently I have not come across any suites that use an end-date **** /// o If the suite has defined a real clock /// then number of repeats is deterministic /// However if the end-date is less than suite clock this should be reported as error /// o Currently under the hybrid clock the date is not updated, this raises /// a whole-lot of issues. (We don't want a separate calendar, just for this). /// o If there is _no_ suite clock, then we must use the current day /// now the number of repeats varies, and if end-date is less than the current /// day we need to report an error /// It is not clear what behaviour is required here, hence I will not implement /// the end-date functionality. Until there is clear requirement is this area. /// end-date will be treated as a parser error. /// The minimum deterministic functionality here is to implement the infinite /// repeat that has no end date /// /// RepeatDay do not really have a name: However we need maintain invariant that all NON-empty repeats /// have a name. Hence, the name will be as day /// Note: this applies to the clone as well class RepeatDay final : public RepeatBase { public: RepeatDay() : RepeatBase("day") {} // Enable implicit conversion from integer RepeatDay(int step) : RepeatBase("day"), step_(step) {} bool operator==(const RepeatDay& rhs) const; bool operator<(const RepeatDay& rhs) const { return step_ < rhs.step(); } int start() const override { return 0; } int end() const override { return 0; } int step() const override { return step_; } void increment() override { /* do nothing */ } long value() const override { return step_; } long index_or_value() const override { return step_; } long last_valid_value() const override { return step_; } RepeatBase* clone() const override { return new RepeatDay(step_, valid_); } bool compare(RepeatBase*) const override; bool valid() const override { return valid_; } std::string valueAsString() const override { return std::string{}; }; std::string value_as_string(int) const override { return std::string{}; } std::string next_value_as_string() const override { return std::string{}; } std::string prev_value_as_string() const override { return std::string{}; } void setToLastValue() override { /* do nothing ?? */ } void reset() override { valid_ = true; } void change(const std::string& /*newValue*/) override { /* do nothing */ } void changeValue(long /*newValue*/) override { /* do nothing */ } void set_value(long /*newValue*/) override { /* do nothing */ } std::string dump() const override; bool isDay() const override { return true; } /// Simulator functions: bool isInfinite() const override { return true; } bool is_repeat_day() const override { return true; } private: RepeatDay(int step, bool valid) : RepeatBase("day"), step_(step), valid_(valid) {} private: int step_{1}; bool valid_{true}; // not persisted since only used in simulator friend class cereal::access; template void serialize(Archive& ar, std::uint32_t const version); }; class Repeat { public: Repeat(); // for serialisation // Enable implicit conversion to Repeat Repeat(const RepeatDate&); Repeat(const RepeatDateTime&); Repeat(const RepeatDateList&); Repeat(const RepeatInteger&); Repeat(const RepeatEnumerated&); Repeat(const RepeatString&); Repeat(const RepeatDay&); // Enable copy & move semantics Repeat(const Repeat&); Repeat(Repeat&& rhs); Repeat& operator=(Repeat rhs); ~Repeat(); bool operator==(const Repeat& rhs) const; bool operator<(const Repeat& rhs) const { return name() < rhs.name(); } bool empty() const { return (type_) ? false : true; } void clear() { type_.reset(nullptr); } const std::string& name() const; void gen_variables(std::vector& vec) const { if (type_) { type_->gen_variables(vec); } } const Variable& find_gen_variable(const std::string& name) const { return (type_) ? type_->find_gen_variable(name) : Variable::EMPTY(); } void update_repeat_genvar() const { if (type_) { type_->update_repeat_genvar(); } } int start() const { return (type_) ? type_->start() : 0; } int end() const { return (type_) ? type_->end() : 0; } int step() const { return (type_) ? type_->step() : 0; } long value() const { return (type_) ? type_->value() : 0; } long index_or_value() const { return (type_) ? type_->index_or_value() : 0; } long last_valid_value() const { return (type_) ? type_->last_valid_value() : 0; } long last_valid_value_minus(int val) const { return (type_) ? type_->last_valid_value_minus(val) : -val; } long last_valid_value_plus(int val) const { return (type_) ? type_->last_valid_value_plus(val) : val; } bool valid() const { return (type_) ? type_->valid() : false; } void setToLastValue() { if (type_) { type_->setToLastValue(); } } std::string valueAsString() const { return (type_) ? type_->valueAsString() : std::string{}; } std::string value_as_string(int index) const { return (type_) ? type_->value_as_string(index) : std::string{}; } std::string next_value_as_string() const { return (type_) ? type_->next_value_as_string() : std::string{}; } std::string prev_value_as_string() const { return (type_) ? type_->prev_value_as_string() : std::string{}; } void reset() { if (type_) { type_->reset(); } } void increment() { if (type_) { type_->increment(); } } void change(const std::string& newValue) { if (type_) { type_->change(newValue); } } void changeValue(long newValue) { if (type_) { type_->changeValue(newValue); } } void set_value(long newValue) { if (type_) { type_->set_value(newValue); } } std::string toString() const { return (type_) ? type_->toString() : std::string{}; } std::string dump() const { return (type_) ? type_->dump() : std::string{}; } // additional state unsigned int state_change_no() const { return (type_) ? type_->state_change_no() : 0; } /// simulator functions: bool isInfinite() const { return (type_) ? type_->isInfinite() : false; } // Allows Repeat's to be returned by reference static const Repeat& EMPTY(); bool is_repeat_day() const { return (type_) ? type_->is_repeat_day() : false; } /// Expose base for the GUI only, use with caution RepeatBase* repeatBase() { return type_.get(); } const RepeatBase* repeatBase() const { return type_.get(); } template const T& as() const { return dynamic_cast(*repeatBase()); } private: std::unique_ptr type_; friend class cereal::access; template void serialize(Archive& ar, std::uint32_t const version); }; #endif /* ecflow_attribute_RepeatAttr_HPP */ ecflow-5.16.0/libs/attribute/src/ecflow/attribute/VerifyAttr.hpp0000664000175000017500000000346415161437256025104 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #ifndef ecflow_attribute_VerifyAttr_HPP #define ecflow_attribute_VerifyAttr_HPP #include "ecflow/core/NState.hpp" // Class VerifyAttr: // This class is only used for testing/verification purposes. It allows us to // embed expected number of states, within the definition file and so // reduce the need for golden log files. // Use compiler , generated destructor, assignment, copy constructor class VerifyAttr { public: VerifyAttr(NState::State state, int expected, int actual = 0) : state_(state), expected_(expected), actual_(actual), state_change_no_(0) {} VerifyAttr() = default; bool operator==(const VerifyAttr& rhs) const; void print(std::string&) const; NState::State state() const { return state_; } int expected() const { return expected_; } int actual() const { return actual_; } void incrementActual(); void reset(); // The state_change_no is never reset. Must be incremented if it can affect equality unsigned int state_change_no() const { return state_change_no_; } std::string toString() const; std::string dump() const; private: NState::State state_{NState::UNKNOWN}; int expected_{0}; int actual_{0}; unsigned int state_change_no_{0}; // *not* persisted, only used on server side friend class cereal::access; template void serialize(Archive& ar); }; #endif /* ecflow_attribute_VerifyAttr_HPP */ ecflow-5.16.0/libs/attribute/src/ecflow/attribute/CronAttr.cpp0000664000175000017500000007460515161437256024541 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "ecflow/attribute/CronAttr.hpp" #include #include #include #include "ecflow/core/Calendar.hpp" #include "ecflow/core/Converter.hpp" #include "ecflow/core/Ecf.hpp" #include "ecflow/core/Message.hpp" #include "ecflow/core/Serialization.hpp" #include "ecflow/core/Str.hpp" using namespace ecf; // #define DEBUG_CRON_ATTR 1 // #define DEBUG_CRON_PARSING 1 // #define DEBUG_CRON_SIM 1 namespace ecf { CronAttr::CronAttr() = default; CronAttr::CronAttr(const std::string& str) { if (str.empty()) { throw std::runtime_error("CronAttr::CronAttr : empty string passed"); } std::vector tokens; Str::split(str, tokens); if (tokens.empty()) { throw std::runtime_error("CronAttr::CronAttr : incorrect time string ?"); } size_t index = 0; timeSeries_ = TimeSeries::create(index, tokens, false /*parse_state*/); } void CronAttr::addWeekDays(const std::vector& w) { weekDays_ = w; for (int day : weekDays_) { if (day < 0 || day > 6) { throw std::out_of_range( MESSAGE("Invalid range for day(" << day << ") of the week expected range is 0==Sun to 6==Sat")); } auto result = std::find(std::begin(last_week_days_of_month_), std::end(last_week_days_of_month_), day); if (result != std::end(last_week_days_of_month_)) { throw std::runtime_error( MESSAGE("Duplicate day(" << day << ") of the week also found in last week day of the month")); } } } void CronAttr::add_last_week_days_of_month(const std::vector& w) { last_week_days_of_month_ = w; for (int day : last_week_days_of_month_) { if (day < 0 || day > 6) { throw std::out_of_range( MESSAGE("Invalid range for day(" << day << ") of the week expected range is 0==Sun to 6==Sat")); } auto result = std::find(std::begin(weekDays_), std::end(weekDays_), day); if (result != std::end(weekDays_)) { throw std::runtime_error( MESSAGE("Duplicate last week day (" << day << ") of the month also found in week day")); } } } void CronAttr::addDaysOfMonth(const std::vector& d) { daysOfMonth_ = d; for (int day_of_month : daysOfMonth_) { if (day_of_month < 1 || day_of_month > 31) { throw std::out_of_range( MESSAGE("Invalid range for day of month(" << day_of_month << ") expected range is 1-31")); } } } void CronAttr::addMonths(const std::vector& m) { months_ = m; for (int month : months_) { if (month < 1 || month > 12) { throw std::out_of_range( MESSAGE("Invalid range for month(" << month << ") expected range is 1==Jan to 12==Dec")); } } } std::string CronAttr::name() const { std::string ret; write(ret); timeSeries_.write_state_for_gui(ret, free_); return ret; } std::string CronAttr::toString() const { std::string ret; write(ret); return ret; } void CronAttr::write(std::string& ret) const { ret += "cron "; if (!weekDays_.empty()) { ret += "-w "; for (size_t i = 0; i < weekDays_.size(); ++i) { ret += ecf::convert_to(weekDays_[i]); if (i != weekDays_.size() - 1) { ret += ","; } } if (last_week_days_of_month_.empty()) { ret += " "; } else { ret += ","; } } if (!last_week_days_of_month_.empty()) { if (weekDays_.empty()) { ret += "-w "; } for (size_t i = 0; i < last_week_days_of_month_.size(); ++i) { ret += ecf::convert_to(last_week_days_of_month_[i]); ret += 'L'; if (i != last_week_days_of_month_.size() - 1) { ret += ","; } } ret += " "; } if (!daysOfMonth_.empty()) { ret += "-d "; for (size_t i = 0; i < daysOfMonth_.size(); ++i) { ret += ecf::convert_to(daysOfMonth_[i]); if (i != daysOfMonth_.size() - 1) { ret += ","; } } if (!last_day_of_month_) { ret += " "; } } if (last_day_of_month_) { if (daysOfMonth_.empty()) { ret += "-d L "; } else { ret += ",L "; } } if (!months_.empty()) { ret += "-m "; for (size_t i = 0; i < months_.size(); ++i) { ret += ecf::convert_to(months_[i]); if (i != months_.size() - 1) { ret += ","; } } ret += " "; } timeSeries_.write(ret); // no new line added, up to caller } std::string CronAttr::dump() const { return MESSAGE(toString() << (free_ ? " (free)" : " (holding)")); } bool CronAttr::operator==(const CronAttr& rhs) const { if (free_ != rhs.free_) { return false; } if (last_day_of_month_ != rhs.last_day_of_month_) { return false; } if (weekDays_ != rhs.weekDays_) { return false; } if (last_week_days_of_month_ != rhs.last_week_days_of_month_) { return false; } if (daysOfMonth_ != rhs.daysOfMonth_) { return false; } if (months_ != rhs.months_) { return false; } return timeSeries_.operator==(rhs.timeSeries_); } bool CronAttr::structureEquals(const CronAttr& rhs) const { if (last_day_of_month_ != rhs.last_day_of_month_) { return false; } if (weekDays_ != rhs.weekDays_) { return false; } if (daysOfMonth_ != rhs.daysOfMonth_) { return false; } if (last_week_days_of_month_ != rhs.last_week_days_of_month_) { return false; } if (months_ != rhs.months_) { return false; } return timeSeries_.structureEquals(rhs.timeSeries_); } void CronAttr::calendarChanged(const ecf::Calendar& c) { // ensure this called first , since we need always update for relative duration ECFLOW-1648 // This assumes that calendarChanged will set TimeSeries::isValid = true, at day change if (timeSeries_.calendarChanged(c)) { state_change_no_ = Ecf::incr_state_change_no(); } if (free_) { return; } // Once a cron is free, it stays free until re-queue if (isFree(c)) { setFree(); } // A cron is always re-queueable, hence we use isFree to control when it can actually run. } void CronAttr::resetRelativeDuration() { if (timeSeries_.resetRelativeDuration()) { state_change_no_ = Ecf::incr_state_change_no(); } } void CronAttr::setFree() { free_ = true; state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_STATE_CHANGE_NO std::cout << "CronAttr::setFree()\n"; #endif } void CronAttr::clearFree() { free_ = false; state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_STATE_CHANGE_NO std::cout << "CronAttr::clearFree()\n"; #endif } void CronAttr::miss_next_time_slot() { // A cron attribute with a single time slot is repeated indefinitely hence always re-queues timeSeries_.miss_next_time_slot(); state_change_no_ = Ecf::incr_state_change_no(); } // ************************************************************************************** // FOR DEBUG THIS IS THE MAIN FUNCTION, AS THIS DECIDES WHETHER WE CONTINUE OR STOP // ************************************************************************************** bool CronAttr::checkForRequeue(const ecf::Calendar& calendar) const { // checkForRequeue is called when a task/family has reach the complete state // This simple checks if node should be put in re-queued state // A cron is always re-queueable // Hence, in order to use this it should be used in conjunction // with a parent node that has complete expression, (& maybe a dummy task) // This will allow its use with a parent repeat somewhere in the hierarchy return true; } bool CronAttr::validForHybrid(const ecf::Calendar& calendar) const { if (timeSeries_.hasIncrement()) { if (last_day_of_month_) { return false; // relies on day change } if (!months_.empty()) { return false; // relies on day change } if (!daysOfMonth_.empty()) { return false; // relies on day change } if (!weekDays_.empty()) { if (weekDays_.size() != 1) { return false; // relies on day change } return (weekDays_[0] == calendar.day_of_week()); } // cron 10:00 20:00 01:00 // valid for hybrid ? return true; } // A time series that does not have an increment runs indefinitely and hence relies on day change // cron 23:00 return false; } bool CronAttr::why(const ecf::Calendar& c, std::string& theReasonWhy) const { // This will logically AND all the times if (isFree(c)) { return false; } // We are here because: // 1/ Not on a valid time slot in the time series // *OR* // 2/ Logical *AND* of day of week, day of month, or month returned false theReasonWhy += "is cron dependent"; // Let's start by assuming that the time series was NOT free. // First check if week day, day of month, month, matches if (is_day_of_week_day_of_month_and_month_free(c)) { if (timeSeries_.is_valid()) { // This can apply to single and series boost::posix_time::time_duration calendar_time = timeSeries_.duration(c); if (calendar_time < timeSeries_.start().duration()) { timeSeries_.why(c, theReasonWhy); return true; } // calendar_time >= timeSeries_.start().duration() if (timeSeries_.hasIncrement()) { if (calendar_time < timeSeries_.finish().duration()) { timeSeries_.why(c, theReasonWhy); return true; } } } // calendar_time >= timeSeries_.start().duration() && calendar_time >= timeSeries_.finish().duration() // past the end of time slot, find next valid date } // take into account, user can use run/force complete to miss time slots bool do_a_requeue = timeSeries_.requeueable(c); if (do_a_requeue && weekDays_.empty() && daysOfMonth_.empty() && months_.empty()) { TimeSlot the_next_time_slot = timeSeries_.compute_next_time_slot(c); if (the_next_time_slot.isNULL()) { theReasonWhy += " ( *re-queue* to run at this time "; } else { theReasonWhy += " ( *re-queue* to run at "; theReasonWhy += the_next_time_slot.toString(); } theReasonWhy += ", otherwise next run is at "; } else { theReasonWhy += " ( next run is at "; } // Find the *NEXT* date that matches, and use the first time slot auto the_next_date = next_date(c); theReasonWhy += timeSeries_.start().toString(); theReasonWhy += " "; theReasonWhy += to_simple_string(the_next_date); std::ostringstream ss; TimeSlot currentTime = TimeSlot(timeSeries_.duration(c)); ss << ", current time "; if (timeSeries_.relative()) { ss << "+"; } ss << currentTime.toString() << " " << to_simple_string(c.date()) << " )"; theReasonWhy += ss.str(); return true; } void CronAttr::reset_only() { clearFree(); timeSeries_.reset_only(); } void CronAttr::reset(const ecf::Calendar& c) { clearFree(); timeSeries_.reset(c); } void CronAttr::requeue(const ecf::Calendar& c, bool reset_next_time_slot) { clearFree(); timeSeries_.requeue(c, reset_next_time_slot); } bool CronAttr::isFree(const ecf::Calendar& c) const { // The FreeDepCmd can be used to free the crons, if (free_) { return true; } if (!timeSeries_.isFree(c)) { return false; } // Ok time series is Free // ******************************************************************** // IMPORTANT: When there are multiple week days, days of month and months, // the attribute is *ONLY* free, if *ALL* are free, i.e. we need Boolean AND behaviour // ******************************************************************** return is_day_of_week_day_of_month_and_month_free(c); } bool CronAttr::is_day_of_week_day_of_month_and_month_free(const ecf::Calendar& c) const { #ifdef DEBUG_CRON_SIM cout << toString() << " cal : " << to_simple_string(c.date()) << " c.day_of_week:" << c.day_of_week() << " c.day_of_month:" << c.day_of_month(); cout.flush(); #endif bool the_week_day_matches = weekDays_.empty() && last_week_days_of_month_.empty(); // week day matches if no week days bool the_day_of_month_matches = daysOfMonth_.empty(); // day of month if no days of month bool the_month_matches = months_.empty(); // month matches if no months if (!weekDays_.empty()) { the_week_day_matches = week_day_matches(c.day_of_week()); } if (!the_week_day_matches && !last_week_days_of_month_.empty()) { the_week_day_matches = last_week_day_of_month_matches(c); } if (!daysOfMonth_.empty() || last_day_of_month_) { the_day_of_month_matches = day_of_month_matches(c.day_of_month(), c); } if (!months_.empty()) { the_month_matches = month_matches(c.month()); } // Remember we *AND* across -w, -d, -m or *OR* for each element in -w, -d,-m bool matches = false; if (daysOfMonth_.empty() && !last_day_of_month_ && months_.empty()) { // cron -w 0L 10:00 # run on the last sunday of each month matches = the_week_day_matches; // only week day, } else { matches = the_week_day_matches && the_day_of_month_matches && the_month_matches; } #ifdef DEBUG_CRON_SIM if (matches) { cout << " *MATCHES*"; } cout << "\n"; #endif return matches; } bool CronAttr::week_day_matches(int theDayOfWeek) const { for (int theWeekDay : weekDays_) { if (theDayOfWeek == theWeekDay) { return true; } } return false; } bool CronAttr::last_week_day_of_month_matches(const ecf::Calendar& c) const { int cal_day_of_week = c.day_of_week(); auto last_day_of_month = c.date().end_of_month(); auto diff_current_date_and_last_day_of_month = last_day_of_month - c.date(); for (int cron_last_week_day_of_month : last_week_days_of_month_) { if (cal_day_of_week == cron_last_week_day_of_month) { if (diff_current_date_and_last_day_of_month.days() < 7) { return true; } } } return false; } bool CronAttr::day_of_month_matches(int theDayOfMonth, const ecf::Calendar& c) const { for (int dayOfMonth : daysOfMonth_) { if (theDayOfMonth == dayOfMonth) { return true; } } if (last_day_of_month_) { return c.date() == c.date().end_of_month(); } return false; } bool CronAttr::month_matches(int theMonth) const { for (int month : months_) { if (theMonth == month) { return true; } } return false; } //------------------------------------------------------------------ bool CronAttr::checkInvariants(std::string& errormsg) const { return timeSeries_.checkInvariants(errormsg); } //-------------------------------------------------------------- boost::gregorian::date CronAttr::next_date(const ecf::Calendar& calendar) const { // Find the next date that matches, day of week, day of year, and month // that is greater than today's date. // This *ASSUMES* day of week, day of month, and month are evaluated with _AND_ together auto one_day = boost::gregorian::date_duration(1); auto future_date = calendar.date(); // today's date #ifdef DEBUG_CRON_SIM cout << "cron : " << toString() << "\n"; cout << "future_date start : " << to_simple_string(future_date) << "\n"; #endif future_date += one_day; // add one day, so its in the future while (true) { bool week_day_matches = weekDays_.empty(); // week day matches if no week days bool the_last_week_day_of_month_matches = last_week_days_of_month_.empty(); // matches if EMPTY bool day_of_month_matches = daysOfMonth_.empty(); // day of month if no days of month bool month_matches = months_.empty(); // month matches if no months if (daysOfMonth_.empty() && last_day_of_month_) { day_of_month_matches = false; } // deal with case where we have: cron -w 0,1 for (int weekDay : weekDays_) { if (future_date.day_of_week().as_number() == weekDay) { week_day_matches = true; break; } } // *IMPORTANT* the days in weekDays_ and last_week_days_of_month_ can *NOT* overlap. for (int weekDay : last_week_days_of_month_) { if (future_date.day_of_week().as_number() == weekDay) { boost::gregorian::date_duration diff = future_date - future_date.end_of_month(); if (diff.days() < 7) { the_last_week_day_of_month_matches = true; } break; } } // deal with case where we have: cron -d 14,15,16,L # L means last day of month if (!daysOfMonth_.empty() || last_day_of_month_) { for (int d : daysOfMonth_) { if (future_date.day() == d) { day_of_month_matches = true; break; } } if (last_day_of_month_ && future_date == future_date.end_of_month()) { day_of_month_matches = true; } } // deal with case where we have: cron -w 0,1 -d 14,15,16 -m 8, 9 for (int month : months_) { if (future_date.month() == month) { month_matches = true; break; } } // if it all matches, then return the future day // Remember we *AND* across -w, -d, -m or *OR* for each element in -w, -d,-m if ((week_day_matches || the_last_week_day_of_month_matches) && day_of_month_matches && month_matches) { break; // return future_date, replaced with break to keep HPUX compiler happy // otherwise it complains that return at the end of the function is // unreachable } future_date += one_day; #ifdef DEBUG_CRON_SIM cout << "future_date " << to_simple_string(future_date) << "\n"; #endif } return future_date; // should never happen, i.e. we can find future date that matches } //========================================================================================================= // code for parsing a cron: static bool isComment(const std::string& token) { if (token.find("#") == std::string::npos) { return false; } return true; } static bool isTimeSpec(const std::string& token) { if (token.find(Str::COLON()) == std::string::npos) { return false; } return true; } static bool isOption(const std::string& token) { if (token.find("-w") != std::string::npos) { return true; } if (token.find("-d") != std::string::npos) { return true; } if (token.find("-m") != std::string::npos) { return true; } return false; } static std::string nextToken(size_t& index, const std::vector& lineTokens) { assert(index < lineTokens.size()); index++; if (index < lineTokens.size()) { #ifdef DEBUG_CRON_PARSING cerr << "nextToken lineTokens[" << index << "] = " << lineTokens[index] << "\n"; #endif return lineTokens[index]; } #ifdef DEBUG_CRON_PARSING cerr << "nextToken empty \n"; #endif return std::string(); } std::string extract_list(size_t& index, const std::vector& lineTokens) { // cron -w 0,1L,2L,3 -d 1,12,14,L -m 5,6,7,8 10:00 20:00 01:00 assert(index < lineTokens.size()); // Collate the list of integers, these may have been separated by spaces // since we stop on option or time spec, the top level code should decrement index std::string theIntList; while (index < lineTokens.size() && (!isOption(lineTokens[index]) || !isTimeSpec(lineTokens[index]))) { std::string theNextToken = nextToken(index, lineTokens); if (theNextToken.empty()) { break; } if (isOption(theNextToken)) { break; } if (isTimeSpec(theNextToken)) { break; } theIntList += theNextToken; } #ifdef DEBUG_CRON_PARSING cerr << "theIntList = " << theIntList << "\n"; #endif return theIntList; } std::vector extract_month(size_t& index, const std::vector& lineTokens, const std::string& option) { // cron -w 0,1L,2L,3 -d 1,12,14,L -m 5,6,7,8 10:00 20:00 01:00 assert(index < lineTokens.size()); // Collate the list of integers, these may have been separated by spaces // since we stop on option or time spec, the top level code should decrement index std::string theIntList = extract_list(index, lineTokens); // should have 0,1,2,3 std::vector theIntVec; boost::char_separator sep(",", nullptr, boost::drop_empty_tokens); using tokenizer = boost::tokenizer>; tokenizer theTokenizer(theIntList, sep); for (tokenizer::iterator beg = theTokenizer.begin(); beg != theTokenizer.end(); ++beg) { std::string theIntToken = *beg; ecf::algorithm::trim(theIntToken); if (theIntToken.empty()) { continue; } try { auto theInt = ecf::convert_to(theIntToken); theIntVec.push_back(theInt); } catch (const ecf::bad_conversion&) { throw std::runtime_error(MESSAGE("Invalid cron option: " << option)); } } return theIntVec; } void extract_days_of_week(size_t& index, const std::vector& lineTokens, const std::string& option, std::vector& days_of_week, std::vector& last_week_days_of_month) { // cron -w 0,1L,2L,3 10:00 20:00 01:00 assert(index < lineTokens.size()); // Collate the list of integers, these may have been separated by spaces // since we stop on option or time spec, the top level code should decrement index std::string theIntList = extract_list(index, lineTokens); // should have 0,1,2,3,4L boost::char_separator sep(",", nullptr, boost::drop_empty_tokens); using tokenizer = boost::tokenizer>; tokenizer theTokenizer(theIntList, sep); for (tokenizer::iterator beg = theTokenizer.begin(); beg != theTokenizer.end(); ++beg) { std::string theIntToken = *beg; ecf::algorithm::trim(theIntToken); if (theIntToken.empty()) { continue; } try { if (theIntToken.size() == 2) { if (theIntToken[1] != 'L') { throw std::runtime_error(MESSAGE("Invalid cron option: " << option << " " << theIntToken)); } auto theInt = ecf::convert_to(theIntToken[0]); last_week_days_of_month.push_back(theInt); } else { auto theInt = ecf::convert_to(theIntToken); days_of_week.push_back(theInt); } } catch (const ecf::bad_conversion&) { throw std::runtime_error(MESSAGE("Invalid cron option: " << option)); } } } void extract_days_of_month(size_t& index, const std::vector& lineTokens, const std::string& option, std::vector& days_of_month, bool& last_day_of_month) { // cron -d 1,12,14,L 10:00 20:00 01:00 assert(index < lineTokens.size()); // Collate the list of integers, these may have been separated by spaces // since we stop on option or time spec, the top level code should decrement index std::string theIntList = extract_list(index, lineTokens); // should have 0,1,2,3,4L boost::char_separator sep(",", nullptr, boost::drop_empty_tokens); using tokenizer = boost::tokenizer>; tokenizer theTokenizer(theIntList, sep); for (tokenizer::iterator beg = theTokenizer.begin(); beg != theTokenizer.end(); ++beg) { std::string theIntToken = *beg; ecf::algorithm::trim(theIntToken); if (theIntToken.empty()) { continue; } try { if (theIntToken == "L") { last_day_of_month = true; } else { auto theInt = ecf::convert_to(theIntToken); days_of_month.push_back(theInt); } } catch (const ecf::bad_conversion&) { throw std::runtime_error(MESSAGE("Invalid cron option: " << option)); } } } void extractOption(CronAttr& cronAttr, size_t& index, const std::vector& lineTokens) { assert(index < lineTokens.size()); if (lineTokens[index] == "-w") { std::vector days_of_week; std::vector last_week_days_of_month; extract_days_of_week(index, lineTokens, "week days", days_of_week, last_week_days_of_month); cronAttr.addWeekDays(days_of_week); cronAttr.add_last_week_days_of_month(last_week_days_of_month); } else if (lineTokens[index] == "-d") { std::vector days_of_month; bool last_day_of_month = false; extract_days_of_month(index, lineTokens, "Days of the month", days_of_month, last_day_of_month); cronAttr.addDaysOfMonth(days_of_month); if (last_day_of_month) { cronAttr.add_last_day_of_month(); } } else if (lineTokens[index] == "-m") { cronAttr.addMonths(extract_month(index, lineTokens, "Months")); } else { throw std::runtime_error("extractOption: Invalid cron option :" + lineTokens[index]); } } void CronAttr::parse(CronAttr& cronAttr, const std::vector& lineTokens, size_t index, bool parse_state) { // cron 23:00 # run every day at 23:00 // cron 10:00 20:00 01:00 # run every hour between 10am and 8pm // cron -w 0,1 10:00 # run every sunday and monday at 10am // cron -d 10,11,12 12:00 # run 10th, 11th and 12th of each month at noon // cron -m 1,2,3 12:00 # run on Jan,Feb and March every day at noon. // cron -w 0 -m 5,6,7,8 10:00 20:00 01:00 # run every sunday, between May-Aug, every hour between 10am and 8pm // cron -w 0,1,2L -d 5,6,L 23:00 # run every sunday,monday, and last tuesday of the month, 5,6 on month, and *last* // day of month @11 pm // make *sure* a time spec is specified bool time_spec_specified = false; size_t line_tokens_size = lineTokens.size(); while (index < line_tokens_size) { const std::string& token = lineTokens[index]; #ifdef DEBUG_CRON_PARSING cerr << "CronAttr::doParse " << token << "\n"; #endif if (isOption(token)) { #ifdef DEBUG_CRON_PARSING cerr << "CronAttr::doParse isOption \n"; #endif extractOption(cronAttr, index, lineTokens); index--; // since we did a look ahead } else if (!time_spec_specified && isTimeSpec(token)) { #ifdef DEBUG_CRON_PARSING cerr << "CronAttr::doParse isTimeSpec \n"; #endif // index is passed by *reference*, and used skip over time series cronAttr.addTimeSeries(TimeSeries::create(index, lineTokens, parse_state)); time_spec_specified = true; if (parse_state) { // if index is on the comment, back track, so that we can add cron state( free) if (index < line_tokens_size && lineTokens[index] == "#") { index--; } } else { break; // need to read state after comment } } else if (isComment(token)) { // cron -m 1,2,3 12:00 # free if (parse_state && index + 1 < line_tokens_size) { if (lineTokens[index + 1] == "free") { cronAttr.setFree(); } } break; } index++; } if (!time_spec_specified) { throw std::runtime_error("Invalid cron, no time specified"); } #ifdef DEBUG_CRON_PARSING cronAttr.print(cerr); cerr << "\n"; #endif } CronAttr CronAttr::create(const std::string& cronString) { std::vector lineTokens; Str::split(cronString, lineTokens); CronAttr theCronAttr; if (lineTokens.empty()) { return theCronAttr; } // adjust the index size_t index = 0; if (lineTokens[0] == "cron") { index = 1; } parse(theCronAttr, lineTokens, index); return theCronAttr; } template void CronAttr::serialize(Archive& ar, std::uint32_t const version) { ar(CEREAL_NVP(timeSeries_)); CEREAL_OPTIONAL_NVP(ar, weekDays_, [this]() { return !weekDays_.empty(); }); // conditionally save CEREAL_OPTIONAL_NVP( ar, last_week_days_of_month_, [this]() { return !last_week_days_of_month_.empty(); }); // conditionally save CEREAL_OPTIONAL_NVP(ar, daysOfMonth_, [this]() { return !daysOfMonth_.empty(); }); // conditionally save CEREAL_OPTIONAL_NVP(ar, months_, [this]() { return !months_.empty(); }); // conditionally save CEREAL_OPTIONAL_NVP(ar, free_, [this]() { return free_; }); // conditionally save CEREAL_OPTIONAL_NVP(ar, last_day_of_month_, [this]() { return last_day_of_month_; }); // conditionally save CEREAL_OPTIONAL_NVP(ar, w_, [this]() { return w_ != 0; }); // conditionally save } CEREAL_TEMPLATE_SPECIALIZE_V(CronAttr); } // namespace ecf ecflow-5.16.0/libs/attribute/src/ecflow/attribute/GenericAttr.hpp0000664000175000017500000000337315161437256025213 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #ifndef ecflow_attribute_GenericAttr_HPP #define ecflow_attribute_GenericAttr_HPP #include #include namespace cereal { class access; } // Class GenericAttr: // Use compiler , generated destructor, assignment, copy constructor // GenericAttr does *not* have any changeable state class GenericAttr { public: GenericAttr(const std::string& name, const std::vector& values); explicit GenericAttr(const std::string& name); GenericAttr() = default; bool operator==(const GenericAttr& rhs) const; bool operator<(const GenericAttr& rhs) const { return name_ < rhs.name(); } bool empty() const { return name_.empty(); } const std::string& name() const { return name_; } const std::vector& values() const { return values_; } std::vector::const_iterator values_begin() const { return values_.begin(); } // for python std::vector::const_iterator values_end() const { return values_.end(); } // for python std::string to_string() const; // Added to support return by reference static const GenericAttr& EMPTY(); public: void write(std::string& ret) const; private: std::string name_; std::vector values_; friend class cereal::access; template void serialize(Archive& ar); }; #endif /* ecflow_attribute_GenericAttr_HPP */ ecflow-5.16.0/libs/attribute/src/ecflow/attribute/QueueAttr.cpp0000664000175000017500000002130515161437256024711 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "ecflow/attribute/QueueAttr.hpp" #include #include "ecflow/core/Ecf.hpp" #include "ecflow/core/Extract.hpp" #include "ecflow/core/Message.hpp" #include "ecflow/core/Serialization.hpp" #include "ecflow/core/Str.hpp" using namespace ecf; ///////////////////////////////////////////////////////////////////////////////////////////// const QueueAttr& QueueAttr::EMPTY() { static const QueueAttr queueAttr = QueueAttr(); return queueAttr; } QueueAttr& QueueAttr::EMPTY1() { static QueueAttr queueAttr = QueueAttr(); return queueAttr; } QueueAttr::QueueAttr(const std::string& name, const std::vector& theQueue) : theQueue_(theQueue), name_(name) { std::string msg; if (!Str::valid_name(name, msg)) { throw std::runtime_error("QueueAttr::QueueAttr: Invalid queue name : " + msg); } if (theQueue.empty()) { throw std::runtime_error("QueueAttr::QueueAttr: No queue items specified"); } for (size_t i = 0; i < theQueue.size(); i++) { state_vec_.push_back(NState::QUEUED); } } QueueAttr::~QueueAttr() = default; bool QueueAttr::operator==(const QueueAttr& rhs) const { if (name_ != rhs.name_) { return false; } if (theQueue_ != rhs.theQueue_) { return false; } if (state_vec_ != rhs.state_vec_) { return false; } if (currentIndex_ != rhs.currentIndex_) { return false; } return true; } std::string QueueAttr::value() const { if (currentIndex_ >= 0 && currentIndex_ < static_cast(theQueue_.size())) { return theQueue_[currentIndex_]; } return ""; } int QueueAttr::index_or_value() const { if (currentIndex_ >= 0 && currentIndex_ < static_cast(theQueue_.size())) { try { return ecf::convert_to(theQueue_[currentIndex_]); } catch (ecf::bad_conversion&) { // Ignore and return currentIndex_ } } return currentIndex_; } NState::State QueueAttr::state(const std::string& step) const { for (size_t i = 0; i < theQueue_.size(); i++) { if (step == theQueue_[i]) { if (i >= state_vec_.size()) { throw std::runtime_error("QueueAttr::state: index out of range"); } return state_vec_[i]; } } throw std::runtime_error("QueueAttr::state: could not find step " + step); } void QueueAttr::requeue() { currentIndex_ = 0; for (auto& i : state_vec_) { i = NState::QUEUED; } incr_state_change_no(); } std::string QueueAttr::active() { if (currentIndex_ >= 0 && currentIndex_ < static_cast(theQueue_.size())) { state_vec_[currentIndex_] = NState::ACTIVE; std::string ret = theQueue_[currentIndex_]; currentIndex_++; incr_state_change_no(); return ret; } return ""; } void QueueAttr::complete(const std::string& step) { for (size_t i = 0; i < theQueue_.size(); i++) { if (step == theQueue_[i]) { state_vec_[i] = NState::COMPLETE; incr_state_change_no(); return; } } throw std::runtime_error(MESSAGE("QueueAttr::complete: Could not find " << step << " in queue " << name_)); } void QueueAttr::aborted(const std::string& step) { for (size_t i = 0; i < theQueue_.size(); i++) { if (step == theQueue_[i]) { state_vec_[i] = NState::ABORTED; incr_state_change_no(); return; } } throw std::runtime_error(MESSAGE("QueueAttr::aborted: Could not find " << step << " in queue " << name_)); } std::string QueueAttr::no_of_aborted() const { int count = 0; for (auto i : state_vec_) { if (i == NState::ABORTED) { count++; } } if (count != 0) { return ecf::convert_to(count); } return std::string{}; } void QueueAttr::reset_index_to_first_queued_or_aborted() { for (size_t i = 0; i < state_vec_.size(); i++) { if (state_vec_[i] == NState::QUEUED || state_vec_[i] == NState::ABORTED) { currentIndex_ = i; incr_state_change_no(); break; } } } std::string QueueAttr::toString() const { std::string ret; write(ret); return ret; } void QueueAttr::write(std::string& ret) const { ret += "queue "; ret += name_; for (const auto& i : theQueue_) { ret += " "; ret += i; } } std::string QueueAttr::dump() const { std::ostringstream ss; ss << toString() << " # " << currentIndex_; for (auto i : state_vec_) { ss << " " << i; } return ss.str(); } void QueueAttr::incr_state_change_no() { state_change_no_ = Ecf::incr_state_change_no(); } void QueueAttr::parse(QueueAttr& queAttr, const std::string& line, std::vector& lineTokens, bool parse_state) { size_t line_tokens_size = lineTokens.size(); if (line_tokens_size < 3) { throw std::runtime_error(MESSAGE("QueueAttr::parse: expected at least 3 tokens, found " << line_tokens_size << " on line:" << line << "\n")); } // queue name "first" "second" "last" # current_index state state state // 0 1 2 3 4 5 6 queAttr.set_name(lineTokens[1]); std::vector theEnums; theEnums.reserve(line_tokens_size); for (size_t i = 2; i < line_tokens_size; i++) { std::string theEnum = lineTokens[i]; if (theEnum[0] == '#') { break; } Str::removeSingleQuotes(theEnum); // remove quotes, they get added back when we persist Str::removeQuotes(theEnum); // remove quotes, they get added back when we persist theEnums.push_back(theEnum); } if (theEnums.empty()) { throw std::runtime_error("queue: has no values " + line); } int index = 0; std::vector state_vec; if (parse_state) { // queue VARIABLE a b c d # index active complete aborted queued for (size_t i = 3; i < line_tokens_size; i++) { if (lineTokens[i] == "#" && i + 1 < line_tokens_size) { i++; index = Extract::value(lineTokens[i], "QueueAttr::parse, could not extract index"); i++; for (; i < line_tokens_size; i++) { NState::State state = NState::toState(lineTokens[i]); state_vec.push_back(state); } break; } } } queAttr.set_queue(theEnums, index, state_vec); } void QueueAttr::set_queue(const std::vector& theQueue, int index, const std::vector& state_vec) { if (theQueue.empty()) { throw std::runtime_error("QueueAttr::set_queue: No queue items specified"); } if (!state_vec.empty()) { if (state_vec.size() != theQueue.size()) { throw std::runtime_error(MESSAGE("QueueAttr::set_state: for queue " << name_ << " size " << theQueue.size() << " does not match state size " << state_vec.size())); } state_vec_ = state_vec; } else { for (size_t i = 0; i < theQueue.size(); i++) { state_vec_.push_back(NState::QUEUED); } } currentIndex_ = index; theQueue_ = theQueue; } void QueueAttr::set_state_vec(const std::vector& state_vec) { state_vec_ = state_vec; if (theQueue_.size() != state_vec_.size()) { std::cout << "QueueAttr::set_state_vec: for queue " << name_ << " queue size " << theQueue_.size() << " not equal to state_vec size " << state_vec_.size() << "\n"; } } void QueueAttr::set_name(const std::string& name) { std::string msg; if (!Str::valid_name(name, msg)) { throw std::runtime_error("QueueAttr::set_name: Invalid queue name : " + msg); } name_ = name; } template void QueueAttr::serialize(Archive& ar, std::uint32_t const version) { ar(CEREAL_NVP(theQueue_), CEREAL_NVP(state_vec_), CEREAL_NVP(name_), CEREAL_NVP(currentIndex_)); } CEREAL_TEMPLATE_SPECIALIZE_V(QueueAttr); ecflow-5.16.0/libs/attribute/src/ecflow/attribute/DayAttr.cpp0000664000175000017500000003523315161437256024347 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "ecflow/attribute/DayAttr.hpp" #include #include #include #include "ecflow/core/Calendar.hpp" #include "ecflow/core/Ecf.hpp" #include "ecflow/core/Extract.hpp" #include "ecflow/core/Message.hpp" #include "ecflow/core/Serialization.hpp" #include "ecflow/core/cereal_boost_time.hpp" using namespace ecf; // #define DEBUG_DAYS 1 //=============================================================================== static const char* theDay(DayAttr::Day_t day) { switch (day) { case DayAttr::SUNDAY: return "sunday"; case DayAttr::MONDAY: return "monday"; case DayAttr::TUESDAY: return "tuesday"; case DayAttr::WEDNESDAY: return "wednesday"; case DayAttr::THURSDAY: return "thursday"; case DayAttr::FRIDAY: return "friday"; case DayAttr::SATURDAY: return "saturday"; default: assert(false); } return nullptr; } //=============================================================================== void DayAttr::calendarChanged(const ecf::Calendar& c, bool clear_at_midnight) { // See ECFLOW-337 // repeat .... // family start // family 0 // time 10:00 // day monday # if there was no c.dayChanged(), then after re-queue, & before midnight Monday is still // free. task dummy # hence we will end up also running on Tuesday at 10:00 // complete 1==1 // trigger 0==1 // // ECFLOW-1550 # If children of a family with day/date are still active/submitted/queued, then don't // clear at midnight. repeat .... # This is only applicable for NodeContainers, for task with day/date // always CLEAR at midnight // family f1 // day monday // time 23:00 // task t1 # Task t1 took longer than 1 hour // task t2 # allow task t2 to continue to the next day, i.e. clear_at_midnight = False // trigger t1 == complete #ifdef DEBUG_DAYS cout << " DayAttr::calendarChanged " << dump() << " clear_at_midnight " << clear_at_midnight << " calendar:" << c.suite_time_str() << "\n"; #endif if (expired_) { // ********* TREAT this Day Attribute as deleted ECFLOW-128 ********** return; } if (c.dayChanged()) { if (clear_at_midnight) { clearFree(); #ifdef DEBUG_DAYS cout << " DayAttr::calendarChanged MIDNIGHT " << dump() << " calendar:" << c.suite_time_str() << "\n"; #endif } } if (free_) { return; } if (is_free(c)) { setFree(); #ifdef DEBUG_DAYS cout << " DayAttr::calendarChanged SET FREE " << dump() << " calendar:" << c.suite_time_str() << "\n"; #endif } } void DayAttr::reset() { expired_ = false; free_ = false; date_ = boost::gregorian::date(); state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_DAYS cout << " DayAttr::reset :" << dump() << "\n"; #endif } void DayAttr::handle_migration(const ecf::Calendar& c) { // temp once in Bologna, and when ecflow4 no longer used. if (date_.is_special()) { if (!c.is_special()) { date_ = matching_date(c); } } } void DayAttr::reset(const ecf::Calendar& c) { reset(); date_ = matching_date(c); #ifdef DEBUG_DAYS cout << " DayAttr::reset(calendar) :" << dump() << " calendar:" << c.suite_time_str() << "\n"; #endif } void DayAttr::requeue_time() { #ifdef DEBUG_DAYS cout << " DayAttr::requeue " << dump() << "\n"; #endif if (expired_) { // ********* TREAT this Day Attribute as deleted ********** #ifdef DEBUG_DAYS cout << " DayAttr::requeue_time " << dump() << " EXPIRED(do nothing) returning\n"; #endif return; } free_ = false; state_change_no_ = Ecf::incr_state_change_no(); } void DayAttr::requeue_manual(const ecf::Calendar& c) { reset(); date_ = matching_date(c); #ifdef DEBUG_DAYS cout << " DayAttr::requeue_manual(calendar) " << dump() << " calendar:" << c.suite_time_str() << "\n"; #endif } void DayAttr::requeue_repeat_increment(const ecf::Calendar& c) { reset(); date_ = next_matching_date(c); #ifdef DEBUG_DAYS cout << " DayAttr::requeue_repeat_increment(calendar) " << dump() << " calendar:" << c.suite_time_str() << "\n"; #endif } bool DayAttr::isFree(const ecf::Calendar& c) const { if (expired_) { // ********* TREAT this Day Attribute as deleted ********** #ifdef DEBUG_DAYS cout << " DayAttr::isFree " << dump() << " calendar:" << c.suite_time_str() << " HOLDING due to EXPIRED flag\n"; #endif return false; } // The FreeDepCmd can be used to free the dates, if (free_) { #ifdef DEBUG_DAYS cout << " DayAttr::isFree " << dump() << " calendar:" << c.suite_time_str() << " FREE free_ = TRUE\n"; #endif return true; } bool res = is_free(c); #ifdef DEBUG_DAYS if (res) { cout << " DayAttr::isFree " << dump() << " calendar:" << c.suite_time_str() << " date is FREE\n"; } else { cout << " DayAttr::isFree " << dump() << " calendar:" << c.suite_time_str() << " date is HOLDING\n"; } #endif return res; } bool DayAttr::is_free(const ecf::Calendar& c) const { return (c.date() == date_); } void DayAttr::setFree() { free_ = true; state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_DAYS cout << " DayAttr::setFree() " << dump() << "\n"; #endif } void DayAttr::clearFree() { free_ = false; state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_DAYS cout << " DayAttr::clearFree() " << dump() << "\n"; #endif } void DayAttr::set_expired() { expired_ = true; state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_DAYS cout << " DayAttr::set_expired() " << dump() << "\n"; #endif } void DayAttr::clear_expired() { expired_ = false; state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_DAYS cout << " DayAttr::clear_expired() " << dump() << "\n"; #endif } void DayAttr::check_for_expiration(const ecf::Calendar& c) { #ifdef DEBUG_DAYS cout << "DayAttr::check_for_expiration " << dump() << " calendar:" << c.suite_time_str() << "\n"; #endif // This function is called when a Node has COMPLETED. Avoid running again on same day date <= calendar, expire the // date Note: time attribute, they get handled before. i.e. allowing multiple re-queues on the same date This // function *MUST* be called before checkForRequeue. if (date_.is_special()) { // migration 4->5, or 5->5 from checkpoint date_ = matching_date(c); } if (day_ == c.day_of_week()) { set_expired(); return; } if (date_ <= c.date()) { set_expired(); } } bool DayAttr::checkForRequeue(const ecf::Calendar& c) const { if (expired_) { #ifdef DEBUG_DAYS cout << " DayAttr::check_for_requeue ALREADY EXPIRED " << dump() << " calendar:" << c.suite_time_str() << " HOLDING <<<<<<<<<<<<\n"; #endif return false; } // if calendar is hybrid, we can't requeue if (c.hybrid()) { return false; } // checkForRequeue is called when we are deciding whether to re-queue the node // Hence we *MUST* have completed. Also, all cron, time, and today attributes have returned false. // *IF* this date is in the future, we should re-queue assert(!date_.is_special()); bool future_date = (date_ > c.date()); #ifdef DEBUG_DAYS cout << " DayAttr::check_for_requeue " << dump() << " calendar:" << c.suite_time_str() << " returning " << future_day << " ************\n"; #endif return future_date; } bool DayAttr::validForHybrid(const ecf::Calendar& calendar) const { return isFree(calendar); } bool DayAttr::why(const ecf::Calendar& c, std::string& theReasonWhy) const { if (isFree(c)) { return false; } theReasonWhy += " is day dependent ( next run on "; theReasonWhy += theDay(day_); theReasonWhy += " "; if (date_.is_special()) { theReasonWhy += to_simple_string(next_matching_date(c)); } else { theReasonWhy += to_simple_string(date_); } theReasonWhy += " the current day is "; theReasonWhy += theDay(static_cast(c.day_of_week())); theReasonWhy += " )"; return true; } std::string DayAttr::name() const { // for display/GUI only std::string os; write(os); bool added_hash = false; if (expired_) { os += " # expired"; added_hash = true; } else { if (free_) { os += " # free"; added_hash = true; } } if (added_hash) { os += " "; os += to_simple_string(date_); } else { os += " # "; os += to_simple_string(date_); } return os; } std::string DayAttr::toString() const { std::string ret; write(ret); return ret; } void DayAttr::write(std::string& ret) const { ret += "day "; ret += theDay(day_); } std::string DayAttr::dump() const { return MESSAGE(toString() << (free_ ? " (free)" : "") << (expired_ ? " (expired)" : "") << " " << as_simple_string()); } std::string DayAttr::as_simple_string() const { return boost::gregorian::to_simple_string(date_); } bool DayAttr::operator==(const DayAttr& rhs) const { if (free_ != rhs.free_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "DayAttr::operator== free_ != rhs.free_ (free_:" << free_ << " rhs.free_:" << rhs.free_ << ") " << dump() << "\n"; } #endif return false; } if (expired_ != rhs.expired_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "DayAttr::operator== expired_ != rhs.expired_ (expired_:" << expired_ << " rhs.expired_:" << rhs.expired_ << ") " << dump() << "\n"; } #endif return false; } if (date_ != rhs.date_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "DayAttr::operator== date_ != rhs.date_ (date_:" << date_ << " rhs.date_:" << rhs.date_ << ") " << dump() << "\n"; } #endif return false; } return structureEquals(rhs); } bool DayAttr::structureEquals(const DayAttr& rhs) const { return (day_ == rhs.day_); } DayAttr DayAttr::create(const std::string& dayStr) { return DayAttr(getDay(dayStr)); } DayAttr DayAttr::create(const std::vector& lineTokens, bool read_state) { if (lineTokens.size() < 2) { throw std::runtime_error("DayAttr::create date tokens to short :"); } // for(size_t i =0; i < lineTokens.size() ; i++) { // cout << "lineTokens[" << i << "] = '" << lineTokens[i] << "'\n"; // } // day monday # free expired date:**** // day monday # expired DayAttr day = DayAttr::create(lineTokens[1]); // state if (read_state) { day.read_state(lineTokens); } return day; } void DayAttr::read_state(const std::vector& lineTokens) { std::string date; for (size_t i = 3; i < lineTokens.size(); i++) { if (lineTokens[i] == "free") { free_ = true; } else if (lineTokens[i] == "expired") { expired_ = true; } else if (lineTokens[i].find("date:") != std::string::npos) { if (!Extract::split_get_second(lineTokens[i], date)) { throw std::runtime_error("DayAttr::read_state failed: (date:)"); } // when a date_ is special date = not-a-date-time\n" if (date.find("not") == std::string::npos) { date_ = boost::gregorian::from_simple_string(date); } } } } DayAttr::Day_t DayAttr::getDay(const std::string& day) { if (day == "monday") { return DayAttr::MONDAY; } if (day == "tuesday") { return DayAttr::TUESDAY; } if (day == "wednesday") { return DayAttr::WEDNESDAY; } if (day == "thursday") { return DayAttr::THURSDAY; } if (day == "friday") { return DayAttr::FRIDAY; } if (day == "saturday") { return DayAttr::SATURDAY; } if (day == "sunday") { return DayAttr::SUNDAY; } throw std::runtime_error(MESSAGE( "Invalid day(" << day << ") specification expected one of [monday,tuesday,wednesday,thursday,friday,saturday,sunday]: ")); } std::vector DayAttr::allDays() { std::vector vec; vec.reserve(7); vec.emplace_back("monday"); vec.emplace_back("tuesday"); vec.emplace_back("wednesday"); vec.emplace_back("thursday"); vec.emplace_back("friday"); vec.emplace_back("saturday"); vec.emplace_back("sunday"); return vec; } boost::gregorian::date DayAttr::matching_date(const ecf::Calendar& c) const { auto one_day = boost::gregorian::date_duration(1); auto matching_date = c.date(); // today's date for (int i = 0; i < 7; i++) { if (matching_date.day_of_week().as_number() == day_) { return matching_date; } matching_date += one_day; } assert(false); // no matching day ?s return c.date(); } boost::gregorian::date DayAttr::next_matching_date(const ecf::Calendar& c) const { auto one_day = boost::gregorian::date_duration(1); auto the_next_matching_date = c.date(); // today's date for (int i = 0; i < 7; i++) { the_next_matching_date += one_day; if (the_next_matching_date.day_of_week().as_number() == day_) { return the_next_matching_date; } } assert(false); return c.date(); } template void DayAttr::serialize(Archive& ar) { ar(CEREAL_NVP(day_)); CEREAL_OPTIONAL_NVP(ar, free_, [this]() { return free_; }); // conditionally save CEREAL_OPTIONAL_NVP(ar, expired_, [this]() { return expired_; }); // conditionally save, new to ecflow 5.4.0, should be ignored by old clients. see tests CEREAL_OPTIONAL_NVP(ar, date_, [this]() { return !date_.is_special(); }); // conditionally save, new to ecflow 5.5.0, should be ignored by old clients. see tests } CEREAL_TEMPLATE_SPECIALIZE(DayAttr); ecflow-5.16.0/libs/attribute/src/ecflow/attribute/ClockAttr.hpp0000664000175000017500000000630615161437256024671 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #ifndef ecflow_attribute_ClockAttr_HPP #define ecflow_attribute_ClockAttr_HPP #include #include "ecflow/core/Chrono.hpp" namespace cereal { class access; } namespace ecf { class Calendar; } // namespace ecf /// The clock attribute is defined on the suite ONLY /// Use default copy constructor and assignment operator, destructor /// The clock attribute is used to initialise the calendar object /// ********************************************************************** /// In the OLD sms the date is actually used as a gain factor(well at least /// according to the user/reference manual), in the ec-flow /// a date, is a date. i.e. it allows us to start a suite in the past. /// /// The Constructor will update the State change number, since it can be added /// by the AlterCmd. in the Client Context, state change number is not incremented /// ************************************************************************ /// class ClockAttr { public: /// The following constructor is used for test only. It allows us to /// create a clock attribute initialised with given date and time explicit ClockAttr(const boost::posix_time::ptime&, bool hybrid = false, bool positiveGain = true); ClockAttr(int day, int month, int year, bool hybrid = false); explicit ClockAttr(bool hybrid = false); bool operator==(const ClockAttr& rhs) const; void date(int day, int month, int year); void set_gain(int hour, int min, bool positiveGain = true); void set_gain_in_seconds(long theGain, bool positiveGain = true); void hybrid(bool f); void set_end_clock() { end_clock_ = true; } // clear local attributes so that, when the suite is requeued, the computer clock is synced void sync(); void init_calendar(ecf::Calendar&); void begin_calendar(ecf::Calendar&) const; // The state_change_no is never reset. Must be incremented if it can affect equality unsigned int state_change_no() const { return state_change_no_; } // access int day() const { return day_; } int month() const { return month_; } int year() const { return year_; } long gain() const { return gain_; } bool positive_gain() const { return positiveGain_; } bool hybrid() const { return hybrid_; } std::string toString() const; boost::posix_time::ptime ptime() const; public: void write(std::string& os) const; private: long gain_{0}; // in seconds int day_{0}; int month_{0}; int year_{0}; unsigned int state_change_no_{0}; // *not* persisted, only used on server side bool hybrid_{false}; bool positiveGain_{false}; bool end_clock_{false}; // *NOT* persisted, used for end clock, simulator only friend class cereal::access; template void serialize(Archive& ar, std::uint32_t const version); }; #endif /* ecflow_attribute_ClockAttr_HPP */ ecflow-5.16.0/libs/attribute/src/ecflow/attribute/AutoArchiveAttr.cpp0000664000175000017500000000737515161437256026052 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "ecflow/attribute/AutoArchiveAttr.hpp" #include "ecflow/core/Calendar.hpp" #include "ecflow/core/Chrono.hpp" #include "ecflow/core/Converter.hpp" #include "ecflow/core/Log.hpp" #include "ecflow/core/Serialization.hpp" namespace ecf { std::string AutoArchiveAttr::toString() const { std::string ret; write(ret); return ret; } void AutoArchiveAttr::write(std::string& ret) const { ret += "autoarchive "; if (days_) { ret += ecf::convert_to(time_.hour() / 24); if (idle_) { ret += " -i"; } return; } if (relative_) { ret += "+"; } time_.print(ret); if (idle_) { ret += " -i"; } } bool AutoArchiveAttr::operator==(const AutoArchiveAttr& rhs) const { if (relative_ != rhs.relative_) { return false; } if (days_ != rhs.days_) { return false; } if (idle_ != rhs.idle_) { return false; } return time_.operator==(rhs.time_); } bool AutoArchiveAttr::isFree( const ecf::Calendar& calendar, const std::pair& last_state_and_change_duration) const { // suiteTime() // suiteDurationAtComplete autoarchive time calendar duration // | | | // V V V // ----------------------------------------------------------------------------------> time // ^ ^ // |--------elapsed time---------------------------------------| // // bool is_valid_state = false; if (last_state_and_change_duration.first == NState::COMPLETE) { is_valid_state = true; } if (idle_) { if (last_state_and_change_duration.first == NState::QUEUED) { is_valid_state = true; } if (last_state_and_change_duration.first == NState::ABORTED) { is_valid_state = true; } } if (!is_valid_state) { return false; } if (relative_) { boost::posix_time::time_duration time_elapsed = calendar.duration() - last_state_and_change_duration.second; LOG_ASSERT(!time_elapsed.is_negative(), "should always be positive or some things gone wrong"); if (time_elapsed >= time_.duration()) { return true; } } else { // real time // #ifdef DEBUG // cout << "real time time_(" << to_simple_string(time_.duration()) // << ") calendar.suiteTime().time_of_day(" << to_simple_string(calendar.suiteTime().time_of_day()) << // ")\n"; // #endif if (calendar.suiteTime().time_of_day() >= time_.duration()) { return true; } } return false; } template void AutoArchiveAttr::serialize(Archive& ar, std::uint32_t const version) { ar(CEREAL_NVP(time_)); CEREAL_OPTIONAL_NVP(ar, relative_, [this]() { return !relative_; }); // conditionally save CEREAL_OPTIONAL_NVP(ar, days_, [this]() { return days_; }); // conditionally save CEREAL_OPTIONAL_NVP(ar, idle_, [this]() { return idle_; }); // conditionally save } CEREAL_TEMPLATE_SPECIALIZE_V(AutoArchiveAttr); } // namespace ecf ecflow-5.16.0/libs/attribute/src/ecflow/attribute/AutoCancelAttr.cpp0000664000175000017500000000605715161437256025652 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "ecflow/attribute/AutoCancelAttr.hpp" #include "ecflow/core/Calendar.hpp" #include "ecflow/core/Chrono.hpp" #include "ecflow/core/Converter.hpp" #include "ecflow/core/Log.hpp" #include "ecflow/core/Serialization.hpp" namespace ecf { std::string AutoCancelAttr::toString() const { std::string ret; write(ret); return ret; } void AutoCancelAttr::write(std::string& ret) const { ret += "autocancel "; if (days_) { ret += ecf::convert_to(time_.hour() / 24); return; } if (relative_) { ret += "+"; } time_.print(ret); } bool AutoCancelAttr::operator==(const AutoCancelAttr& rhs) const { if (relative_ != rhs.relative_) { return false; } if (days_ != rhs.days_) { return false; } return time_.operator==(rhs.time_); } bool AutoCancelAttr::isFree(const ecf::Calendar& calendar, const boost::posix_time::time_duration& suiteDurationAtComplete) const { // suiteTime() // suiteDurationAtComplete autocancel time calendar duration // | | | // V V V // ----------------------------------------------------------------------------------> time // ^ ^ // |--------elapsed time---------------------------------------| // // if (relative_) { boost::posix_time::time_duration timeElapsedAfterComplete = calendar.duration() - suiteDurationAtComplete; LOG_ASSERT(!timeElapsedAfterComplete.is_negative(), "should always be positive or some things gone wrong"); if (timeElapsedAfterComplete >= time_.duration()) { return true; } } else { // real time // #ifdef DEBUG // cout << "real time time_(" << to_simple_string(time_.duration()) // << ") calendar.suiteTime().time_of_day(" << // to_simple_string(calendar.suiteTime().time_of_day()) << ")\n"; #endif if (calendar.suiteTime().time_of_day() >= time_.duration()) { return true; } } return false; } template void AutoCancelAttr::serialize(Archive& ar, std::uint32_t const /*version*/) { ar(CEREAL_NVP(time_)); CEREAL_OPTIONAL_NVP(ar, relative_, [this]() { return !relative_; }); // conditionally save CEREAL_OPTIONAL_NVP(ar, days_, [this]() { return days_; }); // conditionally save } CEREAL_TEMPLATE_SPECIALIZE_V(AutoCancelAttr); } // namespace ecf ecflow-5.16.0/libs/attribute/src/ecflow/attribute/TodayAttr.hpp0000664000175000017500000002006415161437256024713 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #ifndef ecflow_attribute_TodayAttr_HPP #define ecflow_attribute_TodayAttr_HPP /// /// \brief The Today attribute is heavily tied to the `begin` command /// /// Real Clock: /// 1/ Suite Begin time > Today time /// If the suite 'begin' time is past the time given for today /// the node is free to run. /// 2/ Suite Begin time < Today Time /// The node will 'hold' until current time > today time /// 3/ Suite time, has passed midnight(next day) /// then today command will permanently hold the node /// 4/ Under Real/hybrid clocks today will hold node after /// current is past last today time. /// /// Take the following example when we have a single time slot: /// today 10:00 /// isFree:-----free----- /// begin: /// V /// checkForRequeue:rrrrrrrrrrrrrrrrhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh /// isFree:hhhhhhhhhhhhhhhh|fffffffffffffffffffffffffffffffffffffff *once* free we stay free /// (single slot *only*) begin: /// V /// Today ======================0=====================0================= /// 10:00 Midnight /// /// Difference between time and today. If begin is started after the time slot /// then the node is free to re-run /// /// When we have a today time series: /// today 10:00 20:00 01:00 /// /// *** If the beginning time is past 10:00 as in the case above then the /// *** node should is free to run once. However, for a range its different /// *** if suite begin time is past 20:00 then the node is held. /// /// At 10am the Node is free, when node completes, it is re-queued /// At 11am the Node is free, when node completes, it is re-queued /// .... /// At 20pm the Node is free, when node completes, it is *NOT* re-queued. /// /// isFree is called when a node is queued. if it returns true, Task can be submitted /// checkForRequeue: is called when a node has completed, and need to determine if it should run again. /// These are different/orthogonal concerns. /// There is a *separate* issue of whether nodes should be queued when a node is *manually* /// a/ Set complete /// b/ Runs and then completes /// /// For a *single* time slot we can't requeue. /// ****However we could have a set of time slots ***** /// /// isFree:ffffffffffffffff /// Begin: /// V /// checkForRequeue:hhhhhhhhhhhhhhhhhrrrrrrrrhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh /// isFree:hhhhhhhhhhhhhhhh|fffffffffffffffffffffffffffffffffffffff /// begin : /// V /// Today ======================0========0===========0==================== /// 10:00 11:00 Midnight /// isFree:hhhhhhhhhhhhhhhhhh /// V /// CheckForRequeue:hhhhhhhrrrrrrrrrrrrrrrrrrrrrrrrrhhhhhhhhhhhhhhhhhhhhhhhhhhhhh /// isFree:hhhhhhhFhhhhFhhhhFhhhhFhhhhFhhhhFFFFFFFFFFFFFFFFFFFFFFFFFFFFF /// V | | | | | | | /// Today ================o====|====|====|====|====0========0==================== /// 10:00 1 2 3 4 15:00 Midnight /// /// If the job starts at 10:00 but takes more than 1 hour, then it will miss the 11:00 slot /// and will have to start at 12:00 /// #include "ecflow/core/TimeSeries.hpp" class DateAttr; // Used in Why class DayAttr; // Used in Why namespace ecf { class Calendar; } // namespace ecf namespace ecf { // Use compiler , destructor, assignment, copy constructor class TodayAttr { public: explicit TodayAttr(const std::string&); TodayAttr() = default; TodayAttr(int hour, int minute, bool relative = false) : ts_(hour, minute, relative) {} explicit TodayAttr(const TimeSlot& t, bool relative = false) : ts_(t, relative) {} explicit TodayAttr(const TimeSeries& ts) : ts_(ts) {} TodayAttr(const TimeSlot& start, const TimeSlot& finish, const TimeSlot& incr, bool relative = false) : ts_(start, finish, incr, relative) {} bool operator==(const TodayAttr& rhs) const; bool operator<(const TodayAttr& rhs) const { return ts_ < rhs.ts_; } bool structureEquals(const TodayAttr& rhs) const; /// This can set attribute as free, once free its stays free, until re-queue/reset void calendarChanged(const ecf::Calendar& c); void resetRelativeDuration(); void reset_only() { clearFree(); ts_.reset_only(); } void reset(const ecf::Calendar& c) { clearFree(); ts_.reset(c); } // updates state_change_no_ void requeue(const ecf::Calendar& c, bool reset_next_time_slot = true) { clearFree(); ts_.requeue(c, reset_next_time_slot); } // updates state_change_no_ void miss_next_time_slot(); // updates state_change_no_ void setFree(); // ensures that isFree() always returns true, updates state_change_no_ bool isSetFree() const { return free_; } // This is used when we have a *single* today attribute // single-slot is free, if calendar time >= today_time // (range) is free, if calendar time == (one of the time ranges) bool isFree(const ecf::Calendar&) const; // This is used when we have a *multiple* today attribute // (single | range) is free, if calendar time == (one of the time ranges) // if timer *expired* returns false // task t1 // today 09:00 // today 10:00 // If current times is 11:00, then we will return false. // since both 09:00 and 10:00 have expired // Multiple single today, should behave like a today with a range. bool isFreeMultipleContext(const ecf::Calendar& c) const { return ts_.isFree(c); } bool checkForRequeue(const ecf::Calendar& c, const TimeSlot& the_min, const TimeSlot& the_max, bool cmd_context = false) const { return ts_.checkForRequeue(c, the_min, the_max, cmd_context); } void min_max_time_slots(TimeSlot& the_min, TimeSlot& the_max) const { ts_.min_max_time_slots(the_min, the_max); } bool why(const ecf::Calendar&, const std::vector&, const std::vector&, std::string& theReasonWhy) const; bool checkInvariants(std::string& errormsg) const { return ts_.checkInvariants(errormsg); } // The state_change_no is never reset. Must be incremented if it can affect equality // Note: changes in state of ts_, i.e. affect the equality operator (used in test) // must be captured. i.e. things like relative duration & next_time_slot are // reported by the Why command, & hence need to be synced. unsigned int state_change_no() const { return state_change_no_; } std::string name() const; // for display/GUI std::string toString() const; void write(std::string&) const; // access const TimeSeries& time_series() const { return ts_; } private: void clearFree(); // resets the free flag, updates state_change_no_ bool is_free(const ecf::Calendar&) const; // ignores free_ private: TimeSeries ts_; unsigned int state_change_no_{0}; // *not* persisted, only used on server side bool free_{false}; // persisted for use by why() on client side && for state changes friend class cereal::access; template void serialize(Archive& ar); }; } // namespace ecf #endif /* ecflow_attribute_TodayAttr_HPP */ ecflow-5.16.0/libs/attribute/src/ecflow/attribute/DateAttr.hpp0000664000175000017500000000676715161437256024526 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #ifndef ecflow_attribute_DateAttr_HPP #define ecflow_attribute_DateAttr_HPP /// /// \note calendarChanged() once a Date is free, it stays free. It relies on parent cron/repeat to re-queue /// #include #include #include "ecflow/core/Chrono.hpp" namespace cereal { class access; } namespace ecf { class Calendar; } // namespace ecf // Use default copy constructor, assignment operator, destructor // Value of 0 for day,month,year means *, i.e. means any value class DateAttr { public: DateAttr(int day, int month, int year); // will throw std::out_of_range for if invalid date explicit DateAttr(const std::string&); // will throw std::runtime_error for if invalid date DateAttr() = default; // for serialisation explicit DateAttr(const boost::gregorian::date& date) : day_(date.day()), month_(date.month()), year_(date.year()) {} // for test bool operator==(const DateAttr& rhs) const; bool operator<(const DateAttr& rhs) const; bool structureEquals(const DateAttr& rhs) const; void reset(); void requeue(); void setFree(); // ensures that isFree() always returns true void clearFree(); // resets the free flag bool isSetFree() const { return free_; } void calendarChanged(const ecf::Calendar& c, bool clear_at_midnight = true); // can set attribute free bool isFree(const ecf::Calendar&) const; bool checkForRequeue(const ecf::Calendar&) const; bool validForHybrid(const ecf::Calendar&) const; bool why(const ecf::Calendar&, std::string& theReasonWhy) const; // The state_change_no is never reset. Must be incremented if it can affect equality unsigned int state_change_no() const { return state_change_no_; } std::string name() const; // for display/gui only std::string toString() const; std::string dump() const; /// Check the date, will throw std::out_of_range or derivative if invalid static void checkDate(int day, int month, int year, bool allow_wild_cards); /// Extract the date, if return integer is zero, date was of the *, i.e. any day,month,year /// will throw std::runtime_error for parse errors /// expect: /// 15.11.2009 /// 15.*.* /// *.1.* static void getDate(const std::string& date, int& day, int& month, int& year); static DateAttr create(const std::string& dateString); static DateAttr create(const std::vector& lineTokens, bool read_state); boost::gregorian::date next_matching_date(const ecf::Calendar&) const; // access int day() const { return day_; } int month() const { return month_; } int year() const { return year_; } bool is_free(const ecf::Calendar&) const; // ignores free_ public: void write(std::string&) const; private: int day_{0}; int month_{0}; int year_{0}; unsigned int state_change_no_{0}; // *not* persisted, only used on server side bool free_{false}; // persisted for use by why() on client side friend class cereal::access; template void serialize(Archive& ar); }; #endif /* ecflow_attribute_DateAttr_HPP */ ecflow-5.16.0/libs/attribute/src/ecflow/attribute/RepeatAttr.cpp0000664000175000017500000015427415161437256025061 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "ecflow/attribute/RepeatAttr.hpp" #include #include #include #include #include "ecflow/core/Calendar.hpp" #include "ecflow/core/Converter.hpp" #include "ecflow/core/Ecf.hpp" #include "ecflow/core/Log.hpp" #include "ecflow/core/Serialization.hpp" #include "ecflow/core/Str.hpp" #include "ecflow/node/formatter/DefsWriter.hpp" namespace { template std::string as_n_digits(T value) { static_assert(N > 0, "N must be greater than zero"); static_assert(std::is_integral::value, "T must be an integral type"); std::ostringstream ss; ss << std::setfill('0') << std::setw(N) << value; return ss.str(); } template std::string as_2_digits(T value) { return as_n_digits<2>(value); } template std::string as_4_digits(T value) { return as_n_digits<4>(value); } } // namespace using namespace ecf; const Repeat& Repeat::EMPTY() { static const Repeat REPEAT = Repeat(); return REPEAT; } //========================================================================= Repeat::Repeat() = default; Repeat::Repeat(const RepeatDate& r) : type_(std::make_unique(r)) { } Repeat::Repeat(const RepeatDateTime& r) : type_(std::make_unique(r)) { } Repeat::Repeat(const RepeatDateList& r) : type_(std::make_unique(r)) { } Repeat::Repeat(const RepeatInteger& r) : type_(std::make_unique(r)) { } Repeat::Repeat(const RepeatEnumerated& r) : type_(std::make_unique(r)) { } Repeat::Repeat(const RepeatString& r) : type_(std::make_unique(r)) { } Repeat::Repeat(const RepeatDay& r) : type_(std::make_unique(r)) { } Repeat::~Repeat() = default; Repeat::Repeat(const Repeat& rhs) { if (rhs.type_) { type_.reset(rhs.type_->clone()); } } Repeat::Repeat(Repeat&& rhs) : type_(std::move(rhs.type_)) { } Repeat& Repeat::operator=(Repeat rhs) { using std::swap; swap(this->type_, rhs.type_); return *this; } bool Repeat::operator==(const Repeat& rhs) const { if (!type_ && rhs.type_) { return false; } if (type_ && !rhs.type_) { return false; } if (!type_ && !rhs.type_) { return true; } return type_->compare(rhs.type_.get()); } const std::string& Repeat::name() const { return (type_.get()) ? type_->name() : Str::EMPTY(); } // ========================================================================= RepeatBase::~RepeatBase() = default; void RepeatBase::incr_state_change_no() { state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_STATE_CHANGE_NO std::cout << "RepeatBase::incr_state_change_no()\n"; #endif } void RepeatBase::update_repeat_genvar() const { // **** reset name since generated variables are not persisted var_.set_name(name_); // valueAsString() use the last_valid_value() which should always be in range. // Note repeat::value() can be on e past the last valid value, at expiration of Repeat loop // However Repeat::last_valid_value() will just return the last valid value. var_.set_value(valueAsString()); } std::string RepeatBase::toString() const { return ecf::as_string(*this, PrintStyle::DEFS); } // ============================================================= RepeatDate::RepeatDate(const std::string& variable, int start, int end, int delta /* always in days*/ ) : RepeatBase(variable), start_(start), end_(end), delta_(delta), value_(start) { if (!Str::valid_name(variable)) { throw std::runtime_error("RepeatDate::RepeatDate: Invalid name: " + variable); } if (delta == 0) { throw std::runtime_error(MESSAGE("Invalid Repeat date: the delta cannot be zero" << "repeat " << variable << " " << start << " " << end << " " << delta)); } std::string theStart = ecf::convert_to(start); if (theStart.size() != 8) { throw std::runtime_error( MESSAGE("Invalid Repeat date: The start is not a valid date. Please use yyyymmdd format." << "repeat " << variable << " " << start << " " << end << " " << delta)); } std::string theEnd = ecf::convert_to(end); if (theEnd.size() != 8) { throw std::runtime_error(MESSAGE("Invalid Repeat date: The end is not a valid date. Please use yyyymmdd format." << "repeat " << variable << " " << start << " " << end << " " << delta)); } if (delta_ > 0) { // assert end => start if (!(end >= start)) { throw std::runtime_error( MESSAGE("Invalid Repeat date: The end must be greater than the start date, when delta is positive " << "repeat " << variable << " " << start << " " << end << " " << delta)); } } else { // assert start >= end if (!(start >= end)) { throw std::runtime_error( MESSAGE("Invalid Repeat date: The start must be greater than the end date, when delta is negative " << "repeat " << variable << " " << start << " " << end << " " << delta)); } } // Use date lib to check YMD try { (void)boost::gregorian::date(boost::gregorian::from_undelimited_string(theStart)); (void)boost::gregorian::date(boost::gregorian::from_undelimited_string(theEnd)); } catch (std::exception& e) { throw std::runtime_error(MESSAGE("Invalid Repeat date: The start/end is not a valid date." << "repeat " << variable << " " << start << " " << end << " " << delta)); } } void RepeatDate::gen_variables(std::vector& vec) const { vec.push_back(yyyy_); vec.push_back(mm_); vec.push_back(dom_); vec.push_back(dow_); vec.push_back(julian_); RepeatBase::gen_variables(vec); } const Variable& RepeatDate::find_gen_variable(const std::string& name) const { if (name == name_) { return var_; } if (name == julian_.name()) { return julian_; } if (name == dow_.name()) { return dow_; } if (name == dom_.name()) { return dom_; } if (name == mm_.name()) { return mm_; } if (name == yyyy_.name()) { return yyyy_; } return Variable::EMPTY(); } void RepeatDate::update_repeat_genvar() const { RepeatBase::update_repeat_genvar(); yyyy_.set_name(name_ + "_YYYY"); yyyy_.set_value(""); mm_.set_name(name_ + "_MM"); mm_.set_value(""); dom_.set_name(name_ + "_DD"); dom_.set_value(""); dow_.set_name(name_ + "_DOW"); dom_.set_value(""); julian_.set_name(name_ + "_JULIAN"); julian_.set_value(""); yyyy_.set_name(name_ + "_YYYY"); mm_.set_name(name_ + "_MM"); dom_.set_name(name_ + "_DD"); dow_.set_name(name_ + "_DOW"); julian_.set_name(name_ + "_JULIAN"); update_repeat_genvar_value(); } void RepeatDate::update_repeat_genvar_value() const { std::string date_as_string = valueAsString(); if (valid()) { try { auto the_date = boost::gregorian::from_undelimited_string(date_as_string); if (the_date.is_special()) { log(Log::ERR, MESSAGE("RepeatDate::update_repeat_genvar(): invalid current date: " << date_as_string << " is_special")); return; } auto year = static_cast(the_date.year()); yyyy_.set_value(as_4_digits(year)); auto month = the_date.month().as_number(); mm_.set_value(as_2_digits(month)); auto day_of_month = the_date.day().as_number(); dom_.set_value(as_2_digits(day_of_month)); auto day_of_week = the_date.day_of_week().as_number(); dow_.set_value(std::to_string(day_of_week)); auto last_value = last_valid_value(); julian_.set_value(std::to_string(ecf::CalendarDate(last_value).as_julian_day().value())); } catch (std::exception& e) { log(Log::ERR, MESSAGE("RepeatDate::update_repeat_genvar_value : " << toString() << "\n The current date(" << date_as_string << ") is not valid")); return; } } } bool RepeatDate::compare(RepeatBase* rb) const { auto* rhs = dynamic_cast(rb); if (!rhs) { return false; } return operator==(*rhs); } void RepeatDate::setToLastValue() { set_value(end_); } long RepeatDate::last_valid_value() const { return valid_value(value_); } long RepeatDate::valid_value(long value) const { if (delta_ > 0) { if (value < start_) { return start_; } if (value > end_) { return end_; } return value; } if (value > start_) { return start_; } if (value < end_) { return end_; } return value; } long RepeatDate::last_valid_value_minus(int val) const { auto result = ecf::CalendarDate(last_valid_value()) - val; return result.value(); } long RepeatDate::last_valid_value_plus(int val) const { auto result = ecf::CalendarDate(last_valid_value()) + val; return result.value(); } void RepeatDate::reset() { set_value(start_); } std::string RepeatDate::dump() const { return MESSAGE(toString() << " value(" << value_ << ")"); } bool RepeatDate::operator==(const RepeatDate& rhs) const { if (name_ != rhs.name_) { return false; } if (start_ != rhs.start_) { return false; } if (end_ != rhs.end_) { return false; } if (delta_ != rhs.delta_) { return false; } if (value_ != rhs.value_) { return false; } return true; } std::string RepeatDate::valueAsString() const { /// will throw an ecf::bad_conversion if value is not convertible to a string try { return ecf::convert_to(last_valid_value()); } catch (const ecf::bad_conversion&) { LOG_ASSERT(false, "RepeatDate::valueAsString(): could not convert value " << value_ << " to a string"); } return std::string{}; } std::string RepeatDate::value_as_string(int index) const { /// will throw an ecf::bad_conversion if value is not convertible to a string try { return ecf::convert_to(index); } catch (const ecf::bad_conversion&) { } return std::string{}; } std::string RepeatDate::next_value_as_string() const { auto result = ecf::CalendarDate(last_valid_value()) + delta_; long val = result.value(); try { return ecf::convert_to(valid_value(val)); } catch (const ecf::bad_conversion&) { } return std::string{}; } std::string RepeatDate::prev_value_as_string() const { auto result = ecf::CalendarDate(last_valid_value()) - delta_; long val = result.value(); try { return ecf::convert_to(valid_value(val)); } catch (const ecf::bad_conversion&) { } return std::string{}; } void RepeatDate::increment() { auto result = ecf::CalendarDate(last_valid_value()) + delta_; set_value(result.value()); } void RepeatDate::change(const std::string& newdate) { if (newdate.size() != 8) { throw std::runtime_error(MESSAGE( "RepeatDate::change: " << toString() << " The new date is not valid, expected 8 characters in yyyymmdd format but found " << newdate)); } long the_new_date = 0; try { the_new_date = ecf::convert_to(newdate); } catch (const ecf::bad_conversion&) { throw std::runtime_error(MESSAGE("RepeatDate::change: " << toString() << " The new date(" << newdate << ") is not convertible to an long")); } // Use date lib to check YMD try { (void)boost::gregorian::date(boost::gregorian::from_undelimited_string(newdate)); } catch (std::exception& e) { throw std::runtime_error( MESSAGE("RepeatDate::change: " << toString() << " The new date(" << newdate << ") is not valid")); } changeValue(the_new_date); } void RepeatDate::changeValue(long the_new_date) { if (delta_ > 0) { if (the_new_date < start_ || the_new_date > end_) { throw std::runtime_error( MESSAGE("RepeatDate::changeValue: " << toString() << "\nThe new date should be in the range[" << start_ << " : " << end_ << "] but found " << the_new_date)); } } else { if (the_new_date > start_ || the_new_date < end_) { throw std::runtime_error( MESSAGE("RepeatDate::changeValue: " << toString() << "\nThe new date should be in the range[" << start_ << " : " << end_ << "] but found " << the_new_date)); } } // Check new value is in step. ECFLOW-325 repeat date 7 long julian_new_date = ecf::CalendarDate(the_new_date).as_julian_day().value(); long julian_start = ecf::CalendarDate(start_).as_julian_day().value(); long diff = julian_new_date - julian_start; if (diff % delta_ != 0) { throw std::runtime_error(MESSAGE("RepeatDate::changeValue: " << toString() << "\nThe new date " << the_new_date << " is not in line with the delta/step")); } set_value(the_new_date); } void RepeatDate::set_value(long the_new_date) { // Note: the node is incremented one past, the last value // In Node we increment() then check for validity // hence the_new_value may be outside the valid range. // This can occur when an incremental sync happens, // hence, allow memento to copy the value as is. value_ = the_new_date; update_repeat_genvar_value(); incr_state_change_no(); } // ============================================================= RepeatDateTime::RepeatDateTime(const std::string& variable, const std::string& start, const std::string& end, const std::string& delta) : RepeatDateTime(variable, Instant::parse(start), Instant::parse(end), Duration::parse(delta)) { } RepeatDateTime::RepeatDateTime(const std::string& variable, Instant start, Instant end, Duration delta) : RepeatBase(variable), start_(start), end_(end), delta_(delta), value_(start) { if (!Str::valid_name(variable)) { throw std::runtime_error("RepeatDateTime::RepeatDateTime: Invalid name: " + variable); } if (delta == Duration{std::chrono::seconds{0}}) { throw std::runtime_error(MESSAGE("Invalid Repeat datetime: the delta cannot be zero" << "repeat " << variable << " " << start << " " << end << " " << delta)); } auto theStart = boost::lexical_cast(start); if (theStart.size() != 15) { throw std::runtime_error( MESSAGE("Invalid Repeat datetime: The start is not a valid date+time. Please use yyyymmddTMMHHSS format." << "repeat " << variable << " " << start << " " << end << " " << delta)); } auto theEnd = boost::lexical_cast(end); if (theEnd.size() != 15) { throw std::runtime_error( MESSAGE("Invalid Repeat datetime: The end is not a valid date+time. Please use yyyymmddTHHMMSS format." << "repeat " << variable << " " << start << " " << end << " " << delta)); } if (delta_ > Duration{std::chrono::seconds{0}}) { // assert end => start if (!(end >= start)) { throw std::runtime_error(MESSAGE( "Invalid Repeat datetime: The end must be greater than the start date+time, when delta is positive " << "repeat " << variable << " " << start << " " << end << " " << delta)); } } else { // assert start >= end if (!(start >= end)) { throw std::runtime_error(MESSAGE( "Invalid Repeat datetime: The start must be greater than the end date+time, when delta is negative " << "repeat " << variable << " " << start << " " << end << " " << delta)); } } } void RepeatDateTime::gen_variables(std::vector& vec) const { for (const auto& entry : generated_) { vec.push_back(entry); } RepeatBase::gen_variables(vec); } const Variable& RepeatDateTime::find_gen_variable(const std::string& name) const { if (name == name_) { return var_; } for (const auto& entry : generated_) { if (entry.name() == name) { return entry; } } return Variable::EMPTY(); } void RepeatDateTime::update_repeat_genvar() const { RepeatBase::update_repeat_genvar(); // Reset variables values generated_.set_value(""); update_repeat_genvar_value(); } void RepeatDateTime::update_repeat_genvar_value() const { std::string date_as_string = valueAsString(); if (valid()) { try { auto dt = boost::posix_time::from_iso_string(date_as_string); // Using boost posix_time/gregorian, since C++17 still doesn't include calendar types. auto d = dt.date(); // Date generated_[name_ + "_DATE"].set_value(boost::gregorian::to_iso_string(d)); // Date Components auto year = static_cast(d.year()); generated_[name_ + "_YYYY"].set_value(as_4_digits(year)); auto month = d.month().as_number(); generated_[name_ + "_MM"].set_value(as_2_digits(month)); auto day = d.day().as_number(); generated_[name_ + "_DD"].set_value(as_2_digits(day)); auto julian = d.julian_day(); generated_[name_ + "_JULIAN"].set_value(std::to_string(julian)); auto t = dt.time_of_day(); // Time generated_[name_ + "_TIME"].set_value(boost::posix_time::to_iso_string(t)); // Time Components int hours = t.hours(); generated_[name_ + "_HOURS"].set_value(as_2_digits(hours)); int minutes = t.minutes(); generated_[name_ + "_MINUTES"].set_value(as_2_digits(minutes)); int seconds = t.seconds(); generated_[name_ + "_SECONDS"].set_value(as_2_digits(seconds)); } catch (std::exception& e) { log(Log::ERR, MESSAGE("RepeatDateTime::update_repeat_genvar_value : " << toString() << "\n The current date(" << date_as_string << ") is not valid")); return; } } } bool RepeatDateTime::compare(RepeatBase* rb) const { auto* rhs = dynamic_cast(rb); if (!rhs) { return false; } return operator==(*rhs); } void RepeatDateTime::setToLastValue() { set_value(coerce_from_instant_into_seconds(end_)); } long RepeatDateTime::last_valid_value() const { Instant valid = valid_value(value_); return coerce_from_instant_into_seconds(valid); } Instant RepeatDateTime::valid_value(const Instant& value) const { if (delta_ > Duration{std::chrono::seconds{0}}) { if (value < start_) { return start_; } if (value > end_) { return end_; } return value; } if (value > start_) { return start_; } if (value < end_) { return end_; } return value; } long RepeatDateTime::last_valid_value_minus(int val) const { Instant last_value = coerce_from_seconds_into_instant(last_valid_value()); Instant updated_value = last_value - Duration{std::chrono::seconds{val}}; return coerce_from_instant_into_seconds(updated_value); } long RepeatDateTime::last_valid_value_plus(int val) const { Instant last_value = coerce_from_seconds_into_instant(last_valid_value()); Instant updated_value = last_value + Duration{std::chrono::seconds{val}}; return coerce_from_instant_into_seconds(updated_value); } void RepeatDateTime::reset() { set_value(coerce_from_instant_into_seconds(start_)); } std::string RepeatDateTime::dump() const { return MESSAGE(toString() << " value(" << value_ << ")"); } bool RepeatDateTime::operator==(const RepeatDateTime& rhs) const { if (name_ != rhs.name_) { return false; } if (start_ != rhs.start_) { return false; } if (end_ != rhs.end_) { return false; } if (delta_ != rhs.delta_) { return false; } if (value_ != rhs.value_) { return false; } return true; } std::string RepeatDateTime::valueAsString() const { /// will throw a boost::bad_lexical_cast& if value is not convertible to a string try { long value = last_valid_value(); Instant instant = coerce_from_seconds_into_instant(value); return boost::lexical_cast(instant); } catch (boost::bad_lexical_cast&) { LOG_ASSERT(false, "RepeatDateTime::valueAsString(): could not convert value " << value_ << " to a string"); } return {}; } std::string RepeatDateTime::value_as_string(int index) const { /// will throw a boost::bad_lexical_cast& if value is not convertible to a string try { return boost::lexical_cast(index); } catch (boost::bad_lexical_cast&) { } return {}; } std::string RepeatDateTime::next_value_as_string() const { Instant next = coerce_from_seconds_into_instant(last_valid_value()) + delta_; try { return boost::lexical_cast(valid_value(next)); } catch (boost::bad_lexical_cast&) { } return {}; } std::string RepeatDateTime::prev_value_as_string() const { Instant previous = coerce_from_seconds_into_instant(last_valid_value()) - delta_; try { return boost::lexical_cast(valid_value(previous)); } catch (boost::bad_lexical_cast&) { } return {}; } void RepeatDateTime::increment() { auto new_value = value_ + delta_; set_value(ecf::coerce_from_instant_into_seconds(new_value)); } void RepeatDateTime::change(const std::string& newdate) { long the_new_date; try { auto instant = Instant::parse(newdate); the_new_date = ecf::coerce_from_instant_into_seconds(instant); } catch (std::exception& e) { throw std::runtime_error( MESSAGE("RepeatDateTime::change: " << toString() << " The new date(" << newdate << ") is not valid")); } changeValue(the_new_date); } void RepeatDateTime::changeValue(long the_new_date) { auto new_date = ecf::coerce_from_seconds_into_instant(the_new_date); if (delta_ > Duration{std::chrono::seconds{0}}) { if (new_date < start_ || new_date > end_) { throw std::runtime_error( MESSAGE("RepeatDateTime::changeValue: " << toString() << "\nThe new date should be in the range[" << start_ << " : " << end_ << "] but found " << new_date)); } } else { if (new_date > start_ || new_date < end_) { throw std::runtime_error( MESSAGE("RepeatDateTime::changeValue: " << toString() << "\nThe new date should be in the range[" << start_ << " : " << end_ << "] but found " << the_new_date)); } } // Ensure that new value is in step auto diff = new_date - start_; if (diff.as_seconds().count() % delta_.as_seconds().count() != 0) { throw std::runtime_error(MESSAGE("RepeatDateTime::changeValue: " << toString() << "\nThe new date " << the_new_date << " is not in line with the delta/step")); } set_value(the_new_date); } void RepeatDateTime::set_value(long the_new_date) { // Note: the node is incremented one past, the last value // In Node we increment() then check for validity // hence the_new_value may be outside the valid range. // This can occur when an incremental sync happens, // hence, allow memento to copy the value as is. value_ = coerce_from_seconds_into_instant(the_new_date); update_repeat_genvar_value(); incr_state_change_no(); } //====================================================================================== RepeatDateList::RepeatDateList(const std::string& variable, const std::vector& l) : RepeatBase(variable), list_(l) { if (!Str::valid_name(variable)) { throw std::runtime_error("RepeatDateList: Invalid name: " + variable); } if (list_.empty()) { throw std::runtime_error("RepeatDateList: " + variable + " is empty"); } for (int i : list_) { std::string date_i = ecf::convert_to(i); if (date_i.size() != 8) { throw std::runtime_error( MESSAGE("Invalid Repeat datelist : " << variable << " the date " << i << " is not valid. Please use yyyymmdd format.")); } try { (void)boost::gregorian::date(boost::gregorian::from_undelimited_string(date_i)); } catch (std::exception& e) { throw std::runtime_error( MESSAGE("Invalid Repeat datelist : " << variable << " the date " << i << " is not valid. Please use yyyymmdd format.")); } } } void RepeatDateList::gen_variables(std::vector& vec) const { vec.push_back(yyyy_); vec.push_back(mm_); vec.push_back(dom_); vec.push_back(dow_); vec.push_back(julian_); RepeatBase::gen_variables(vec); } const Variable& RepeatDateList::find_gen_variable(const std::string& name) const { if (name == name_) { return var_; } if (name == yyyy_.name()) { return yyyy_; } if (name == mm_.name()) { return mm_; } if (name == dom_.name()) { return dom_; } if (name == dow_.name()) { return dow_; } if (name == julian_.name()) { return julian_; } return Variable::EMPTY(); } void RepeatDateList::update_repeat_genvar() const { RepeatBase::update_repeat_genvar(); yyyy_.set_name(name_ + "_YYYY"); yyyy_.set_value(""); mm_.set_name(name_ + "_MM"); mm_.set_value(""); dom_.set_name(name_ + "_DD"); dom_.set_value(""); dow_.set_name(name_ + "_DOW"); dom_.set_value(""); julian_.set_name(name_ + "_JULIAN"); julian_.set_value(""); update_repeat_genvar_value(); } void RepeatDateList::update_repeat_genvar_value() const { if (valid()) { std::string date_as_string = valueAsString(); try { auto the_date = boost::gregorian::from_undelimited_string(date_as_string); if (the_date.is_special()) { log(Log::ERR, MESSAGE("RepeatDateList::update_repeat_genvar_value(): " << toString() << "\n invalid current date: " << date_as_string << " is special ")); return; } auto year = static_cast(the_date.year()); yyyy_.set_value(as_4_digits(year)); auto month = the_date.month().as_number(); mm_.set_value(as_2_digits(month)); auto day_of_month = the_date.day().as_number(); dom_.set_value(as_2_digits(day_of_month)); auto day_of_week = the_date.day_of_week().as_number(); dow_.set_value(std::to_string(day_of_week)); auto julian_day = CalendarDate(last_valid_value()).as_julian_day().value(); julian_.set_value(std::to_string(julian_day)); } catch (std::exception& e) { log(Log::ERR, MESSAGE("RepeatDateList::update_repeat_genvar_value(): " << toString() << "\n invalid current date: " << date_as_string)); } } } int RepeatDateList::start() const { if (list_.empty()) { return 0; } return list_[0]; } int RepeatDateList::end() const { if (list_.empty()) { return 0; } return list_[list_.size() - 1]; } bool RepeatDateList::compare(RepeatBase* rb) const { auto* rhs = dynamic_cast(rb); if (!rhs) { return false; } return operator==(*rhs); } std::string RepeatDateList::dump() const { return MESSAGE(toString() << " ordinal-value(" << value() << ") value-as-string(" << valueAsString() << ")"); } void RepeatDateList::reset() { set_value(0); } void RepeatDateList::increment() { set_value(currentIndex_ + 1); } long RepeatDateList::value() const { if (list_.empty()) { return 0; } if (currentIndex_ >= 0 && currentIndex_ < static_cast(list_.size())) { return list_[currentIndex_]; } return 0; } long RepeatDateList::last_valid_value() const { if (list_.empty()) { return 0; } if (currentIndex_ >= 0 && currentIndex_ < static_cast(list_.size())) { return list_[currentIndex_]; } if (currentIndex_ < 0) { return list_[0]; } if (currentIndex_ >= static_cast(list_.size())) { return list_[list_.size() - 1]; } return 0; } long RepeatDateList::last_valid_value_minus(int val) const { long last_value = last_valid_value(); if (last_value == 0) { return 0; } return (CalendarDate(last_value) - val).value(); } long RepeatDateList::last_valid_value_plus(int val) const { long last_value = last_valid_value(); if (last_value == 0) { return 0; } return (CalendarDate(last_value) + val).value(); } void RepeatDateList::setToLastValue() { if (list_.empty()) { return; } set_value(static_cast(list_.size() - 1)); } std::string RepeatDateList::valueAsString() const { return ecf::convert_to(last_valid_value()); } std::string RepeatDateList::value_as_string(int index) const { if (list_.empty()) { return std::string("0"); } if (index >= 0 && index < static_cast(list_.size())) { return ecf::convert_to(list_[index]); } if (index < 0) { return ecf::convert_to(list_[0]); } if (index >= static_cast(list_.size())) { return ecf::convert_to(list_[list_.size() - 1]); } return std::string{}; } std::string RepeatDateList::next_value_as_string() const { if (list_.empty()) { return std::string("0"); } int index = currentIndex_; index++; return value_as_string(index); } std::string RepeatDateList::prev_value_as_string() const { if (list_.empty()) { return std::string("0"); } int index = currentIndex_; index--; return value_as_string(index); } void RepeatDateList::change(const std::string& newValue) { // See if it matches one of the dates int new_val = 0; try { new_val = ecf::convert_to(newValue); } catch (...) { throw std::runtime_error(MESSAGE( "RepeatDateList::change: " << toString() << "\nThe new value " << newValue << " is must be convertible to integer, and correspond to an existing value\n")); } for (size_t i = 0; i < list_.size(); i++) { if (list_[i] == new_val) { set_value(i); return; } } throw std::runtime_error(MESSAGE("RepeatDateList::change: " << toString() << "\nThe new value " << newValue << " is not a valid member of the date list\n")); } void RepeatDateList::changeValue(long the_new_index) { if (list_.empty()) { return; } if (the_new_index < 0 || the_new_index >= static_cast(list_.size())) { throw std::runtime_error(MESSAGE("RepeatDateList::changeValue:" << toString() << "\nThe new value '" << the_new_index << "' is not a valid index " << "expected range[0-" << list_.size() - 1 << "] but found '" << the_new_index << "'")); } set_value(the_new_index); } void RepeatDateList::set_value(long the_new_index) { if (list_.empty()) { return; } // Note: the node is incremented one past, the last value // In Node we increment() then check for validity // hence the_new_value may be outside the valid range. // This can occur when an incremental sync happens, // hence, allow memento to copy the value as is. currentIndex_ = the_new_index; update_repeat_genvar_value(); incr_state_change_no(); } bool RepeatDateList::operator==(const RepeatDateList& rhs) const { if (name_ != rhs.name_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "RepeatDateList::operator==( name_(" << name_ << ") != rhs.name_(" << rhs.name_ << "))\n"; } #endif return false; } if (list_ != rhs.list_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "RepeatDateList::operator==( list_ != rhs.list_ )\n"; } #endif return false; } if (currentIndex_ != rhs.currentIndex_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "RepeatDateList::operator==( currentIndex_(" << currentIndex_ << ") != rhs.currentIndex_(" << rhs.currentIndex_ << "))\n"; } #endif return false; } return true; } //====================================================================================== RepeatInteger::RepeatInteger(const std::string& variable, int start, int end, int delta) : RepeatBase(variable), start_(start), end_(end), delta_(delta), value_(start) { // cout << toString() << "\n"; if (!Str::valid_name(variable)) { throw std::runtime_error("RepeatInteger: Invalid name: " + variable); } } RepeatInteger::RepeatInteger() = default; bool RepeatInteger::compare(RepeatBase* rb) const { auto* rhs = dynamic_cast(rb); if (!rhs) { return false; } return operator==(*rhs); } void RepeatInteger::reset() { value_ = start_; incr_state_change_no(); } long RepeatInteger::last_valid_value() const { return valid_value(value_); } long RepeatInteger::valid_value(long value) const { if (delta_ > 0) { if (value < start_) { return start_; } if (value > end_) { return end_; } return value; } if (value > start_) { return start_; } if (value < end_) { return end_; } return value; } void RepeatInteger::increment() { value_ += delta_; incr_state_change_no(); } void RepeatInteger::change(const std::string& newValue) { long the_new_value = 0; try { the_new_value = ecf::convert_to(newValue); } catch (const ecf::bad_conversion&) { throw std::runtime_error(MESSAGE("RepeatInteger::change:" << toString() << " The new value(" << newValue << ") is not convertible to an long")); } changeValue(the_new_value); } void RepeatInteger::changeValue(long the_new_value) { if (delta_ > 0) { if (the_new_value < start_ || the_new_value > end_) { throw std::runtime_error( MESSAGE("RepeatInteger::changeValue:" << toString() << ". The new value should be in the range[" << start_ << "-" << end_ << "] but found " << the_new_value)); } } else { if (the_new_value > start_ || the_new_value < end_) { throw std::runtime_error( MESSAGE("RepeatInteger::changeValue:" << toString() << ". The new value should be in the range[" << start_ << "-" << end_ << "] but found " << the_new_value)); } } set_value(the_new_value); } void RepeatInteger::set_value(long the_new_value) { // To be used by Memento only. as it does no checking // Note: the node is incremented one past, the last value // In Node we increment() then check for validity // hence the_new_value may be outside the valid range. // This can occur when an incremental sync happens, // hence, allow memento to copy the value as is. value_ = the_new_value; incr_state_change_no(); } void RepeatInteger::setToLastValue() { value_ = end_; incr_state_change_no(); } std::string RepeatInteger::dump() const { return MESSAGE(toString() << " value(" << value_ << ")"); } bool RepeatInteger::operator==(const RepeatInteger& rhs) const { if (name_ != rhs.name_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "RepeatInteger::operator==( name_(" << name_ << ") != rhs.name_(" << rhs.name_ << "))" << "\n"; } #endif return false; } if (start_ != rhs.start_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "RepeatInteger::operator==( start_(" << start_ << ") != rhs.start_(" << rhs.start_ << "))" << "\n"; } #endif return false; } if (end_ != rhs.end_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "RepeatInteger::operator==( end_(" << end_ << ") != rhs.end_(" << rhs.end_ << "))" << "\n"; } #endif return false; } if (delta_ != rhs.delta_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "RepeatInteger::operator==( delta_(" << delta_ << ") != rhs.delta_(" << rhs.delta_ << "))" << "\n"; } #endif return false; } if (value_ != rhs.value_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "RepeatInteger::operator==( value_(" << value_ << ") != rhs.value_(" << rhs.value_ << "))" << "\n"; } #endif return false; } return true; } std::string RepeatInteger::valueAsString() const { /// will throw an ecf::bad_conversion if value is not convertible to a string try { return ecf::convert_to(last_valid_value()); } catch (const ecf::bad_conversion&) { LOG_ASSERT(false, ""); } return std::string{}; } std::string RepeatInteger::value_as_string(int index) const { /// will throw an ecf::bad_conversion if value is not convertible to a string try { return ecf::convert_to(index); } catch (const ecf::bad_conversion&) { } return std::string{}; } std::string RepeatInteger::next_value_as_string() const { long val = last_valid_value(); val += delta_; try { return ecf::convert_to(valid_value(val)); } catch (const ecf::bad_conversion&) { } return std::string{}; } std::string RepeatInteger::prev_value_as_string() const { long val = last_valid_value(); val -= delta_; try { return ecf::convert_to(valid_value(val)); } catch (const ecf::bad_conversion&) { } return std::string{}; } //====================================================================================== RepeatEnumerated::RepeatEnumerated(const std::string& variable, const std::vector& theEnums) : RepeatBase(variable), theEnums_(theEnums) { if (!Str::valid_name(variable)) { throw std::runtime_error("RepeatEnumerated: Invalid name: " + variable); } if (theEnums.empty()) { throw std::runtime_error("RepeatEnumerated: " + variable + " is empty"); } } int RepeatEnumerated::end() const { if (theEnums_.empty()) { return 0; } return static_cast(theEnums_.size() - 1); } bool RepeatEnumerated::compare(RepeatBase* rb) const { auto* rhs = dynamic_cast(rb); if (!rhs) { return false; } return operator==(*rhs); } std::string RepeatEnumerated::dump() const { return MESSAGE(toString() << " ordinal-value(" << value() << ") value-as-string(" << valueAsString() << ")"); } void RepeatEnumerated::reset() { currentIndex_ = 0; incr_state_change_no(); } void RepeatEnumerated::increment() { currentIndex_++; incr_state_change_no(); } long RepeatEnumerated::value() const { if (currentIndex_ >= 0 && currentIndex_ < static_cast(theEnums_.size())) { try { return ecf::convert_to(theEnums_[currentIndex_]); } catch (const ecf::bad_conversion&) { // Ignore and return currentIndex_ } } return currentIndex_; } long RepeatEnumerated::last_valid_value() const { if (!theEnums_.empty()) { if (currentIndex_ < 0) { try { return ecf::convert_to(theEnums_[0]); } catch (const ecf::bad_conversion&) { /* Ignore and return first index */ } return 0; } if (currentIndex_ >= static_cast(theEnums_.size())) { try { return ecf::convert_to(theEnums_[theEnums_.size() - 1]); } catch (const ecf::bad_conversion&) { /* Ignore and return last index */ } return static_cast(theEnums_.size() - 1); } // return current value as integer or as index return value(); } return 0; } void RepeatEnumerated::setToLastValue() { currentIndex_ = static_cast(theEnums_.size() - 1); if (currentIndex_ < 0) { currentIndex_ = 0; } incr_state_change_no(); } std::string RepeatEnumerated::valueAsString() const { // This must always return a valid value if (!theEnums_.empty()) { // Returns the last valid value if (currentIndex_ < 0) { return theEnums_[0]; // return first } if (currentIndex_ >= static_cast(theEnums_.size())) { return theEnums_[theEnums_.size() - 1]; // return last } return theEnums_[currentIndex_]; } return std::string{}; } std::string RepeatEnumerated::value_as_string(int index) const { if (index >= 0 && index < static_cast(theEnums_.size())) { return theEnums_[index]; } return std::string{}; } std::string RepeatEnumerated::next_value_as_string() const { if (theEnums_.empty()) { return std::string{}; } int index = currentIndex_; index++; if (index < 0) { return theEnums_[0]; // return first } if (index >= static_cast(theEnums_.size())) { return theEnums_[theEnums_.size() - 1]; // return last } return theEnums_[index]; } std::string RepeatEnumerated::prev_value_as_string() const { if (theEnums_.empty()) { return std::string{}; } int index = currentIndex_; index--; if (index < 0) { return theEnums_[0]; // return first } if (index >= static_cast(theEnums_.size())) { return theEnums_[theEnums_.size() - 1]; // return last } return theEnums_[index]; } void RepeatEnumerated::change(const std::string& newValue) { // See if it matches one of the enums for (size_t i = 0; i < theEnums_.size(); i++) { if (theEnums_[i] == newValue) { currentIndex_ = i; incr_state_change_no(); return; } } // If the value is convertible to an integer, treat as an index try { auto the_new_value = ecf::convert_to(newValue); changeValue(the_new_value); // can throw if out of range return; } catch (const ecf::bad_conversion&) { } throw std::runtime_error( MESSAGE("RepeatEnumerated::change:" << toString() << "\nThe new value " << newValue << " is not a valid index or a member of the enumerated list\n")); } void RepeatEnumerated::changeValue(long the_new_value) { if (the_new_value < 0 || the_new_value >= static_cast(theEnums_.size())) { throw std::runtime_error(MESSAGE("RepeatEnumerated::changeValue:" << toString() << "\nThe new value '" << the_new_value << "' is not a valid index " << "expected range[0-" << theEnums_.size() - 1 << "] but found '" << the_new_value << "'")); } set_value(the_new_value); } void RepeatEnumerated::set_value(long the_new_value) { // Note: the node is incremented one past, the last value // In Node we increment() then check for validity // hence the_new_value may be outside the valid range. // This can occur when an incremental sync happens, // hence, allow memento to copy the value as is. currentIndex_ = the_new_value; incr_state_change_no(); } bool RepeatEnumerated::operator==(const RepeatEnumerated& rhs) const { if (name_ != rhs.name_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "RepeatEnumerated::operator==( name_(" << name_ << ") != rhs.name_(" << rhs.name_ << "))\n"; } #endif return false; } if (theEnums_ != rhs.theEnums_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "RepeatEnumerated::operator==( theEnums_ != rhs.theEnums_ )\n"; } #endif return false; } if (currentIndex_ != rhs.currentIndex_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "RepeatEnumerated::operator==( currentIndex_(" << currentIndex_ << ") != rhs.currentIndex_(" << rhs.currentIndex_ << "))\n"; } #endif return false; } return true; } //====================================================================================== RepeatString::RepeatString(const std::string& variable, const std::vector& theEnums) : RepeatBase(variable), theStrings_(theEnums) { if (!Str::valid_name(variable)) { throw std::runtime_error("RepeatString:: Invalid name: " + variable); } if (theEnums.empty()) { throw std::runtime_error("RepeatString : " + variable + " is empty"); } } int RepeatString::end() const { if (theStrings_.empty()) { return 0; } return static_cast(theStrings_.size() - 1); } bool RepeatString::compare(RepeatBase* rb) const { auto* rhs = dynamic_cast(rb); if (!rhs) { return false; } return operator==(*rhs); } std::string RepeatString::dump() const { return MESSAGE(toString() << " ordinal-value(" << value() << ") value-as-string(" << valueAsString() << ")"); } void RepeatString::reset() { currentIndex_ = 0; incr_state_change_no(); } long RepeatString::last_valid_value() const { if (!theStrings_.empty()) { if (currentIndex_ < 0) { return 0; } if (currentIndex_ >= static_cast(theStrings_.size())) { return static_cast(theStrings_.size() - 1); } return currentIndex_; } return 0; } std::string RepeatString::next_value_as_string() const { if (!theStrings_.empty()) { int index = currentIndex_; index++; if (index < 0) { return theStrings_[0]; // return first } if (index >= static_cast(theStrings_.size())) { return theStrings_[theStrings_.size() - 1]; // return last } return theStrings_[index]; } return std::string{}; } std::string RepeatString::prev_value_as_string() const { if (!theStrings_.empty()) { int index = currentIndex_; index--; if (index < 0) { return theStrings_[0]; // return first } if (index >= static_cast(theStrings_.size())) { return theStrings_[theStrings_.size() - 1]; // return last } return theStrings_[index]; } return std::string{}; } void RepeatString::increment() { currentIndex_++; incr_state_change_no(); } void RepeatString::setToLastValue() { currentIndex_ = static_cast(theStrings_.size() - 1); if (currentIndex_ < 0) { currentIndex_ = 0; } incr_state_change_no(); } std::string RepeatString::valueAsString() const { if (!theStrings_.empty()) { return theStrings_[last_valid_value()]; } return std::string{}; } std::string RepeatString::value_as_string(int index) const { if (index >= 0 && index < static_cast(theStrings_.size())) { return theStrings_[index]; } return std::string{}; } void RepeatString::change(const std::string& newValue) { // See if it matches one of the strings for (size_t i = 0; i < theStrings_.size(); i++) { if (theStrings_[i] == newValue) { currentIndex_ = i; incr_state_change_no(); return; } } // If the value is convertible to an integer, treat as an index try { long the_new_value = ecf::convert_to(newValue); changeValue(the_new_value); return; } catch (const ecf::bad_conversion&) { } throw std::runtime_error(MESSAGE("RepeatString::change: " << toString() << "\nThe new value " << newValue << " is not a valid index or member of the string list")); } void RepeatString::changeValue(long the_new_value) { if (the_new_value < 0 || the_new_value >= static_cast(theStrings_.size())) { throw std::runtime_error(MESSAGE("RepeatString::change: " << toString() << " The new the integer " << the_new_value << " is not a valid index " << "expected range[0-" << theStrings_.size() - 1 << "]'")); } set_value(the_new_value); } void RepeatString::set_value(long the_new_value) { // Note: the node is incremented one past, the last value // In Node we increment() then check for validity // hence the_new_value may be outside the valid range. // This can occur when an incremental sync happens, // hence, allow memento to copy the value as is. currentIndex_ = the_new_value; incr_state_change_no(); } bool RepeatString::operator==(const RepeatString& rhs) const { if (name_ != rhs.name_) { return false; } if (theStrings_ != rhs.theStrings_) { return false; } if (currentIndex_ != rhs.currentIndex_) { return false; } return true; } //======================================================================================= bool RepeatDay::compare(RepeatBase* rb) const { auto* rhs = dynamic_cast(rb); if (!rhs) { return false; } return operator==(*rhs); } std::string RepeatDay::dump() const { return toString(); } bool RepeatDay::operator==(const RepeatDay& rhs) const { if (step_ != rhs.step_) { return false; } return true; } // ========================================================================================= template void RepeatBase::serialize(Archive& ar) { ar(CEREAL_NVP(name_)); } template void RepeatDate::serialize(Archive& ar, std::uint32_t const version) { ar(cereal::base_class(this), CEREAL_NVP(start_), CEREAL_NVP(end_), CEREAL_NVP(delta_), CEREAL_NVP(value_)); } template void RepeatDateTime::serialize(Archive& ar, std::uint32_t const version) { ar(cereal::base_class(this), CEREAL_NVP(start_), CEREAL_NVP(end_), CEREAL_NVP(delta_), CEREAL_NVP(value_)); } template void RepeatInteger::serialize(Archive& ar, std::uint32_t const version) { ar(cereal::base_class(this), CEREAL_NVP(start_), CEREAL_NVP(end_), CEREAL_NVP(delta_), CEREAL_NVP(value_)); } template void RepeatEnumerated::serialize(Archive& ar, std::uint32_t const version) { ar(cereal::base_class(this), CEREAL_NVP(theEnums_), CEREAL_NVP(currentIndex_)); } template void RepeatDateList::serialize(Archive& ar, std::uint32_t const version) { ar(cereal::base_class(this), CEREAL_NVP(list_), CEREAL_NVP(currentIndex_)); } template void RepeatString::serialize(Archive& ar, std::uint32_t const version) { ar(cereal::base_class(this), CEREAL_NVP(theStrings_), CEREAL_NVP(currentIndex_)); } template void RepeatDay::serialize(Archive& ar, std::uint32_t const version) { ar(cereal::base_class(this), CEREAL_NVP(step_)); } template void Repeat::serialize(Archive& ar, std::uint32_t const version) { ar(CEREAL_NVP(type_)); } CEREAL_TEMPLATE_SPECIALIZE(RepeatBase); CEREAL_TEMPLATE_SPECIALIZE_V(RepeatDate); CEREAL_TEMPLATE_SPECIALIZE_V(RepeatDateTime); CEREAL_TEMPLATE_SPECIALIZE_V(RepeatDateList); CEREAL_TEMPLATE_SPECIALIZE_V(RepeatInteger); CEREAL_TEMPLATE_SPECIALIZE_V(RepeatEnumerated); CEREAL_TEMPLATE_SPECIALIZE_V(RepeatString); CEREAL_TEMPLATE_SPECIALIZE_V(RepeatDay); CEREAL_TEMPLATE_SPECIALIZE_V(Repeat); CEREAL_REGISTER_TYPE(RepeatDate) CEREAL_REGISTER_TYPE(RepeatDateTime) CEREAL_REGISTER_TYPE(RepeatDateList) CEREAL_REGISTER_TYPE(RepeatInteger) CEREAL_REGISTER_TYPE(RepeatEnumerated) CEREAL_REGISTER_TYPE(RepeatString) CEREAL_REGISTER_TYPE(RepeatDay) ecflow-5.16.0/libs/attribute/src/ecflow/attribute/RepeatRange.hpp0000664000175000017500000002112015161437256025167 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #ifndef ecflow_attribute_RepeatRange_HPP #define ecflow_attribute_RepeatRange_HPP #include "ecflow/attribute/RepeatAttr.hpp" #include "ecflow/core/Calendar.hpp" namespace ecf { template struct Range { explicit Range(const R& r) : r_(r) {} const R& r_; }; template <> struct Range { using size_type = std::size_t; using iterator = std::size_t; using value_type = int; explicit Range(const RepeatDay& r) : r_(r) {} iterator begin() const { return 0; } iterator end() const { return 0; } iterator current_index() const { return r_.index_or_value(); } value_type current_value() const { return r_.value(); } size_type size() const { return end() - begin(); } private: const RepeatDay& r_; }; template <> struct Range { using size_type = std::size_t; using iterator = std::size_t; using value_type = int; explicit Range(const RepeatDate& r) : r_(r) {} iterator begin() const { return 0; } iterator end() const { int i = ecf::CalendarDate(r_.start()).as_julian_day().value(); int j = ecf::CalendarDate(r_.end()).as_julian_day().value(); int s = r_.step(); int d = (j - i) + 1; return (d / s) + ((d % s == 0) ? 0 : 1); } iterator current_index() const { auto i = ecf::CalendarDate(r_.start()).as_julian_day().value(); auto s = r_.step(); auto v = ecf::CalendarDate(r_.value()).as_julian_day().value(); auto idx = (v - i) / s; return idx; } value_type current_value() const { return r_.value(); } value_type at(iterator i) const { int j = ecf::CalendarDate(r_.start()).as_julian_day().value(); return ecf::JulianDay(j + i * r_.step()).as_calendar_date().value(); } size_type size() const { return end() - begin(); } private: const RepeatDate& r_; }; template <> struct Range { using date_t = int; using size_type = std::size_t; using iterator = std::size_t; using value_type = date_t; explicit Range(const RepeatDateList& r) : r_(r) {} iterator begin() const { return 0; } iterator end() const { return r_.indexNum(); } iterator current_index() const { return r_.index_or_value(); } value_type current_value() const { return r_.value(); } value_type at(iterator i) const { return r_.value_at(i); } size_type size() const { return end() - begin(); } private: const RepeatDateList& r_; }; template <> struct Range { using size_type = std::size_t; using iterator = std::size_t; using value_type = Instant; explicit Range(const RepeatDateTime& r) : r_(r) {} iterator begin() const { return 0; } iterator end() const { auto i = r_.start_instant(); auto j = r_.end_instant(); auto s = r_.step_duration(); auto d = (j - i); auto idx = d.as_seconds().count() / s.as_seconds().count(); auto last = i + Duration{std::chrono::seconds{s.as_seconds().count() * idx}}; if (last <= j) { idx++; } return idx; } iterator current_index() const { auto i = r_.start_instant(); auto s = r_.step_duration(); auto v = r_.value_instant(); auto idx = (v - i).as_seconds().count() / s.as_seconds().count(); return idx; } value_type current_value() const { return r_.value_instant(); } value_type at(iterator i) const { auto j = r_.start_instant(); auto s = r_.step_duration(); return j + Duration{std::chrono::seconds{s.as_seconds().count() * i}}; } size_type size() const { return end() - begin(); } private: const RepeatDateTime& r_; }; template <> struct Range { using size_type = std::size_t; using iterator = std::size_t; using value_type = int; explicit Range(const RepeatInteger& r) : r_(r) {} iterator begin() const { return 0; } iterator end() const { int i = r_.start(); int j = r_.end(); int s = r_.step(); int d = (j - i) + 1; auto idx = (d / s) + ((d % s == 0) ? 0 : 1); return idx; } iterator current_index() const { auto i = r_.start(); auto s = r_.step(); auto v = r_.value(); auto idx = (v - i) / s; return idx; } value_type current_value() const { return r_.value(); } value_type at(iterator i) const { return r_.start() + i * r_.step(); } size_type size() const { return end() - begin(); } private: const RepeatInteger& r_; }; template <> struct Range { using size_type = std::size_t; using iterator = std::size_t; using value_type = std::string; explicit Range(const RepeatEnumerated& r) : r_(r) {} iterator begin() const { return 0; } iterator end() const { return r_.end() + 1; } iterator current_index() const { return r_.index_or_value(); } value_type current_value() const { return r_.valueAsString(); } value_type at(iterator i) const { return r_.value_as_string(i); } size_type size() const { return end() - begin(); } private: const RepeatEnumerated& r_; }; template <> struct Range { using size_type = std::size_t; using iterator = std::size_t; using value_type = std::string; explicit Range(const RepeatString& r) : r_(r) {} iterator begin() const { return 0; } iterator end() const { return r_.end() + 1; } iterator current_index() const { return r_.index_or_value(); } value_type current_value() const { return r_.valueAsString(); } value_type at(iterator i) const { return r_.value_as_string(i); } size_type size() const { return end() - begin(); } private: const RepeatString& r_; }; template auto front(const Range& rng) { assert(rng.size() > 0); return rng.at(rng.begin()); } template auto back(const Range& rng) { assert(rng.size() > 0); return rng.at(rng.end() - 1); } template auto size(const Range& rng) { return rng.size(); } template bool empty(const Range& rng) { return size(rng) == 0; } struct Limits { /* * The limits of a Repeat is described by a 0-based range. * * - begin: the first index in the range (inclusive, meaning it is a valid index) * - end: the last index in the range (exclusive, meaning it is one past the last valid index) * * - current is expected to be in the interval [begin, end[ */ size_t begin; size_t end; size_t current; bool is_first() const { return current == begin; } bool is_last() const { return current + 1 >= end; } }; Limits limits_of(const RepeatBase* repeat) { if (auto r1 = dynamic_cast(repeat)) { Range rng(*r1); return {rng.begin(), rng.end(), rng.current_index()}; } else if (auto r2 = dynamic_cast(repeat)) { Range rng(*r2); return {rng.begin(), rng.end(), rng.current_index()}; } else if (auto r3 = dynamic_cast(repeat)) { Range rng(*r3); return {rng.begin(), rng.end(), rng.current_index()}; } else if (auto r4 = dynamic_cast(repeat)) { Range rng(*r4); return {rng.begin(), rng.end(), rng.current_index()}; } else if (auto r5 = dynamic_cast(repeat)) { Range rng(*r5); return {rng.begin(), rng.end(), rng.current_index()}; } else if (auto r6 = dynamic_cast(repeat)) { Range rng(*r6); return {rng.begin(), rng.end(), rng.current_index()}; } else if (auto r7 = dynamic_cast(repeat)) { Range rng(*r7); return {rng.begin(), rng.end(), rng.current_index()}; } assert(false); // Should never be reached! return {0, 1, 0}; } template auto make_range(const Repeat& repeat) { return Range{repeat.as()}; } } // namespace ecf #endif /* ecflow_attribute_RepeatRange_HPP */ ecflow-5.16.0/libs/attribute/src/ecflow/attribute/TimeAttr.cpp0000664000175000017500000001616015161437256024526 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "ecflow/attribute/TimeAttr.hpp" #include #include #include "ecflow/attribute/DateAttr.hpp" // Used in Why #include "ecflow/attribute/DayAttr.hpp" // Used in Why #include "ecflow/core/Calendar.hpp" #include "ecflow/core/Ecf.hpp" #include "ecflow/core/Log.hpp" #include "ecflow/core/Serialization.hpp" #include "ecflow/core/Str.hpp" namespace ecf { TimeAttr::TimeAttr(const std::string& str) { if (str.empty()) { throw std::runtime_error("Time::Time: empty string passed"); } std::vector tokens; Str::split(str, tokens); if (tokens.empty()) { throw std::runtime_error("Time::Time: incorrect time string ?"); } size_t index = 0; ts_ = TimeSeries::create(index, tokens, false /*parse_state*/); } void TimeAttr::calendarChanged(const ecf::Calendar& c) { // ensure this called first , since we need always update for relative duration. ECFLOW-1648 if (ts_.calendarChanged(c)) { state_change_no_ = Ecf::incr_state_change_no(); } // log(Log::DBG,"TimeAttr::calendarChanged(1) " + dump()); // ECFLOW-1648 if (free_) { return; } // For a time series, we rely on the re queue to reset makeFree if (isFree(c)) { setFree(); } // log(Log::DBG,"TimeAttr::calendarChanged(2) " + dump()); // ECFLOW-1648 } void TimeAttr::resetRelativeDuration() { if (ts_.resetRelativeDuration()) { state_change_no_ = Ecf::incr_state_change_no(); } } std::string TimeAttr::name() const { std::string ret; write(ret); ts_.write_state_for_gui(ret, free_); return ret; } std::string TimeAttr::toString() const { std::string ret; write(ret); return ret; } void TimeAttr::write(std::string& ret) const { ret += "time "; ts_.write(ret); } std::string TimeAttr::dump() const { return MESSAGE("time " << (free_ ? "(free) " : "(holding) ") << ts_.dump()); } bool TimeAttr::operator==(const TimeAttr& rhs) const { if (free_ != rhs.free_) { return false; } return ts_.operator==(rhs.ts_); } bool TimeAttr::structureEquals(const TimeAttr& rhs) const { return ts_.structureEquals(rhs.ts_); } bool TimeAttr::isFree(const ecf::Calendar& calendar) const { // The FreeDepCmd can be used to free the time, if (free_) { return true; } return is_free(calendar); } bool TimeAttr::is_free(const ecf::Calendar& calendar) const { return ts_.isFree(calendar); } void TimeAttr::setFree() { free_ = true; state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_STATE_CHANGE_NO std::cout << "TimeAttr::setFree()\n"; #endif } void TimeAttr::clearFree() { free_ = false; state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_STATE_CHANGE_NO std::cout << "TimeAttr::clearFree()\n"; #endif } void TimeAttr::miss_next_time_slot() { ts_.miss_next_time_slot(); state_change_no_ = Ecf::incr_state_change_no(); } bool TimeAttr::why(const ecf::Calendar& c, const std::vector& days, const std::vector& dates, std::string& theReasonWhy) const { if (isFree(c)) { return false; } theReasonWhy += "is time "; if (!days.empty()) { theReasonWhy += "and day "; } if (!dates.empty()) { theReasonWhy += "and date "; } theReasonWhy += "dependent"; // Check to see if time has expired; if not, then report why if (ts_.is_valid()) { // This can apply to single and series boost::posix_time::time_duration calendar_time = ts_.duration(c); if (calendar_time < ts_.start().duration()) { ts_.why(c, theReasonWhy); return true; } // calendar_time >= ts_.start().duration() if (ts_.hasIncrement()) { if (calendar_time < ts_.finish().duration()) { ts_.why(c, theReasonWhy); return true; } } // calendar_time >= ts_.start().duration() && calendar_time >= ts_.finish().duration() // past the end of time slot, hence this should not hold job generation, } // the time has expired theReasonWhy += " ( '"; theReasonWhy += toString(); theReasonWhy += "' has expired,"; // take into account, user can use run/force complete to miss time slots bool do_a_requeue = ts_.requeueable(c); if (do_a_requeue) { TimeSlot the_next_time_slot = ts_.compute_next_time_slot(c); if (the_next_time_slot.isNULL() || !ts_.hasIncrement()) { theReasonWhy += " *re-queue* to run at this time"; } else { theReasonWhy += " *re-queue* to run at "; theReasonWhy += the_next_time_slot.toString(); } } else { if (ts_.relative()) { theReasonWhy += " please *re-queue*, to reset the relative duration"; } else { boost::gregorian::date the_min_next_date; if (!days.empty() || !dates.empty()) { for (const auto& day : days) { auto the_next_matching_date = day.next_matching_date(c); if (the_min_next_date.is_special()) { the_min_next_date = the_next_matching_date; } if (the_next_matching_date < the_min_next_date) { the_min_next_date = the_next_matching_date; } } for (const auto& date : dates) { auto the_next_matching_date = date.next_matching_date(c); if (the_min_next_date.is_special()) { the_min_next_date = the_next_matching_date; } if (the_next_matching_date < the_min_next_date) { the_min_next_date = the_next_matching_date; } } theReasonWhy += " next run at "; } else { auto one_day = boost::gregorian::date_duration(1); the_min_next_date = c.date(); // today's date the_min_next_date += one_day; // add one day, so its in the future theReasonWhy += " next run tomorrow at "; } theReasonWhy += ts_.start().toString(); theReasonWhy += " "; theReasonWhy += to_simple_string(the_min_next_date); } } theReasonWhy += " )"; return true; } template void TimeAttr::serialize(Archive& ar) { ar(CEREAL_NVP(ts_)); // Only persisted for testing, see usage of isSetFree() CEREAL_OPTIONAL_NVP(ar, free_, [this]() { return free_; }); // conditionally save } CEREAL_TEMPLATE_SPECIALIZE(TimeAttr); } // namespace ecf ecflow-5.16.0/libs/attribute/src/ecflow/attribute/AutoArchiveAttr.hpp0000664000175000017500000000403715161437256026047 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #ifndef ecflow_attribute_AutoArchiveAttr_HPP #define ecflow_attribute_AutoArchiveAttr_HPP #include #include "ecflow/core/NState.hpp" #include "ecflow/core/TimeSlot.hpp" namespace ecf { class Calendar; } // namespace ecf namespace ecf { // Use compiler , destructor, assignment, copy constructor class AutoArchiveAttr { public: AutoArchiveAttr() = default; AutoArchiveAttr(int hour, int minute, bool relative, bool idle = false) : time_(hour, minute), relative_(relative), idle_(idle) {} AutoArchiveAttr(const TimeSlot& ts, bool relative, bool idle = false) : time_(ts), relative_(relative), idle_(idle) {} explicit AutoArchiveAttr(int days, bool idle = false) : time_(TimeSlot(days * 24, 0)), days_(true), idle_(idle) {} bool operator==(const AutoArchiveAttr& rhs) const; bool operator<(const AutoArchiveAttr& rhs) const { return time_ < rhs.time(); } bool isFree(const ecf::Calendar&, const std::pair& last_state_and_change_duration) const; std::string toString() const; const TimeSlot& time() const { return time_; } bool relative() const { return relative_; } bool days() const { return days_; } bool idle() const { return idle_; } public: void write(std::string&) const; private: TimeSlot time_; bool relative_{true}; bool days_{false}; bool idle_{false}; friend class cereal::access; template void serialize(Archive& ar, std::uint32_t const version); }; } // namespace ecf #endif /* ecflow_attribute_AutoArchiveAttr_HPP */ ecflow-5.16.0/libs/attribute/src/ecflow/attribute/NodeAttr.cpp0000664000175000017500000003406515161437256024521 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "ecflow/attribute/NodeAttr.hpp" #include #include #include "ecflow/core/Converter.hpp" #include "ecflow/core/Ecf.hpp" #include "ecflow/core/Message.hpp" #include "ecflow/core/Serialization.hpp" #include "ecflow/core/Str.hpp" using namespace ecf; const std::string& Event::SET() { static const std::string SET = "set"; return SET; } const std::string& Event::CLEAR() { static const std::string CLEAR = "clear"; return CLEAR; } const Event& Event::EMPTY() { static const Event EVENT = Event(); return EVENT; } const Meter& Meter::EMPTY() { static const Meter METER = Meter(); return METER; } const Label& Label::EMPTY() { static const Label LABEL = Label(); return LABEL; } //////////////////////////////////////////////////////////////////////////////////////////// Event::Event(int number, const std::string& eventName, bool iv, bool check_name) : n_(eventName), number_(number), v_(iv), iv_(iv) { if (!eventName.empty() && check_name) { std::string msg; if (!Str::valid_name(eventName, msg)) { throw std::runtime_error("Event::Event: Invalid event name : " + msg); } } } Event::Event(const std::string& eventName, bool iv) : n_(eventName), v_(iv), iv_(iv) { if (eventName.empty()) { throw std::runtime_error("Event::Event: Invalid event name : name must be specified if no number supplied"); } // If the eventName is an integer, then treat it as such, by setting number_ and clearing n_ // This was added after migration failed, since *python* api allowed: // ta.add_event(1); // ta.add_event("1"); // and when we called ecflow_client --migrate/--get it generated // event 1 // event 1 // which then did *not* load. // // Test for numeric, and then casting, is ****faster***** than relying on exception alone if (eventName.find_first_of(Str::NUMERIC()) == 0) { try { number_ = ecf::convert_to(eventName); n_.clear(); return; } catch (const ecf::bad_conversion&) { // cast failed, a real string, carry on } } std::string msg; if (!Str::valid_name(eventName, msg)) { throw std::runtime_error("Event::Event: Invalid event name : " + msg); } } Event Event::make_from_value(const std::string& name, const std::string& value) { // value is expected to be either "set" or "clear" if (!isValidState(value)) { throw std::runtime_error("Event::Event: Invalid state : " + value); } return Event(name, value == Event::SET()); } void Event::set_value(bool b) { v_ = b; state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_STATE_CHANGE_NO std::cout << "Event::set_value\n"; #endif } std::string Event::name_or_number() const { if (n_.empty()) { return MESSAGE(number_); } return n_; } bool Event::operator<(const Event& rhs) const { if (!n_.empty() && !rhs.name().empty()) { return n_ < rhs.name(); } if (n_.empty() && rhs.name().empty()) { return number_ < rhs.number(); } return name_or_number() < rhs.name_or_number(); } bool Event::operator==(const Event& rhs) const { if (v_ != rhs.v_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "v_ != rhs.v_ (v_:" << v_ << " rhs.v_:" << rhs.v_ << ") " << toString() << "\n"; } #endif return false; } if (number_ != rhs.number_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "number_ != rhs.number_ (number_:" << number_ << " rhs.number_:" << rhs.number_ << ") " << toString() << "\n"; } #endif return false; } if (n_ != rhs.n_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "n_ != rhs.n_ (n_:" << n_ << " rhs.n_:" << rhs.n_ << ") " << toString() << "\n"; } #endif return false; } if (iv_ != rhs.iv_) { #ifdef DEBUG if (Ecf::debug_equality()) { std::cout << "iv_ != rhs.iv_ (iv_:" << iv_ << " rhs.iv_:" << rhs.iv_ << ") " << toString() << "\n"; } #endif return false; } return true; } bool Event::compare(const Event& rhs) const { if (number_ != rhs.number_) { return false; } if (n_ != rhs.n_) { return false; } return true; } std::string Event::toString() const { std::string ret; write(ret); return ret; } void Event::write(std::string& ret) const { ret += "event "; if (number_ == std::numeric_limits::max()) { ret += n_; } else { ret += ecf::convert_to(number_); ret += " "; ret += n_; } if (iv_) { ret += " set"; // initial value } } std::string Event::dump() const { return MESSAGE(toString() << " value(" << v_ << ") used(" << used_ << ")"); } bool Event::isValidState(const std::string& state) { return state == Event::SET() || state == Event::CLEAR(); } //////////////////////////////////////////////////////////////////////////////////////////// Meter::Meter(const std::string& name, int min, int max, int colorChange, int value, bool check) : min_(min), max_(max), v_(value), cc_(colorChange), n_(name) { if (check) { if (!Str::valid_name(name)) { throw std::runtime_error("Meter::Meter: Invalid Meter name: " + name); } } if (min > max) { throw std::out_of_range("Meter::Meter: Invalid Meter(name,min,max,color_change) : min must be less than max"); } if (colorChange == std::numeric_limits::max()) { cc_ = max_; } if (value == std::numeric_limits::max()) { v_ = min_; } if (cc_ < min || cc_ > max) { throw std::out_of_range(MESSAGE("Meter::Meter: Invalid Meter(name,min,max,color_change) color_change(" << cc_ << ") must be between min(" << min_ << ") and max(" << max_ << ")")); } } Meter Meter::make_from_value(const std::string& name, const std::string& value) { // value is expected to be of the form "min,max,value", where min, max and value are integers std::vector tokens; ecf::algorithm::split(tokens, value, ","); if (tokens.size() != 3) { throw std::runtime_error( MESSAGE("Meter::make_from_value: Expect three comma-separated values, but found: '" << value << "'")); } try { auto min = ecf::convert_to(tokens[0]); auto max = ecf::convert_to(tokens[1]); auto value = ecf::convert_to(tokens[2]); return Meter(name, min, max, max, value, false); } catch (const ecf::bad_conversion&) { throw std::runtime_error(MESSAGE("Meter::make_from_value: Expect three comma-separated values, but found: (" << tokens[0] << ", " << tokens[1] << ", " << tokens[2] << ")")); } } void Meter::set_value(int v) { if (!isValidValue(v)) { throw std::runtime_error(MESSAGE("Meter::set_value(int): The meter(" << n_ << ") value must be in the range[" << min() << "->" << max() << "] but found '" << v << "'")); } v_ = v; state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_STATE_CHANGE_NO std::cout << "Meter::set_value\n"; #endif } bool Meter::operator==(const Meter& rhs) const { if (v_ != rhs.v_) { return false; } if (min_ != rhs.min_) { return false; } if (max_ != rhs.max_) { return false; } if (cc_ != rhs.cc_) { return false; } if (n_ != rhs.n_) { return false; } return true; } std::string Meter::toString() const { std::string ret; write(ret); return ret; } void Meter::write(std::string& ret) const { ret += "meter "; ret += n_; ret += " "; ret += ecf::convert_to(min_); ret += " "; ret += ecf::convert_to(max_); ret += " "; ret += ecf::convert_to(cc_); } std::string Meter::dump() const { return MESSAGE("meter " << n_ << " min(" << min_ << ") max (" << max_ << ") colorChange(" << cc_ << ") value(" << v_ << ") used(" << used_ << ")"); } ///////////////////////////////////////////////////////////////////////////////////////////// Label::Label(const std::string& name, const std::string& value, const std::string& new_value, bool check_name) : n_(name), v_(value), new_v_(new_value) { if (check_name && !Str::valid_name(n_)) { throw std::runtime_error(MESSAGE("Label::Label: Invalid Label name :" << n_)); } } std::string Label::toString() const { // parsing always STRIPS the quotes, hence add them back std::string ret; ret.reserve(n_.size() + v_.size() + 10); write(ret); return ret; } void Label::write(std::string& ret) const { // parsing always STRIPS the quotes, hence add them back ret += "label "; ret += n_; ret += " \""; if (v_.find("\n") == std::string::npos) { ret += v_; } else { // replace \n, otherwise re-parse will fail std::string value = v_; Str::replaceall(value, "\n", "\\n"); ret += value; } ret += "\""; } std::string Label::dump() const { return MESSAGE(toString() << " : \"" << new_v_ << "\""); } void Label::set_new_value(const std::string& l) { new_v_ = l; state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_STATE_CHANGE_NO std::cout << "Label::set_new_value\n"; #endif } void Label::reset() { new_v_.clear(); state_change_no_ = Ecf::incr_state_change_no(); #ifdef DEBUG_STATE_CHANGE_NO std::cout << "Label::reset()\n"; #endif } void Label::parse(const std::string& line, std::vector& lineTokens, bool parse_state) { parse(line, lineTokens, parse_state, n_, v_, new_v_); } void Label::parse(const std::string& line, std::vector& lineTokens, bool parse_state, std::string& the_name, std::string& the_value, std::string& the_new_value) { size_t line_token_size = lineTokens.size(); if (line_token_size < 3) { throw std::runtime_error("Label::parse: Invalid label :" + line); } the_name = lineTokens[1]; // parsing will always STRIP single or double quotes, print will add double quotes // label simple_label 'ecgems' if (line_token_size == 3) { Str::removeQuotes(lineTokens[2]); Str::removeSingleQuotes(lineTokens[2]); the_value = lineTokens[2]; if (the_value.find("\\n") != std::string::npos) { Str::replaceall(the_value, "\\n", "\n"); } } else { // label complex_label "smsfetch -F %ECF_FILES% -I %ECF_INCLUDE%" # fred // label simple_label "fred" # "smsfetch -F %ECF_FILES% -I %ECF_INCLUDE%" std::string value; value.reserve(line.size()); for (size_t i = 2; i < line_token_size; ++i) { if (lineTokens[i].at(0) == '#') { break; } if (i != 2) { value += " "; } value += lineTokens[i]; } Str::removeQuotes(value); Str::removeSingleQuotes(value); the_value = value; if (the_value.find("\\n") != std::string::npos) { Str::replaceall(the_value, "\\n", "\n"); } // state if (parse_state) { // label name "value" # "new value" bool comment_fnd = false; size_t first_quote_after_comment = 0; size_t last_quote_after_comment = 0; for (size_t i = line.size() - 1; i > 0; i--) { if (line[i] == '#') { comment_fnd = true; break; } if (line[i] == '"') { if (last_quote_after_comment == 0) { last_quote_after_comment = i; } first_quote_after_comment = i; } } if (comment_fnd && first_quote_after_comment != last_quote_after_comment) { std::string new_value = line.substr(first_quote_after_comment + 1, last_quote_after_comment - first_quote_after_comment - 1); // std::cout << "new label = '" << new_value << "'\n"; the_new_value = new_value; if (the_new_value.find("\\n") != std::string::npos) { Str::replaceall(the_new_value, "\\n", "\n"); } } } } } template void Label::serialize(Archive& ar) { ar(CEREAL_NVP(n_)); CEREAL_OPTIONAL_NVP(ar, v_, [this]() { return !v_.empty(); }); CEREAL_OPTIONAL_NVP(ar, new_v_, [this]() { return !new_v_.empty(); }); // conditionally save } template void Event::serialize(Archive& ar) { CEREAL_OPTIONAL_NVP(ar, n_, [this]() { return !n_.empty(); }); CEREAL_OPTIONAL_NVP(ar, number_, [this]() { return number_ != std::numeric_limits::max(); }); CEREAL_OPTIONAL_NVP(ar, v_, [this]() { return v_; }); CEREAL_OPTIONAL_NVP(ar, iv_, [this]() { return iv_; }); } template void Meter::serialize(Archive& ar) { ar(CEREAL_NVP(min_), CEREAL_NVP(max_), CEREAL_NVP(v_), CEREAL_NVP(n_), CEREAL_NVP(cc_)); } CEREAL_TEMPLATE_SPECIALIZE(Label); CEREAL_TEMPLATE_SPECIALIZE(Event); CEREAL_TEMPLATE_SPECIALIZE(Meter); ecflow-5.16.0/libs/attribute/src/ecflow/attribute/ZombieAttr.hpp0000664000175000017500000000614215161437256025061 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #ifndef ecflow_attribute_ZombieAttr_HPP #define ecflow_attribute_ZombieAttr_HPP #include #include "ecflow/core/Child.hpp" #include "ecflow/core/ZombieCtrlAction.hpp" namespace cereal { class access; } // Class ZombieAttr: // Use compiler , generated destructor, assignment, copy constructor // ZombieAttr does *not* have any changeable state class ZombieAttr { public: ZombieAttr(ecf::Child::ZombieType t, const std::vector& c, ecf::ZombieCtrlAction a, int zombie_lifetime = 0); ZombieAttr() = default; bool operator==(const ZombieAttr& rhs) const; bool empty() const { return zombie_type_ == ecf::Child::NOT_SET; } ecf::Child::ZombieType zombie_type() const { return zombie_type_; } ecf::ZombieCtrlAction action() const { return action_; } int zombie_lifetime() const { return zombie_lifetime_; } const std::vector& child_cmds() const { return child_cmds_; } std::vector::const_iterator child_begin() const { return child_cmds_.begin(); } // for python std::vector::const_iterator child_end() const { return child_cmds_.end(); } // for python std::string toString() const; bool fob(ecf::Child::CmdType) const; bool fail(ecf::Child::CmdType) const; bool adopt(ecf::Child::CmdType) const; bool block(ecf::Child::CmdType) const; bool remove(ecf::Child::CmdType) const; bool kill(ecf::Child::CmdType) const; /// Create from a string. Will throw std::runtime_error of parse errors /// expects ::child_cmds:zombie_lifetime static ZombieAttr create(const std::string& str); // Added to support return by reference static const ZombieAttr& EMPTY(); // Provide the default behaviour static ZombieAttr get_default_attr(ecf::Child::ZombieType); static int default_ecf_zombie_life_time() { return 3600; } static int default_user_zombie_life_time() { return 300; } static int default_path_zombie_life_time() { return 900; } static int minimum_zombie_life_time() { return 60; } public: void write(std::string&) const; private: std::vector child_cmds_; // init, event, meter,label, complete ecf::Child::ZombieType zombie_type_{ecf::Child::NOT_SET}; // User,path or ecf ecf::ZombieCtrlAction action_{ecf::ZombieCtrlAction::BLOCK}; // fob, fail,remove, adopt, block, kill int zombie_lifetime_{0}; // How long zombie lives in server friend class cereal::access; template void serialize(Archive& ar, std::uint32_t const version); }; #endif /* ecflow_attribute_ZombieAttr_HPP */ ecflow-5.16.0/libs/attribute/src/ecflow/attribute/NodeAttr.hpp0000664000175000017500000001623315161437256024523 0ustar alastairalastair/* * Copyright 2009- ECMWF. * * This software is licensed under the terms of the Apache Licence version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #ifndef ecflow_attribute_NodeAttr_HPP #define ecflow_attribute_NodeAttr_HPP #include // for std::numeric_limits::max() #include #include #include namespace cereal { class access; } //////////////////////////////////////////////////////////////////////////////////////// // Class Label: // Use compiler , generated destructor, assignment, copy constructor class Label : public boost::equality_comparable