<commit_before>
#include <iostream>
#include <ini.h>
#include <stdexcept>
#include <wiringPi.h>

#include <asiodnp3/DNP3Manager.h>
#include <asiodnp3/UpdateBuilder.h>
#include <asiodnp3/PrintingChannelListener.h>
#include <asiopal/UTCTimeSource.h>
#include <asiodnp3/ConsoleLogger.h>
#include <opendnp3/LogLevels.h>

#include <thread>

#include "Config.h"
#include "GPIOCommandHandler.h"

// prototypes for handling ini file reading
int cfg_handler(void* user, const char* section, const char* name, const char* value);
bool safe_handler(Config& config, const std::string& section, const std::string& name, const std::string& value);

using namespace opendnp3;
using namespace asiopal;
using namespace asiodnp3;

int main(int argc, char *argv[])
{
	if(argc != 2)
	{
		std::cerr << "usage: rpi-dnp3-gpio <ini file path>" << std::endl;
		return -1;
	}

	auto filename = argv[1];

	Config config;
	if(ini_parse(filename, cfg_handler, &config) != 0)
	{
		std::cerr << "error parsing ini file: " << filename << std::endl;
		return -1;
	}

	// Initialize wiringPi -- using Broadcom pin numbers
	// this call automatically terminates program with an error msg if the setup cannot be completed
	wiringPiSetupGpio();

	// setup inputs and outputs
	for(auto pin : config.inputs) {
		pinMode(pin, INPUT);
		std::cout << "pin " << static_cast<int>(pin) << " set as INPUT" << std::endl;
	}

	for(auto pin : config.outputs) {
		pinMode(pin, OUTPUT);
		std::cout << "pin " << static_cast<int>(pin) << " set as OUTPUT" << std::endl;
	}

	const auto commandHandler = std::make_shared<GPIOCommandHandler>(config.outputs);

	const auto LOG_LEVELS = levels::NORMAL | levels::ALL_APP_COMMS;

	DNP3Manager manager(1, ConsoleLogger::Create());

	auto channel = manager.AddTCPServer("server", LOG_LEVELS, ChannelRetry::Default(), "0.0.0.0", config.port, PrintingChannelListener::Create());

	OutstationStackConfig stack(DatabaseSizes::BinaryOnly(config.inputs.size()));
	stack.link = config.link;

	stack.outstation.eventBufferConfig = EventBufferConfig(50);
	stack.outstation.params.allowUnsolicited = true;

	auto outstation = channel->AddOutstation("outstation", commandHandler, DefaultOutstationApplication::Create(), stack);

	outstation->Enable();

	std::cout << "Sample period is: " << config.sample_period_ms << std::endl;
        const auto SAMPLE_PERIOD = std::chrono::milliseconds(config.sample_period_ms);



	while(true) {

		uint16_t index = 0;
		DNPTime time(asiopal::UTCTimeSource::Instance().Now().msSinceEpoch);

		UpdateBuilder builder;
		for(auto pin : config.inputs) {
			bool value = digitalRead(pin);
			builder.Update(Binary(value, 0x01, time), index);
			++index;
		}
		outstation->Apply(builder.Build());

		

		// determines the sampling rate
		std::this_thread::sleep_for(SAMPLE_PERIOD);
	}
}

