mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
170 lines
5.2 KiB
C++
170 lines
5.2 KiB
C++
/*
|
|
This Source Code Form is subject to the
|
|
terms of the Mozilla Public License, v.
|
|
2.0. If a copy of the MPL was not
|
|
distributed with this file, You can
|
|
obtain one at
|
|
http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "Classes.h"
|
|
|
|
namespace plc {
|
|
|
|
using element_handle = short;
|
|
|
|
// basic logic element.
|
|
class basic_element {
|
|
public:
|
|
// types
|
|
// rtti
|
|
enum class type_e {
|
|
variable,
|
|
timer,
|
|
counter,
|
|
};
|
|
// constructors
|
|
template<typename ...Args_>
|
|
basic_element( basic_element::type_e Type = basic_element::type_e::variable, Args_ ...Args );
|
|
// methods
|
|
// data access
|
|
auto input() -> int &;
|
|
auto output() const -> int;
|
|
private:
|
|
// types
|
|
// cell content variants
|
|
struct variable {
|
|
std::int32_t value;
|
|
};
|
|
struct timer {
|
|
std::int32_t value;
|
|
short time_preset;
|
|
short time_elapsed;
|
|
};
|
|
struct counter {
|
|
std::int32_t value;
|
|
short count_limit;
|
|
short count_value;
|
|
};
|
|
// members
|
|
std::variant<variable, timer, counter> data;
|
|
|
|
static int blank;
|
|
// friends
|
|
friend class basic_controller;
|
|
};
|
|
|
|
class basic_controller {
|
|
|
|
public:
|
|
// methods
|
|
auto load( std::string const &Filename ) -> bool;
|
|
auto update( double const Timestep ) -> int;
|
|
// finds element with specified name, potentially creating new element of specified type initialized with provided arguments. returns: handle to the element
|
|
template<typename ...Args_>
|
|
auto find_or_insert( std::string const &Name, basic_element::type_e Type = basic_element::type_e::variable, Args_ ...Args ) -> element_handle;
|
|
// data access
|
|
auto input( element_handle const Element ) -> int &;
|
|
auto output( element_handle const Element ) const -> int;
|
|
|
|
private:
|
|
//types
|
|
// plc program instruction
|
|
enum class opcode_e : short {
|
|
op_nop,
|
|
op_ld,
|
|
op_ldi,
|
|
op_and,
|
|
op_ani,
|
|
op_anb,
|
|
op_or,
|
|
op_ori,
|
|
op_orb,
|
|
op_out,
|
|
op_set,
|
|
op_rst,
|
|
};
|
|
struct operation {
|
|
opcode_e code;
|
|
short element;
|
|
short parameter1;
|
|
short parameter2;
|
|
};
|
|
// containers
|
|
using element_sequence = std::vector<basic_element>;
|
|
using name_sequence = std::vector<std::string>;
|
|
using operation_sequence = std::vector<operation>;
|
|
using handle_sequence = std::vector<element_handle>;
|
|
// methods
|
|
auto deserialize_operation( cParser &Input ) -> bool;
|
|
// adds provided item to the collection. returns: true if there's no duplicate with the same name, false otherwise
|
|
auto insert( std::string const Name, basic_element Element ) -> element_handle;
|
|
// runs one cycle of current program. returns: error code or 0 if there's no error
|
|
auto run() -> int;
|
|
void log_error( std::string const &Error, int const Line = -1 ) const;
|
|
auto guess_element_type_from_name( std::string const &Name ) const->basic_element::type_e;
|
|
inline
|
|
auto inverse( int const Value ) const -> int {
|
|
return ( Value == 0 ? 1 : 0 ); }
|
|
// element access
|
|
inline
|
|
auto element( element_handle const Element ) const -> basic_element const {
|
|
return m_elements[ Element - 1 ]; }
|
|
inline
|
|
auto element( element_handle const Element ) -> basic_element & {
|
|
return m_elements[ Element - 1 ]; }
|
|
// members
|
|
static std::map<std::string, basic_controller::opcode_e> const m_operationcodemap;
|
|
element_sequence m_elements; // collection of elements accessed by the plc program
|
|
name_sequence m_elementnames;
|
|
handle_sequence m_timerhandles; // indices of timer elements, timer update optimization helper
|
|
std::string m_programfilename; // cached filename of currently loaded program
|
|
operation_sequence m_program; // current program for the plc
|
|
std::vector<int> m_accumulator; // state accumulator for currently processed program rung
|
|
bool m_popstack { false }; // whether ld(i) operation should pop the accumulator stack or just add onto it
|
|
double m_updateaccumulator { 0.0 }; //
|
|
double m_updaterate { 0.1 };
|
|
};
|
|
|
|
template<typename ...Args_>
|
|
basic_element::basic_element( basic_element::type_e Type, Args_ ...Args )
|
|
{
|
|
switch( Type ) {
|
|
case type_e::variable: {
|
|
data = variable{ Args ... };
|
|
break;
|
|
}
|
|
case type_e::timer: {
|
|
data = timer{ Args ... };
|
|
break;
|
|
}
|
|
case type_e::counter: {
|
|
data = counter{ Args ... };
|
|
break;
|
|
}
|
|
default: {
|
|
// TBD: log error if we get here?
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename ...Args_>
|
|
auto basic_controller::find_or_insert( std::string const &Name, basic_element::type_e Type, Args_ ...Args ) -> element_handle {
|
|
// NOTE: because we expect all lookups to be performed only (once) during controller (code) initialization
|
|
// we're using simple linear container for names, to allow for easy access to both elements and their names with the same handle
|
|
auto index { 1 };
|
|
for( auto const &name : m_elementnames ) {
|
|
if( name == Name ) {
|
|
return index;
|
|
}
|
|
++index;
|
|
}
|
|
// create and insert a new element if we didn't find existing one
|
|
return insert( Name, basic_element( Type, Args ... ) );
|
|
}
|
|
|
|
} // plc
|