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

#include "scriptEngineWorker.h"

#include <QtCore/QFile>
#include <QtCore/QVector>
#include <QtCore/QTextStream>
#include <QtCore/QSignalMapper>
#include <QtCore/QMetaMethod>

#include <trikKernel/fileUtils.h>
#include <trikKernel/paths.h>
#include <trikKernel/timeVal.h>
#include <trikControl/batteryInterface.h>
#include <trikControl/colorSensorInterface.h>
#include <trikControl/displayInterface.h>
#include <trikControl/encoderInterface.h>
#include <trikControl/eventCodeInterface.h>
#include <trikControl/eventDeviceInterface.h>
#include <trikControl/eventInterface.h>
#include <trikControl/gamepadInterface.h>
#include <trikControl/gyroSensorInterface.h>
#include <trikControl/lineSensorInterface.h>
#include <trikControl/motorInterface.h>
#include <trikControl/objectSensorInterface.h>
#include <trikControl/soundSensorInterface.h>
#include <trikControl/sensorInterface.h>
#include <trikControl/vectorSensorInterface.h>
#include <trikNetwork/mailboxInterface.h>

#include "scriptable.h"
#include "utils.h"

#include <QsLog.h>

using namespace trikScriptRunner;
using namespace trikControl;
using namespace trikNetwork;

Q_DECLARE_METATYPE(QVector<uint8_t>)
Q_DECLARE_METATYPE(QVector<int>)
Q_DECLARE_METATYPE(trikKernel::TimeVal)
Q_DECLARE_METATYPE(QTimer*)

#define DECLARE_METATYPE_TEMPLATE(TYPE) \
	Q_DECLARE_METATYPE(TYPE*)

#define REGISTER_METATYPE_FOR_ENGINE(TYPE) \
	Scriptable<TYPE>::registerMetatype(engine);

#define REGISTER_METATYPE(TYPE) \
	qRegisterMetaType<TYPE*>(TYPE::staticMetaObject.className());

/// Here we define a convenient template that registers all devices used in trik.
/// When creating a new device(interface), you should append it to this list.
/// So it lets you write the device just one time rather than append appropriate line to each place
/// that uses devices.
/// ATTENTION: do not forget to append newly created device to this list!
#define REGISTER_DEVICES_WITH_TEMPLATE(TEMPLATE) \
	TEMPLATE(BatteryInterface) \
	TEMPLATE(ColorSensorInterface) \
	TEMPLATE(FifoInterface) \
	TEMPLATE(DisplayInterface) \
	TEMPLATE(EncoderInterface) \
	TEMPLATE(EventCodeInterface) \
	TEMPLATE(EventDeviceInterface) \
	TEMPLATE(EventInterface) \
	TEMPLATE(GamepadInterface) \
	TEMPLATE(GyroSensorInterface) \
	TEMPLATE(KeysInterface) \
	TEMPLATE(LedInterface) \
	TEMPLATE(LineSensorInterface) \
	TEMPLATE(MailboxInterface) \
	TEMPLATE(MarkerInterface) \
	TEMPLATE(MotorInterface) \
	TEMPLATE(ObjectSensorInterface) \
	TEMPLATE(SoundSensorInterface) \
	TEMPLATE(SensorInterface) \
	TEMPLATE(Threading) \
	TEMPLATE(VectorSensorInterface)

REGISTER_DEVICES_WITH_TEMPLATE(DECLARE_METATYPE_TEMPLATE)

QScriptValue print(QScriptContext *context, QScriptEngine *engine)
{
	QString result;
	for (int i = 0; i < context->argumentCount(); ++i) {
		if (i > 0) {
			result.append(" ");
		}

		result.append(context->argument(i).toString());
	}

	QTextStream(stdout) << result << "\n";
	auto scriptValue = engine->globalObject().property("script");
	auto script = dynamic_cast<ScriptExecutionControl*> (scriptValue.toQObject());
	if (script) {
		QMetaObject::invokeMethod(script, "sendMessage", Q_ARG(QString, QString("print: %1").arg(result)));
//		If this does not work then it can be workarrounded:
//		QSignalMapper *mapper = new QSignalMapper();
//		QObject::connect(mapper, SIGNAL(mapped(QString)), script, SIGNAL(sendMessage(QString)), Qt::DirectConnection);
//		mapper->setMapping(nullptr, QString("print: %1").arg(result));
//		mapper->map(nullptr);
//		delete mapper;
	}

	return engine->toScriptValue(result);
}

QScriptValue timeInterval(QScriptContext *context, QScriptEngine *engine)
{
	int result = trikKernel::TimeVal::timeInterval(context->argument(0).toInteger(), context->argument(1).toInteger());
	return engine->toScriptValue(result);
}

QScriptValue getPhoto(QScriptContext *context,	QScriptEngine *engine)
{
	const QScriptValue & brickValue = engine->globalObject().property("brick");
	QObject *qObjBrick = brickValue.toQObject();
	if (qObjBrick)
	{
		BrickInterface *brick = qobject_cast<BrickInterface*>(qObjBrick);
		if (brick)
		{
			auto port = context->argumentCount() > 0 ? context->argument(0).toString()
				: QString("/dev/video0");
			QLOG_INFO() << "Calling getStillImage()";
			auto data = brick->getStillImage();
			QList<int> result;
			result.reserve(data.size());
			std::copy(data.begin(), data.end(), std::back_inserter(result));
			QLOG_INFO() << "Constructed result of getStillImage()";
			auto val = engine->toScriptValue(result);
			QLOG_INFO() << "Result of getStillImage() converted to JS value";
			return val;
		}
		else
		{
			QLOG_ERROR() << "script getPhoto failed at downcasting qObject to Brick";<commit_msg>getPhoto converts to 160x120 xRGB32<commit_after>/* Copyright 2013 - 2016 <NAME>, CyberTech Labs Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. */

#include "scriptEngineWorker.h"

#include <QtCore/QFile>
#include <QtCore/QVector>
#include <QtCore/QTextStream>
#include <QtCore/QSignalMapper>
#include <QtCore/QMetaMethod>

#include <trikKernel/fileUtils.h>
#include <trikKernel/paths.h>
#include <trikKernel/timeVal.h>
#include <trikControl/batteryInterface.h>
#include <trikControl/colorSensorInterface.h>
#include <trikControl/displayInterface.h>
#include <trikControl/encoderInterface.h>
#include <trikControl/eventCodeInterface.h>
#include <trikControl/eventDeviceInterface.h>
#include <trikControl/eventInterface.h>
#include <trikControl/gamepadInterface.h>
#include <trikControl/gyroSensorInterface.h>
#include <trikControl/lineSensorInterface.h>
#include <trikControl/motorInterface.h>
#include <trikControl/objectSensorInterface.h>
#include <trikControl/soundSensorInterface.h>
#include <trikControl/sensorInterface.h>
#include <trikControl/vectorSensorInterface.h>
#include <trikNetwork/mailboxInterface.h>

#include "scriptable.h"
#include "utils.h"

#include <QsLog.h>

using namespace trikScriptRunner;
using namespace trikControl;
using namespace trikNetwork;

Q_DECLARE_METATYPE(QVector<uint8_t>)
Q_DECLARE_METATYPE(QVector<int>)
Q_DECLARE_METATYPE(trikKernel::TimeVal)
Q_DECLARE_METATYPE(QTimer*)

#define DECLARE_METATYPE_TEMPLATE(TYPE) \
	Q_DECLARE_METATYPE(TYPE*)

#define REGISTER_METATYPE_FOR_ENGINE(TYPE) \
	Scriptable<TYPE>::registerMetatype(engine);

#define REGISTER_METATYPE(TYPE) \
	qRegisterMetaType<TYPE*>(TYPE::staticMetaObject.className());

/// Here we define a convenient template that registers all devices used in trik.
/// When creating a new device(interface), you should append it to this list.
/// So it lets you write the device just one time rather than append appropriate line to each place
/// that uses devices.
/// ATTENTION: do not forget to append newly created device to this list!
#define REGISTER_DEVICES_WITH_TEMPLATE(TEMPLATE) \
	TEMPLATE(BatteryInterface) \
	TEMPLATE(ColorSensorInterface) \
	TEMPLATE(FifoInterface) \
	TEMPLATE(DisplayInterface) \
	TEMPLATE(EncoderInterface) \
	TEMPLATE(EventCodeInterface) \
	TEMPLATE(EventDeviceInterface) \
	TEMPLATE(EventInterface) \
	TEMPLATE(GamepadInterface) \
	TEMPLATE(GyroSensorInterface) \
	TEMPLATE(KeysInterface) \
	TEMPLATE(LedInterface) \
	TEMPLATE(LineSensorInterface) \
	TEMPLATE(MailboxInterface) \
	TEMPLATE(MarkerInterface) \
	TEMPLATE(MotorInterface) \
	TEMPLATE(ObjectSensorInterface) \
	TEMPLATE(SoundSensorInterface) \
	TEMPLATE(SensorInterface) \
	TEMPLATE(Threading) \
	TEMPLATE(VectorSensorInterface)

REGISTER_DEVICES_WITH_TEMPLATE(DECLARE_METATYPE_TEMPLATE)

QScriptValue print(QScriptContext *context, QScriptEngine *engine)
{
	QString result;
	for (int i = 0; i < context->argumentCount(); ++i) {
		if (i > 0) {
			result.append(" ");
		}

		result.append(context->argument(i).toString());
	}

	QTextStream(stdout) << result << "\n";
	auto scriptValue = engine->globalObject().property("script");
	auto script = dynamic_cast<ScriptExecutionControl*> (scriptValue.toQObject());
	if (script) {
		QMetaObject::invokeMethod(script, "sendMessage", Q_ARG(QString, QString("print: %1").arg(result)));
//		If this does not work then it can be workarrounded:
//		QSignalMapper *mapper = new QSignalMapper();
//		QObject::connect(mapper, SIGNAL(mapped(QString)), script, SIGNAL(sendMessage(QString)), Qt::DirectConnection);
//		mapper->setMapping(nullptr, QString("print: %1").arg(result));
//		mapper->map(nullptr);
//		delete mapper;
	}

	return engine->toScriptValue(result);
}

QScriptValue timeInterval(QScriptContext *context, QScriptEngine *engine)
{
	int result = trikKernel::TimeVal::timeInterval(context->argument(0).toInteger(), context->argument(1).toInteger());
	return engine->toScriptValue(result);
}

QScriptValue getPhoto(QScriptContext *context,	QScriptEngine *engine)
{
	const QScriptValue & brickValue = engine->globalObject().property("brick");
	QObject *qObjBrick = brickValue.toQObject();
	if (qObjBrick)
	{
		BrickInterface *brick = qobject_cast<BrickInterface*>(qObjBrick);
		if (brick)
		{
			auto port = context->argumentCount() > 0 ? context->argument(0).toString()
				: QString("/dev/video0");
			QLOG_INFO() << "Calling getStillImage()";
			auto data = brick->getStillImage();
			QList<int32_t> result;
			result.reserve(data.size() / 3); //Repack RGB88 from 3 x uint8_t into int32_t
			constexpr auto IMAGE_WIDTH = 320;
			constexpr auto IMAGE_HEIGHT = 240;
			for(int row = 0; row < IMAGE_HEIGHT; row += 2) {
				for(int col = 0; col < IMAGE_WIDTH; col+=2) {
					auto row1 = &data[(row*IMAGE_WIDTH+col)*3];
					auto row2 = row1 + IMAGE_WIDTH*3;
					auto r1 = row1[0];
					auto g1 = row1[1];
					auto b1 = row1[2];
					auto r2 = row1[3];
					auto g2 = row1[4];
					auto b2 = row1[5];
					auto r3 = row2[0];
					auto g3 = row2[1];
					auto b3 = row2[2];
					auto r4 = row2[3];
					auto g4 = row2[4];
					auto b4 = row2[5];
					result.push_back(((r1 + r2 + r3 + r4) << 14)
						| ((g1 + g2 + g3 + g4) << 6)
						| (( b1 + b2 +b3 +b4)>> 2));
				}
			}

			QLOG_INFO() << "Constructed result of getStillImage()";
			auto val = engine->toScriptValue(result);
			QLOG_INFO() << "Result of getStillImage() converted to JS value";
			return val;
		}
		else
		{
			QLOG_ERROR() << "script getPhoto failed at downcasting qObject to Brick";
{'fraction_non_alphanumeric': 0.12504418522446095, 'fraction_numerical': 0.011311417462000707, 'mean_word_length': 5.430113636363636, 'pattern_counts': {'":': 0, '<': 101, '<?xml version=': 0, '>': 103, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, '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': '7476882', 'n_tokens_mistral': 3974, 'n_tokens_neox': 3728, 'n_words': 858}