bool safe_handler(Config& config, const std::string& section, const std::string& name, const std::string& value)
{
	try
	{		
		if(section == "input")
		{
			return config.AddInput(std::stoi(name));
		}
		else if(section == "output")
		{
			return config.AddOutput(std::stoi(name));
		}
		else if(section == "program")
		{
			if(name == "sample-period-ms")
			{
				config.sample_period_ms = std::stoi(value);
				return true;
			}
		}<commit_msg>read and update binary output status values<commit_after>
#include <iostream>
#include <ini.h>
#include <stdexcept>
#include <wiringPi.h>

#include <asiodnp3/DNP3Manager.h>
#include <asiodnp3/UpdateBuilder.h>
#include <asiodnp3/PrintingChannelListener.h>
#include <asiopal/UTCTimeSource.h>
#include <asiodnp3/ConsoleLogger.h>
#include <opendnp3/LogLevels.h>

#include <thread>

#include "Config.h"
#include "GPIOCommandHandler.h"

// prototypes for handling ini file reading
int cfg_handler(void* user, const char* section, const char* name, const char* value);
bool safe_handler(Config& config, const std::string& section, const std::string& name, const std::string& value);

using namespace opendnp3;
using namespace asiopal;
using namespace asiodnp3;

int main(int argc, char *argv[])
{
	if(argc != 2)
	{
		std::cerr << "usage: rpi-dnp3-gpio <ini file path>" << std::endl;
		return -1;
	}

	auto filename = argv[1];

	Config config;
	if(ini_parse(filename, cfg_handler, &config) != 0)
	{
		std::cerr << "error parsing ini file: " << filename << std::endl;
		return -1;
	}

	// Initialize wiringPi -- using Broadcom pin numbers
	// this call automatically terminates program with an error msg if the setup cannot be completed
	wiringPiSetupGpio();

	// setup inputs and outputs
	for(auto pin : config.inputs) {
		pinMode(pin, INPUT);
		std::cout << "pin " << static_cast<int>(pin) << " set as INPUT" << std::endl;
	}

	for(auto pin : config.outputs) {
		pinMode(pin, OUTPUT);
		std::cout << "pin " << static_cast<int>(pin) << " set as OUTPUT" << std::endl;
	}

	const auto commandHandler = std::make_shared<GPIOCommandHandler>(config.outputs);

	const auto LOG_LEVELS = levels::NORMAL | levels::ALL_APP_COMMS;

	DNP3Manager manager(1, ConsoleLogger::Create());

	auto channel = manager.AddTCPServer("server", LOG_LEVELS, ChannelRetry::Default(), "0.0.0.0", config.port, PrintingChannelListener::Create());

	OutstationStackConfig stack(
		DatabaseSizes(
			config.inputs.size(), // binary
			0, 0, 0, 0,
			config.inputs.size(), // binary output status
			0, 0
		)
	);
	stack.link = config.link;

	stack.outstation.eventBufferConfig = EventBufferConfig(50);
	stack.outstation.params.allowUnsolicited = true;

	auto outstation = channel->AddOutstation("outstation", commandHandler, DefaultOutstationApplication::Create(), stack);

	outstation->Enable();

	std::cout << "Sample period is: " << config.sample_period_ms << std::endl;
        const auto SAMPLE_PERIOD = std::chrono::milliseconds(config.sample_period_ms);



	while(true) {

		DNPTime time(asiopal::UTCTimeSource::Instance().Now().msSinceEpoch);

		UpdateBuilder builder;

		uint16_t index = 0;
		for(auto pin : config.inputs) {
			bool value = digitalRead(pin);
			builder.Update(Binary(value, 0x01, time), index);
			++index;
		}

		index = 0;
		for(auto pin : config.outputs) {
			bool value = digitalRead(pin);
			builder.Update(BinaryOutputStatus(value, 0x01, time), index);
			++index;
		}

		outstation->Apply(builder.Build());

		
		// determines the sampling rate
		std::this_thread::sleep_for(SAMPLE_PERIOD);
	}
}

bool safe_handler(Config& config, const std::string& section, const std::string& name, const std::string& value)
{
	try
	{		
		if(section == "input")
		{
			return config.AddInput(std::stoi(name));
		}
		else if(section == "output")
		{
			return config.AddOutput(std::stoi(name));
		}
		else if(section == "program")
		{
			if(name == "sample-period-ms")
			{
				config.sample_period_ms = std::stoi(value);
				return true;
			}
		}
{'fraction_non_alphanumeric': 0.14735441915459652, 'fraction_numerical': 0.00975465563109666, 'mean_word_length': 4.7936643835616435, 'pattern_counts': {'":': 0, '<': 97, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5059531', 'n_tokens_mistral': 2463, 'n_tokens_neox': 2269, 'n_words': 536}