Commit 0590385c authored by Giacomo Lavermicocca's avatar Giacomo Lavermicocca

Fix bugs - add Array data pattern comunication

parent b4a4c919
......@@ -12,6 +12,7 @@
#include <math.h>
#include <unistd.h>
#include <cstdio>
#include <iostream>
namespace demo {
......@@ -53,9 +54,8 @@ void displayImage(__u16 bmp[], int res, int daddress, int file)
block[i] = (bmp[i] & 0xfe) >> 1 |
(bmp[i] & 0x01) << 7;
}
i2c_smbus_write_i2c_block_data(file, daddress, 16,
(__u8 *)block);
usleep(100000);
i2c_smbus_write_i2c_block_data(file, daddress, 16, (__u8 *)block);
//usleep(100000);
}
void INThandler(int sig)
......@@ -182,42 +182,11 @@ int SetFile(int _i2cbus, int _address) //Setta le variabili iniziali...
return 0;
}
void Write(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (args.Length() < 4) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments")));
return;
}
if (!args[0]->IsNumber() || !args[1]->IsNumber() || !args[2]->IsNumber() || !args[3]->IsNumber()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong type of arguments")));
return;
}
int ROW = 0;
int VALUE = 0;
// get the param
ROW = args[0]->ToInteger()->Value();
VALUE = args[1]->ToInteger()->Value();
int _i2cbus = args[2]->ToInteger()->Value();
int _address = args[3]->ToInteger()->Value();
SetFile(_i2cbus, _address); //Devo settare /dev/i2c... tutte le volte
i2c_smbus_write_byte_data(file, ROW, VALUE);
close(file);
args.GetReturnValue().Set(String::NewFromUtf8(isolate, "end of job"));
}
void WriteAllFrames(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (args.Length() < 4) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong number arguments")));
if (!args[0]->IsArray()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Array - Wrong type of arguments")));
return;
}
......@@ -233,58 +202,28 @@ void WriteAllFrames(const FunctionCallbackInfo<Value>& args) {
SetFile(_i2cbus, _address); //Devo settare /dev/i2c... tutte le volte
Local<Array> FirstArray = Local<Array>::Cast(args[0]);
Local<Array> SecondArray;
// ... init obj from arguments or wherever ...
//// ... init obj from arguments or wherever ...
int length = FirstArray->Length();
int VALUE = 0;
//for (int i = 0; i < length; i++)
//{
// // do something with element
// SecondArray = Local<Array>::Cast(FirstArray->Get(Number::New(i)));
// for (int n = 0; n < 16; n++) {
// VALUE = SecondArray->Get(Number::New(n))->ToInteger()->Value();
// i2c_smbus_write_byte_data(file, n, VALUE);
// }
// usleep(50000 * velocity);
//}
close(file);
args.GetReturnValue().Set(String::NewFromUtf8(isolate, "end of 'WRITE ALL FRAMES' job"));
}
void WriteSleep(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (args.Length() < 5) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong number arguments")));
return;
}
for (int n = 0; n < length; n++) {
VALUE = FirstArray->Get(n)->ToInteger()->Value();
int i = n % 16;
i2c_smbus_write_byte_data(file, i, VALUE);
if (!args[0]->IsNumber() || !args[1]->IsNumber() || !args[2]->IsNumber() || !args[3]->IsNumber() || !args[4]->IsNumber()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong type of arguments")));
return;
if (i == 15)
{
usleep(1000 * velocity);
}
//std::cout << VALUE << " " << i;
//std::cout << std::endl;
}
int ROW = 0;
int VALUE = 0;
// get the param
ROW = args[0]->ToInteger()->Value();
VALUE = args[1]->ToInteger()->Value();
int _i2cbus = args[2]->ToInteger()->Value();
int _address = args[3]->ToInteger()->Value();
int delay = args[4]->ToInteger()->Value();
SetFile(_i2cbus, _address); //Devo settare /dev/i2c... tutte le volte
i2c_smbus_write_byte_data(file, ROW, VALUE);
sleep(delay);
close(file);
args.GetReturnValue().Set(String::NewFromUtf8(isolate, "end of job"));
args.GetReturnValue().Set(String::NewFromUtf8(isolate, "end of 'WRITE ALL FRAMES' job"));
}
void Begin(const FunctionCallbackInfo<Value>& args) {
......@@ -311,44 +250,13 @@ void Begin(const FunctionCallbackInfo<Value>& args) {
void Exit(const FunctionCallbackInfo<Value>& args) {
//LOOP = false;
Isolate* isolate = args.GetIsolate();
Exit();
args.GetReturnValue().Set(String::NewFromUtf8(isolate, "Exit"));
}
// This is the implementation of the "add" method
// Input arguments are passed using the
// const FunctionCallbackInfo<Value>& args struct
void Add(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
// Check the number of arguments passed.
if (args.Length() < 2) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Wrong number of arguments")));
return;
}
// Check the argument types
if (!args[0]->IsNumber() || !args[1]->IsNumber()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Wrong arguments")));
return;
}
// Perform the operation
double value = args[0]->NumberValue() + args[1]->NumberValue();
Local<Number> num = Number::New(isolate, value);
// Set the return value (using the passed in
// FunctionCallbackInfo<Value>&)
args.GetReturnValue().Set(num);
}
void Init(Local<Object> exports) {
NODE_SET_METHOD(exports, "add", Add);
NODE_SET_METHOD(exports, "Write", Write);
NODE_SET_METHOD(exports, "WriteAllFrames", WriteAllFrames);
NODE_SET_METHOD(exports, "WriteSleep", WriteSleep);
NODE_SET_METHOD(exports, "Begin", Begin);
NODE_SET_METHOD(exports, "Exit", Exit);
}
......
......@@ -308,8 +308,8 @@ ifeq ($(strip $(foreach prefix,$(NO_LOAD),\
endif
quiet_cmd_regen_makefile = ACTION Regenerating $@
cmd_regen_makefile = cd $(srcdir); /usr/lib/node_modules/npm/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "--toplevel-dir=." -I/root/Matrix8x8/AddOn1/build/config.gypi -I/usr/lib/node_modules/npm/node_modules/node-gyp/addon.gypi -I/root/.node-gyp/8.5.0/include/node/common.gypi "--depth=." "-Goutput_dir=." "--generator-output=build" "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/root/.node-gyp/8.5.0" "-Dnode_gyp_dir=/usr/lib/node_modules/npm/node_modules/node-gyp" "-Dnode_lib_file=/root/.node-gyp/8.5.0/<(target_arch)/node.lib" "-Dmodule_root_dir=/root/Matrix8x8/AddOn1" "-Dnode_engine=v8" binding.gyp
Makefile: $(srcdir)/../../.node-gyp/8.5.0/include/node/common.gypi $(srcdir)/build/config.gypi $(srcdir)/binding.gyp $(srcdir)/../../../usr/lib/node_modules/npm/node_modules/node-gyp/addon.gypi
cmd_regen_makefile = cd $(srcdir); /usr/lib/node_modules/npm/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "--toplevel-dir=." -I/root/matrix8x8_bicolor/AddOn/build/config.gypi -I/usr/lib/node_modules/npm/node_modules/node-gyp/addon.gypi -I/root/.node-gyp/8.6.0/include/node/common.gypi "--depth=." "-Goutput_dir=." "--generator-output=build" "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/root/.node-gyp/8.6.0" "-Dnode_gyp_dir=/usr/lib/node_modules/npm/node_modules/node-gyp" "-Dnode_lib_file=/root/.node-gyp/8.6.0/<(target_arch)/node.lib" "-Dmodule_root_dir=/root/matrix8x8_bicolor/AddOn" "-Dnode_engine=v8" binding.gyp
Makefile: $(srcdir)/../../.node-gyp/8.6.0/include/node/common.gypi $(srcdir)/build/config.gypi $(srcdir)/binding.gyp $(srcdir)/../../../usr/lib/node_modules/npm/node_modules/node-gyp/addon.gypi
$(call do_cmd,regen_makefile)
# "all" is a concatenation of the "all" targets from all the included
......
cmd_Release/obj.target/addon/addon.o := g++ '-DNODE_GYP_MODULE_NAME=addon' '-DUSING_UV_SHARED=1' '-DUSING_V8_SHARED=1' '-DV8_DEPRECATION_WARNINGS=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DBUILDING_NODE_EXTENSION' -I/root/.node-gyp/8.5.0/include/node -I/root/.node-gyp/8.5.0/src -I/root/.node-gyp/8.5.0/deps/uv/include -I/root/.node-gyp/8.5.0/deps/v8/include -fPIC -pthread -Wall -Wextra -Wno-unused-parameter -O3 -fno-omit-frame-pointer -fno-rtti -fno-exceptions -std=gnu++0x -MMD -MF ./Release/.deps/Release/obj.target/addon/addon.o.d.raw -c -o Release/obj.target/addon/addon.o ../addon.cc
cmd_Release/obj.target/addon/addon.o := g++ '-DNODE_GYP_MODULE_NAME=addon' '-DUSING_UV_SHARED=1' '-DUSING_V8_SHARED=1' '-DV8_DEPRECATION_WARNINGS=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DBUILDING_NODE_EXTENSION' -I/root/.node-gyp/8.6.0/include/node -I/root/.node-gyp/8.6.0/src -I/root/.node-gyp/8.6.0/deps/uv/include -I/root/.node-gyp/8.6.0/deps/v8/include -fPIC -pthread -Wall -Wextra -Wno-unused-parameter -O3 -fno-omit-frame-pointer -fno-rtti -fno-exceptions -std=gnu++0x -MMD -MF ./Release/.deps/Release/obj.target/addon/addon.o.d.raw -c -o Release/obj.target/addon/addon.o ../addon.cc
Release/obj.target/addon/addon.o: ../addon.cc \
/root/.node-gyp/8.5.0/include/node/node.h \
/root/.node-gyp/8.5.0/include/node/v8.h \
/root/.node-gyp/8.5.0/include/node/v8-version.h \
/root/.node-gyp/8.5.0/include/node/v8config.h \
/root/.node-gyp/8.5.0/include/node/node_version.h ../i2c-dev.h \
/root/.node-gyp/8.6.0/include/node/node.h \
/root/.node-gyp/8.6.0/include/node/v8.h \
/root/.node-gyp/8.6.0/include/node/v8-version.h \
/root/.node-gyp/8.6.0/include/node/v8config.h \
/root/.node-gyp/8.6.0/include/node/node_version.h ../i2c-dev.h \
../8x8font.h
../addon.cc:
/root/.node-gyp/8.5.0/include/node/node.h:
/root/.node-gyp/8.5.0/include/node/v8.h:
/root/.node-gyp/8.5.0/include/node/v8-version.h:
/root/.node-gyp/8.5.0/include/node/v8config.h:
/root/.node-gyp/8.5.0/include/node/node_version.h:
/root/.node-gyp/8.6.0/include/node/node.h:
/root/.node-gyp/8.6.0/include/node/v8.h:
/root/.node-gyp/8.6.0/include/node/v8-version.h:
/root/.node-gyp/8.6.0/include/node/v8config.h:
/root/.node-gyp/8.6.0/include/node/node_version.h:
../i2c-dev.h:
../8x8font.h:
No preview for this file type
No preview for this file type
......@@ -34,10 +34,10 @@ CFLAGS_CC_Debug := \
-std=gnu++0x
INCS_Debug := \
-I/root/.node-gyp/8.5.0/include/node \
-I/root/.node-gyp/8.5.0/src \
-I/root/.node-gyp/8.5.0/deps/uv/include \
-I/root/.node-gyp/8.5.0/deps/v8/include
-I/root/.node-gyp/8.6.0/include/node \
-I/root/.node-gyp/8.6.0/src \
-I/root/.node-gyp/8.6.0/deps/uv/include \
-I/root/.node-gyp/8.6.0/deps/v8/include
DEFS_Release := \
'-DNODE_GYP_MODULE_NAME=addon' \
......@@ -68,10 +68,10 @@ CFLAGS_CC_Release := \
-std=gnu++0x
INCS_Release := \
-I/root/.node-gyp/8.5.0/include/node \
-I/root/.node-gyp/8.5.0/src \
-I/root/.node-gyp/8.5.0/deps/uv/include \
-I/root/.node-gyp/8.5.0/deps/v8/include
-I/root/.node-gyp/8.6.0/include/node \
-I/root/.node-gyp/8.6.0/src \
-I/root/.node-gyp/8.6.0/deps/uv/include \
-I/root/.node-gyp/8.6.0/deps/v8/include
OBJS := \
$(obj).target/$(TARGET)/addon.o
......
......@@ -60,8 +60,7 @@
"v8_trace_maps": 0,
"v8_use_snapshot": "false",
"want_separate_host_toolset": 0,
"want_separate_host_toolset_mkpeephole": 0,
"nodedir": "/root/.node-gyp/8.5.0",
"nodedir": "/root/.node-gyp/8.6.0",
"standalone_static_library": 1
}
}
......@@ -22,65 +22,6 @@ var currentframeIndex;
var _i2cbus = 1;
var _address = 112;
//// Define a message handler
//io.sockets.on('connection', function (socket) {
// //spedito il disegno della matrice al client appena connesso.
// socket.emit('drawAllFrames', matrixFrames); //0 is FRAME POSITION
// console.log("I'm a new client");
// socket.on('addNewFrame', function () {
// matrixFrames.push([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
// socket.broadcast.emit('addNewFrame', matrixFrames);
// socket.emit('addNewFrame', matrixFrames);
// console.log("------------ added new Frame ------------");
// });
// socket.on('loadFrames', function (fileName) {
// fs.readFile('/home/pi/NodeJs/Matrix8x8/myJobs/' + fileName, 'utf8', function (err, data) {
// if (err) { console.log(err); }
// matrixFrames = eval(data);
// console.log(data);
// socket.broadcast.emit('deleteFrame', matrixFrames);
// socket.emit('deleteFrame', matrixFrames);
// console.log("I load the frames");
// });
// });
// socket.on('deleteFrame', function (frameIndex) {
// matrixFrames.splice(frameIndex, 1);
// socket.broadcast.emit('deleteFrame', matrixFrames);
// socket.emit('deleteFrame', matrixFrames);
// console.log("ARRAY LENGHT --->>>>>>>>> " + matrixFrames.length);
// console.log("I deleted a frame");
// });
// socket.on('copyFrame', function (currentMatrixPosition, currentMatrixCopyed) {
// matrixFrames.splice(currentMatrixPosition, 0, matrixFrames[currentMatrixCopyed]);
// console.log(matrixFrames);
// socket.broadcast.emit('deleteFrame', matrixFrames);
// socket.emit('deleteFrame', matrixFrames);
// console.log("ARRAY LENGHT --->>>>>>>>> " + matrixFrames.length);
// console.log("I copyed a frame");
// });
// socket.on('drawFrame', function (msg) {
// //condivido con il resto degli utenti il mio disegno corrente
// socket.broadcast.emit('paintFrameinOthersBrowser', matrixFrames[currentframeIndex], currentframeIndex);
// });
// //-----------------------------------------------------------------------------------------
// socket.on('playFrames', function (msg) {
// console.log("Write ALL FRAMES");
// inizializzoDisplay();
// var output = addon.WriteAllFrames(matrixFrames, _i2cbus, _address, msg);
// console.log(output);
// });
//});
function clearDisplay(matrix) {
for (var i = 0; i < 16; i++) {
addon.Write(i, matrix[i], _i2cbus, _address);
......@@ -98,108 +39,43 @@ function inizializzoDisplay()
}
}
////------------------------ COMUNICATIONS ------------------------------
////parametri obbligatori - va indicata almeno una cartella
//dispatcher.setStatic('resources');
//dispatcher.setStaticDirname('.');
////oppure posso dargli il path intero
////dispatcher.setStaticDirname('/home/pi/NodeJs/Matrix8x8/');
////seplicemente ricrea un oggetto valido per il template
//function convertArrayInObject(array)
//{
// var objectList = { optionEntity: [] };
// for (var i = 0; i < array.length; i++) {
// objectList.optionEntity.push({ fileName:array[i] });
// }
// return objectList;
//}
//dispatcher.onGet("/HomePage", function (req, res) {
// //lista dei file in directory
// var files = fs.readdirSync("/home/pi/NodeJs/Matrix8x8/myJobs/", function (err) { console.log(err); });
// //(:option-entry ~ <option value="[:file-name:]">[:file-name:]</option>:)
// var fileNameList = convertArrayInObject(files);
// bind.toFile('/home/pi/NodeJs/Matrix8x8/resources/HomePage.tpl', fileNameList,
// function (data) {
// res.writeHead(200, { 'Content-Type': 'text/html' });
// res.end(data);
// });
//});
////ho scelto questa strada alternativa solo per esemplificazione - lo stesso avrebbe funzionare con un socket.emit (socket.broadcast.emit)
//dispatcher.onGet("/save.do", function (req, res) {
// res.writeHead(200, { 'Content-Type': 'text/html' });
// console.log(req.params["nomeFile"]);
// console.log(matrixFrames);
// var json = JSON.stringify(matrixFrames);
// fs.writeFile("/home/pi/NodeJs/Matrix8x8/myJobs/" + req.params["nomeFile"], json, function (err, data) { if (err) { console.log(err); } console.log(data); });
// res.end();
//});
server.listen(8082, function () {
//setto colori e intensit del display
inizializzoDisplay();
matrixFrames.push([255, 0, 255, 0, 255, 0, 255, 0,
255, 0, 255, 0, 255, 0, 255, 0]);
clearDisplay(matrixFrames[0]);
////Pulisco il display prima di procedere soltanto se il precedente diverso (quindi sto cambiando il frame)
//if (currentframeIndex != msg.frameIndex) {
// currentframeIndex = msg.frameIndex;
// if (matrixFrames[currentframeIndex] != undefined)
// { clearDisplay(matrixFrames[currentframeIndex]); }
// else
// {
// matrixFrames.push([0, 0, 0, 0, 0, 0, 0, 0,
// 0, 0, 0, 0, 0, 0, 0, 0]);
// clearDisplay(matrixFrames[currentframeIndex]);
// }
//}
//currentframeIndex = msg.frameIndex;
//if (matrixFrames[currentframeIndex] == undefined) {
// matrixFrames.push([0, 0, 0, 0, 0, 0, 0, 0,
// 0, 0, 0, 0, 0, 0, 0, 0]);
// //setto variabile matrix con ultimo array pushato
// matrix = matrixFrames[matrixFrames.length - 1];
//}
//else {
// matrix = matrixFrames[currentframeIndex];
//}
//var output = "";
//if (msg.color == "green") {
// matrix[msg.row * 2] = matrix[msg.row * 2] | msg.value;
// output = addon.Write(msg.row * 2, matrix[msg.row * 2], _i2cbus, _address);
//}
//if (msg.color == "red") {
// matrix[msg.row * 2 + 1] = matrix[msg.row * 2 + 1] | msg.value;
// output = addon.Write(msg.row * 2 + 1, matrix[msg.row * 2 + 1], _i2cbus, _address);
//}
//if (msg.color == "orange") {
// matrix[msg.row * 2] = matrix[msg.row * 2] | msg.value;
// output = addon.Write(msg.row * 2, matrix[msg.row * 2], _i2cbus, _address);
// matrix[msg.row * 2 + 1] = matrix[msg.row * 2 + 1] | msg.value;
// output = addon.Write(msg.row * 2 + 1, matrix[msg.row * 2 + 1], _i2cbus, _address);
//}
//if (msg.color == "gray") {
// matrix[msg.row * 2] = matrix[msg.row * 2] & (~msg.value);
// output = addon.Write(msg.row * 2, matrix[msg.row * 2], _i2cbus, _address);
//matrixFrames.push([255, 0, 255, 0, 255, 0, 255, 0,
// 255, 0, 255, 0, 255, 0, 255, 0]);
//clearDisplay(matrixFrames[0]);
//RED & GREEN
var page = [
128, 0, 64, 0, 32, 0, 16, 0, 8, 0, 4, 0, 2, 0, 1, 0,
0, 1, 0, 2, 0, 4, 0, 8, 0, 16, 0, 32, 0, 64, 0, 128,
255, 0, 129, 0, 129, 0, 129, 0, 129, 0, 129, 0, 129, 0, 255, 0,
];
do {
var p = [];
for (i = 0; i < 16; i++)
{
var a = getRandomInt(0, 255);
p.push(a);
}
addon.WriteAllFrames(p, _i2cbus, _address, 200);
}
while (true);
// matrix[msg.row * 2 + 1] = matrix[msg.row * 2 + 1] & (~msg.value);
// output = addon.Write(msg.row * 2 + 1, matrix[msg.row * 2 + 1], _i2cbus, _address);
//}
console.log("Write : ", "End of program");
});
/**
* Returns a random integer between min (inclusive) and max (inclusive)
* Using Math.round() will give you a non-uniform distribution!
*/
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
//console.log("Your server is on 8082 port visit http://127.0.0.1/HomePage to start");
console.log(networkInterfaces.eth0[0].address);
\ No newline at end of file
# Do not edit. File was generated by node-gyp's "configure" step
{
"target_defaults": {
"cflags": [],
"default_configuration": "Release",
"defines": [],
"include_dirs": [],
"libraries": []
},
"variables": {
"arm_float_abi": "hard",
"arm_fpu": "vfp",
"arm_thumb": 0,
"arm_version": "6",
"asan": 0,
"coverage": "false",
"debug_devtools": "node",
"debug_http2": "false",
"debug_nghttp2": "false",
"force_dynamic_crt": 0,
"host_arch": "arm",
"icu_gyp_path": "tools/icu/icu-system.gyp",
"icu_small": "false",
"node_byteorder": "little",
"node_enable_d8": "false",
"node_enable_v8_vtunejit": "false",
"node_install_npm": "false",
"node_module_version": 57,
"node_no_browser_globals": "false",
"node_prefix": "/usr",
"node_release_urlbase": "",
"node_shared": "false",
"node_shared_cares": "true",
"node_shared_http_parser": "true",
"node_shared_libuv": "true",
"node_shared_openssl": "true",
"node_shared_zlib": "true",
"node_tag": "",
"node_use_bundled_v8": "true",
"node_use_dtrace": "false",
"node_use_etw": "false",
"node_use_lttng": "false",
"node_use_openssl": "true",
"node_use_perfctr": "false",
"node_use_v8_platform": "true",
"node_without_node_options": "false",
"openssl_fips": "",
"openssl_no_asm": 0,
"shlib_suffix": "so.57",
"target_arch": "arm",
"uv_parent_path": "/deps/uv/",
"uv_use_dtrace": "false",
"v8_enable_gdbjit": 0,
"v8_enable_i18n_support": 1,
"v8_enable_inspector": 1,
"v8_no_strict_aliasing": 1,
"v8_optimized_debug": 0,
"v8_promise_internal_field_count": 1,
"v8_random_seed": 0,
"v8_trace_maps": 0,
"v8_use_snapshot": "false",
"want_separate_host_toolset": 0,
"nodedir": "/root/.node-gyp/8.6.0",
"standalone_static_library": 1
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment