Related
My MVCE for SSL relay server:
#pragma once
#include <stdint.h>
#include <iostream>
#include <asio.hpp>
#include <asio/ssl.hpp>
namespace test
{
namespace setup
{
const uint32_t maxMessageSize = 1024 * 1024;
const uint32_t maxSessionsNum = 10;
}
enum class MessageType
{
LOG_ON = 0,
TEXT_MESSAGE = 1
};
class MessageHeader
{
public:
uint32_t messageType;
uint32_t messageLength;
MessageHeader(uint32_t messageType, uint32_t messageLength) : messageType(messageType), messageLength(messageLength) {}
};
class LogOn
{
public:
MessageHeader header;
uint32_t sessionId;
uint32_t isClient0;
LogOn() : header((uint32_t)MessageType::LOG_ON, sizeof(LogOn)) {}
};
class TextMessage
{
public:
MessageHeader header;
uint8_t data[];
TextMessage() : header((uint32_t)MessageType::TEXT_MESSAGE, sizeof(TextMessage)){}
};
class ClientSocket;
class Session
{
public:
ClientSocket* pClient0;
ClientSocket* pClient1;
};
Session* getSession(uint32_t sessionId);
class ClientSocket
{
public:
bool useTLS;
std::shared_ptr<asio::ip::tcp::socket> socket;
std::shared_ptr<asio::ssl::stream<asio::ip::tcp::socket>> socketSSL;
Session* pSession;
bool isClient0;
std::recursive_mutex writeBufferLock;
std::vector<char> readBuffer;
uint32_t readPos;
ClientSocket(asio::ip::tcp::socket& socket) : useTLS(false)
{
this->socket = std::make_shared<asio::ip::tcp::socket>(std::move(socket));
this->readBuffer.resize(setup::maxMessageSize + sizeof(MessageHeader));
this->readPos = 0;
}
ClientSocket(asio::ssl::stream<asio::ip::tcp::socket>& socket) : useTLS(true)
{
this->socketSSL = std::make_shared<asio::ssl::stream<asio::ip::tcp::socket>>(std::move(socket));
this->readBuffer.resize(setup::maxMessageSize + sizeof(MessageHeader));
this->readPos = 0;
}
bool writeSocket(uint8_t* pBuffer, uint32_t bufferSize)
{
try
{
std::unique_lock<std::recursive_mutex>
lock(this->writeBufferLock);
size_t writtenBytes = 0;
if (true == this->useTLS)
{
writtenBytes = asio::write(*this->socketSSL,
asio::buffer(pBuffer, bufferSize));
}
else
{
writtenBytes = asio::write(*this->socket,
asio::buffer(pBuffer, bufferSize));
}
return (writtenBytes == bufferSize);
}
catch (asio::system_error e)
{
std::cout << e.what() << std::endl;
}
catch (std::exception e)
{
std::cout << e.what() << std::endl;
}
catch (...)
{
std::cout << "Some other exception" << std::endl;
}
return false;
}
void asyncReadNextMessage(uint32_t messageSize)
{
auto readMessageLambda = [&](const asio::error_code errorCode, std::size_t length)
{
this->readPos += (uint32_t)length;
if (0 != errorCode.value())
{
//send socket to remove
printf("errorCode= %u, message=%s\n", errorCode.value(), errorCode.message().c_str());
//sendRemoveMeSignal();
return;
}
if ((this->readPos < sizeof(MessageHeader)))
{
asyncReadNextMessage(sizeof(MessageHeader) - this->readPos);
return;
}
MessageHeader* pMessageHeader = (MessageHeader*)this->readBuffer.data();
if (pMessageHeader->messageLength > setup::maxMessageSize)
{
//Message to big - should disconnect ?
this->readPos = 0;
asyncReadNextMessage(sizeof(MessageHeader));
return;
}
if (this->readPos < pMessageHeader->messageLength)
{
asyncReadNextMessage(pMessageHeader->messageLength - this->readPos);
return;
}
MessageType messageType = (MessageType)pMessageHeader->messageType;
switch(messageType)
{
case MessageType::LOG_ON:
{
LogOn* pLogOn = (LogOn*)pMessageHeader;
printf("LOG_ON message sessionId=%u, isClient0=%u\n", pLogOn->sessionId, pLogOn->isClient0);
this->isClient0 = pLogOn->isClient0;
this->pSession = getSession(pLogOn->sessionId);
if (this->isClient0)
this->pSession->pClient0 = this;
else
this->pSession->pClient1 = this;
}
break;
case MessageType::TEXT_MESSAGE:
{
TextMessage* pTextMessage = (TextMessage*)pMessageHeader;
if (nullptr != pSession)
{
if (this->isClient0)
{
if (nullptr != pSession->pClient1)
{
pSession->pClient1->writeSocket((uint8_t*)pTextMessage, pTextMessage->header.messageLength);
}
}
else
{
if (nullptr != pSession->pClient0)
{
pSession->pClient0->writeSocket((uint8_t*)pTextMessage, pTextMessage->header.messageLength);
}
}
}
}
break;
}
this->readPos = 0;
asyncReadNextMessage(sizeof(MessageHeader));
};
if (true == this->useTLS)
{
this->socketSSL->async_read_some(asio::buffer(this->readBuffer.data() + this->readPos, messageSize), readMessageLambda);
}
else
{
this->socket->async_read_some(asio::buffer(this->readBuffer.data() + this->readPos, messageSize), readMessageLambda);
}
}
};
class SSLRelayServer
{
public:
static SSLRelayServer* pSingleton;
asio::io_context ioContext;
asio::ssl::context sslContext;
std::vector<std::thread> workerThreads;
asio::ip::tcp::acceptor* pAcceptor;
asio::ip::tcp::endpoint* pEndpoint;
bool useTLS;
Session* sessions[setup::maxSessionsNum];
SSLRelayServer() : pAcceptor(nullptr), pEndpoint(nullptr), sslContext(asio::ssl::context::tlsv13_server)//sslContext(asio::ssl::context::sslv23)
{
this->useTLS = false;
this->pSingleton = this;
//this->sslContext.set_options(asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2);
this->sslContext.set_password_callback(std::bind(&SSLRelayServer::getPrivateKeyPEMFilePassword, this));
this->sslContext.use_certificate_chain_file("server_cert.pem");
this->sslContext.use_private_key_file("server_private_key.pem",
asio::ssl::context::pem);
}
static SSLRelayServer* getSingleton()
{
return pSingleton;
}
std::string getPrivateKeyPEMFilePassword() const
{
return "";
}
void addClientSocket(asio::ip::tcp::socket& socket)
{
ClientSocket* pClientSocket = new ClientSocket(socket); // use smart pointers
pClientSocket->asyncReadNextMessage(sizeof(MessageHeader));
}
void addSSLClientToken(asio::ssl::stream<asio::ip::tcp::socket>&sslSocket)
{
ClientSocket* pClientSocket = new ClientSocket(sslSocket); // use smart pointers
pClientSocket->asyncReadNextMessage(sizeof(MessageHeader));
}
void handleAccept(asio::ip::tcp::socket& socket, const asio::error_code& errorCode)
{
if (!errorCode)
{
printf("accepted\n");
if (true == socket.is_open())
{
asio::ip::tcp::no_delay no_delay_option(true);
socket.set_option(no_delay_option);
addClientSocket(socket);
}
}
}
void handleAcceptTLS(asio::ip::tcp::socket& socket, const asio::error_code& errorCode)
{
if (!errorCode)
{
printf("accepted\n");
if (true == socket.is_open())
{
asio::ip::tcp::no_delay no_delay_option(true);
asio::ssl::stream<asio::ip::tcp::socket> sslStream(std::move(socket), this->sslContext);
try
{
sslStream.handshake(asio::ssl::stream_base::server);
sslStream.lowest_layer().set_option(no_delay_option);
addSSLClientToken(sslStream);
}
catch (asio::system_error e)
{
std::cout << e.what() << std::endl;
return;
}
catch (std::exception e)
{
std::cout << e.what() << std::endl;
return;
}
catch (...)
{
std::cout << "Other exception" << std::endl;
return;
}
}
}
}
void startAccept()
{
auto acceptHandler = [this](const asio::error_code& errorCode, asio::ip::tcp::socket socket)
{
printf("acceptHandler\n");
handleAccept(socket, errorCode);
this->startAccept();
};
auto tlsAcceptHandler = [this](const asio::error_code& errorCode, asio::ip::tcp::socket socket)
{
printf("tlsAcceptHandler\n");
handleAcceptTLS(socket, errorCode);
this->startAccept();
};
if (true == this->useTLS)
{
this->pAcceptor->async_accept(tlsAcceptHandler);
}
else
{
this->pAcceptor->async_accept(acceptHandler);
}
}
bool run(uint32_t servicePort, uint32_t threadsNum, bool useTLS)
{
this->useTLS = useTLS;
this->pEndpoint = new asio::ip::tcp::endpoint(asio::ip::tcp::v4(), servicePort);
this->pAcceptor = new asio::ip::tcp::acceptor(ioContext, *pEndpoint);
this->pAcceptor->listen();
this->startAccept();
for (uint32_t threadIt = 0; threadIt < threadsNum; ++threadIt)
{
this->workerThreads.emplace_back([&]() {
#ifdef WINDOWS
SetThreadDescription(GetCurrentThread(), L"SSLRelayServer worker thread");
#endif
this->ioContext.run(); }
);
}
return true;
}
Session* getSession(uint32_t sessionId)
{
if (nullptr == this->sessions[sessionId])
{
this->sessions[sessionId] = new Session();
}
return this->sessions[sessionId];
}
};
SSLRelayServer* SSLRelayServer::pSingleton = nullptr;
Session* getSession(uint32_t sessionId)
{
SSLRelayServer* pServer = SSLRelayServer::getSingleton();
Session* pSession = pServer->getSession(sessionId);
return pSession;
}
class Client
{
public:
asio::ssl::context sslContext;
std::shared_ptr<asio::ip::tcp::socket> socket;
std::shared_ptr<asio::ssl::stream<asio::ip::tcp::socket>> socketSSL;
asio::io_context ioContext;
bool useTLS;
bool isClient0;
uint32_t readDataIt;
std::vector<uint8_t> readBuffer;
std::thread listenerThread;
Client() : sslContext(asio::ssl::context::tlsv13_client)//sslContext(asio::ssl::context::sslv23)
{
sslContext.load_verify_file("server_cert.pem");
//sslContext.set_verify_mode(asio::ssl::verify_peer);
using asio::ip::tcp;
using std::placeholders::_1;
using std::placeholders::_2;
sslContext.set_verify_callback(std::bind(&Client::verifyCertificate, this, _1, _2));
this->readBuffer.resize(setup::maxMessageSize);
this->readDataIt = 0;
}
bool verifyCertificate(bool preverified, asio::ssl::verify_context& verifyCtx)
{
return true;
}
void listenerRunner()
{
#ifdef WINDOWS
if (this->isClient0)
{
SetThreadDescription(GetCurrentThread(), L"listenerRunner client0");
}
else
{
SetThreadDescription(GetCurrentThread(), L"listenerRunner client1");
}
#endif
while (1==1)
{
asio::error_code errorCode;
size_t transferred = 0;
if (true == this->useTLS)
{
transferred = this->socketSSL->read_some(asio::buffer(this->readBuffer.data() + this->readDataIt, sizeof(MessageHeader) - this->readDataIt), errorCode);
}
else
{
transferred = this->socket->read_some(asio::buffer(this->readBuffer.data() + this->readDataIt, sizeof(MessageHeader) - this->readDataIt), errorCode);
}
this->readDataIt += transferred;
if (0 != errorCode.value())
{
this->readDataIt = 0;
continue;
}
if (this->readDataIt < sizeof(MessageHeader))
continue;
MessageHeader* pMessageHeader = (MessageHeader*)this->readBuffer.data();
if (pMessageHeader->messageLength > setup::maxMessageSize)
{
exit(1);
}
bool resetSocket = false;
while (pMessageHeader->messageLength > this->readDataIt)
{
printf("readDataIt=%u, threadId=%u\n", this->readDataIt, GetCurrentThreadId());
{
//message not complete
if (true == this->useTLS)
{
transferred = this->socketSSL->read_some(asio::buffer(this->readBuffer.data() + this->readDataIt, pMessageHeader->messageLength - this->readDataIt), errorCode);
}
else
{
transferred = this->socket->read_some(asio::buffer(this->readBuffer.data() + this->readDataIt, pMessageHeader->messageLength - this->readDataIt), errorCode);
}
this->readDataIt += transferred;
}
if (0 != errorCode.value())
{
exit(1);
}
}
MessageType messageType = (MessageType)pMessageHeader->messageType;
switch (messageType)
{
case MessageType::TEXT_MESSAGE:
{
TextMessage* pTextMessage = (TextMessage*)pMessageHeader;
printf("TEXT_MESSAGE: %s\n", pTextMessage->data);
}
break;
}
this->readDataIt = 0;
}
}
void run(uint32_t sessionId, bool isClient0, bool useTLS, uint32_t servicePort)
{
this->useTLS = useTLS;
this->isClient0 = isClient0;
if (useTLS)
{
socketSSL = std::make_shared<asio::ssl::stream<asio::ip::tcp::socket>>(ioContext, sslContext);
}
else
{
socket = std::make_shared<asio::ip::tcp::socket>(ioContext);
}
asio::ip::tcp::resolver resolver(ioContext);
asio::ip::tcp::resolver::results_type endpoints = resolver.resolve(asio::ip::tcp::v4(), "127.0.0.1", std::to_string(servicePort));
asio::ip::tcp::no_delay no_delay_option(true);
if (true == useTLS)
{
asio::ip::tcp::endpoint sslEndpoint = asio::connect(socketSSL->lowest_layer(), endpoints);
socketSSL->handshake(asio::ssl::stream_base::client);
socketSSL->lowest_layer().set_option(no_delay_option);
}
else
{
asio::ip::tcp::endpoint endpoint = asio::connect(*socket, endpoints);
socket->set_option(no_delay_option);
}
this->listenerThread = std::thread(&Client::listenerRunner, this);
LogOn logOn;
logOn.isClient0 = isClient0;
logOn.sessionId = sessionId;
const uint32_t logOnSize = sizeof(logOn);
if (true == useTLS)
{
size_t transferred = asio::write(*socketSSL, asio::buffer(&logOn, sizeof(LogOn)));
}
else
{
size_t transferred = asio::write(*socket, asio::buffer(&logOn, sizeof(LogOn)));
}
uint32_t counter = 0;
while (1 == 1)
{
std::string number = std::to_string(counter);
std::string message;
if (this->isClient0)
{
message = "Client0: " + number;
}
else
{
message = "Client1: " + number;
}
TextMessage textMessage;
textMessage.header.messageLength += message.size() + 1;
if (this->useTLS)
{
size_t transferred = asio::write(*socketSSL, asio::buffer(&textMessage, sizeof(TextMessage)));
transferred = asio::write(*socketSSL, asio::buffer(message.c_str(), message.length() + 1));
}
else
{
size_t transferred = asio::write(*socket, asio::buffer(&textMessage, sizeof(TextMessage)));
transferred = asio::write(*socket, asio::buffer(message.c_str(), message.length() + 1));
}
++counter;
//Sleep(1000);
}
}
};
void clientTest(uint32_t sessionId, bool isClient0, bool useTLS,
uint32_t servicePort)
{
#ifdef WINDOWS
if (isClient0)
{
SetThreadDescription(GetCurrentThread(), L"Client0");
}
else
{
SetThreadDescription(GetCurrentThread(), L"Client1");
}
#endif
Client client;
client.run(sessionId, isClient0, useTLS, servicePort);
while (1 == 1)
{
Sleep(1000);
}
}
void SSLRelayTest()
{
SSLRelayServer relayServer;
const uint32_t threadsNum = 1;
const bool useTLS = true;
const uint32_t servicePort = 777;
relayServer.run(servicePort, threadsNum, useTLS);
Sleep(5000);
std::vector<std::thread> threads;
const uint32_t sessionId = 0;
threads.emplace_back(clientTest, sessionId, true, useTLS, servicePort);
threads.emplace_back(clientTest, sessionId, false, useTLS,servicePort);
for (std::thread& threadIt : threads)
{
threadIt.join();
}
}
}
What this sample does ?
It runs SSL relay server on localhost port 777 which connects two clients and allows exchanging
of text messages between them.
Promblem:
When I run that sample server returns error "errorCode= 167772441, message=decryption failed or bad record mac (SSL routines)" in void "asyncReadNextMessage(uint32_t messageSize)"
I found out this is caused by client which reads and writes to client SSL socket from separate threads (changing variable useTLS to 0 runs it on normal socket which proves that it is SSL socket problem).
Apparently TLS is not full-duplex protocol (I did not know about that). I can't synchronize access to read and write with mutex because when socket enters read state and there is no
incoming message writing to socked will be blocked forever. At this thread Boost ASIO, SSL: How do strands help the implementation?
someone recommended using strands but someone else wrote that asio only synchronizes not concurrent execution of read and write handles which does not fix the problem.
I expect that somehow there is a way to synchronize read and write to SSL socket. I'm 100% sure that problem lies in synchronizing read and writes to socket because when I wrote example with read and write to socket done by one thread it worked. However then client always expects that there is message to read which can block all write if there is not. Can it be solved without using separate sockets for reads and writes ?
Okay I figured it out by writting many diffrent samples of code including SSL sockets.
When asio::io_context is already running you can't simply schedule asio::async_write or asio::async_read from thread which is not
associated with strand connected to that socket.
So when there is:
asio::async_write(*this->socketSSL, asio::buffer(pBuffer, bufferSize), asio::bind_executor(readWriteStrand,writeMessageLambda));
but thread which is executing is not running from readWriteStrand strand then it should be written as:
asio::post(ioContext, asio::bind_executor(readWriteStrand, [&]() {asio::async_read(*this->socketSSL, asio::buffer(readBuffer.data() + this->readDataIt, messageSize), asio::bind_executor(readWriteStrand, readMessageLambda)); }));
I'm building a robot for my class, and we have to have 2 servos and 1 DC motor working in a specific way. Everything is hooked up to an arduino uno, and my code works, but I using tinkercad to test a few things but I'm getting an error which is stopping my code from functioning in tinker cad, and I'm at a total loss.
ERROR
In function 'void loop()':
44:9: error: too few arguments to function 'void motor(char, char)'
17:6: note: declared here
exit status 1
CODE
#include <Servo.h> // set servo header to let ardduino know you intend to use a servo
Servo mycontinuousservo; // declare servos to be used
Servo mydegreeservo;
int In1 = 7; // declare your global variables to indicate pin numbers
int In2 = 8;
int pin = 6;
int servocontinuouspin = 10;
int servodegreepin = 9;
int angle = 90;
void servopos();
void servocontinous();
void motor(char Speed,char Direction);
void setup() {
// put your setup code here, to run once:
pinMode(In1, OUTPUT);
pinMode(In2, OUTPUT);
pinMode(pin, OUTPUT);
digitalWrite(In1, HIGH); //pin 7 moves forward
digitalWrite(In2, LOW); //pin 8 moves forward
analogWrite(pin, 0); // start at 0
pinMode(servocontinuouspin, OUTPUT);
pinMode(servodegreepin, OUTPUT);
mycontinuousservo.attach(servocontinuouspin);
mydegreeservo.attach(servodegreepin);
mycontinuousservo.write(90);
Serial.begin(9600); // for serial communication
}
void loop() {
servocontinous(); //call by ref aforedeclared functions
servopos();
motor();
}
// EXIT THE LOOP
void servopos() { //position function
int degree = 0;
int i = 0;
for (i = 0; i < 18; i++) {
mydegreeservo.write(degree);
delay(500); //delay 0.5 seconds
degree = degree + 10;
}
}
void servocontinous() // continous servo settings
{
for (int angle = 90; angle >= 0; angle--) {
mycontinuousservo.write(angle);
delay(50);
}
if (angle == 0) {
Serial.print("speed\n");
}
for (angle = 0; angle < 90; angle++)
{
mycontinuousservo.write(angle);
delay(50);
}
}
void motor() //motor function
{
char Speed = 0;
char Direction = 0;
if (Serial.available() > 0) //initialising
{
if (Direction == 'f') //70 representing F on the ASCII table
{
delay(500);
Serial.println("F");
}
if (Direction == 'r')
{
delay(500);
Serial.println("R");
}
}
if (Serial.available() > 0)
{
Speed = Serial.read();
if (Speed == '0')
{
Speed = 0;
Serial.println("Speed 0");
}
if (Speed == '1')
{
Speed = 14;
Serial.println("Speed 1");
}
if (Speed == '2')
{
Speed = 29;
Serial.println("Speed 2");
}
if (Speed == '3')
{
Speed = 42;
Serial.println("Speed 3");
}
if (Speed == '4')
{
Speed = 56;
Serial.println("Speed 4");
}
if (Speed == '5')
{
Speed = 70;
Serial.println("Speed 5");
}
if (Speed == '6')
{
Speed = 84;
Serial.println("Speed 6");
}
if (Speed == '7')
{
Speed = 98;
Serial.println("Speed 7");
}
if (Speed == '8')
{
Speed = 112;
Serial.println("Speed 8");
}
if (Speed == '9')
{
Speed = 128;
Serial.println("Speed 9");
}
} delay(5000);
analogWrite(pin, Speed);
if (Direction == 'f')
{ digitalWrite(In1, HIGH);
digitalWrite(In2, LOW);
} if (Direction == 'r')
{
digitalWrite(In1, LOW);
digitalWrite(In2, HIGH);
}
}
Here you declare the function as taking two arguments:
void motor(char Speed,char Direction);
Later you call it with no arguments, which is invalid when compared to that declaration:
motor();
This will be an immediate compiler error. That function is described as having two arguments, you call it with zero. Compile hard fails and stops because of this contradiction.
Yet when you define it the arguments are gone, they're actually local variables:
void motor() //motor function
{
char Speed = 0;
char Direction = 0;
// ...
}
This too contradicts the earlier declaration, so if you comment out the place where it's called you'll likely get a different error.
Local variables are the private business of a function, they do not need to be shown in the function signature, so don't think these need to be included as arguments.
What you need to do is either snip the arguments from the declaration, make sure that declaration matches the function signature exactly, or move the motor() function definition to before where it is first called.
I prefer to organize things so that pre-declaration is not necessary, or at least minimized. There's no reason to not put the motor() definition before loop().
I am trying to implement a producer (master) speaking to a memory element (slave) through the memory controller (which implements the interface simple_mem_interface).
Note: Some functions details and include statements are not fully mentioned in the code attached.
Searching for bugs in the code.
Adding debugging tools to find the fault in Write Enable Port.
binding.cpp
int sc_main(int argc, char* argv[])
{
sc_signal<unsigned int> d_out,d_in,address_d;
sc_signal<bool> wen, ren, ack;
sc_clock ClkFast("ClkFast", 100, SC_NS);
sc_clock ClkSlow("ClkSlow", 50, SC_NS);
Memory_Controller Controller1 ("Controller");
d_out = Controller1.data_mem_read;
ren.write(Controller1.REN);
ack.write(Controller1.ack);
d_in.write(Controller1.data_write);
address_d.write(Controller1.address);
wen.write(Controller1.WEN);
producer P1("Producer");
P1.out(Controller1);
P1.Clk(ClkFast);
Memory_module MEM("Memory");
MEM.Wen(wen);
MEM.Ren(ren);
MEM.ack(ack);
MEM.Clock(ClkSlow);
MEM.data_in(d_in);
MEM.data_out(d_out);
MEM.address(address_d);
sc_start(5000, SC_NS);
return 0;
Memory_controller.h
#define MEM_SIZE 100
#include <interface_func.h>
class Memory_Controller : public sc_module, public simple_mem_if
{
public:
// Ports
sc_in <unsigned int> data_mem_read{ "Data_Read_from_Memory" };
sc_out<bool> REN { "Read_Enable" };
sc_out<bool> WEN { "Write_Enable" };
sc_out <bool> ack{ "ACK_Bool" };
sc_out<unsigned int> address{ "Memory_Address" }, data_write{
"Data_Written_to_Memory" };
// constructor
Memory_Controller(sc_module_name nm) : sc_module(nm)
{ // Creating a 2 dimentional array holding adresses and data
WEN.write(false);
REN.write(false);
ack.write(false);
}
~Memory_Controller() //destructor
{
}
bool Write(unsigned int address_i, unsigned int datum) // blocking write
{
WEN.write(true);
REN.write(false);
data_write.write(datum);
address.write(address_i);
if (ack == true)
return true;
else
return false;
}
bool Read(unsigned int address_i, unsigned int& datum_i) // blocking read
{
WEN.write(false);
REN.write(true);
datum_i=data_mem_read;
address.write(address_i);
if (ack == true)
return true;
else
return false;
}
void register_port(sc_port_base& port, const char* if_typename)
{
cout << "binding " << port.name() << " to "
<< "interface: " << if_typename << endl;
}
};
Memory.h
#define MEM_SIZE 100
#include "interface_func.h"
class Memory_module : public sc_module
{
public:
sc_in<bool> Wen,Ren;
sc_in <unsigned int> address, data_in ;
sc_in<bool> Clock;
sc_out <unsigned int> data_out;
sc_out <bool> ack;
bool fileinput = false;
ifstream myfile;
unsigned int item [MEM_SIZE];
Memory_module()
{
}
void Write() // blocking write
{
while (true)
{
wait();
if (Wen==true)
{
if (address >= MEM_SIZE || address < 0)
{
ack=false;
}
else
{
item[address]=data_in;
ack=true;
}
}
}
}
void Read() // blocking read
{
while (true)
{
wait();
if (Ren)
{
if (address >= MEM_SIZE || address < 0)
ack=false;
else
{
data_out.write(item[address]);
ack=true;
}
}
}
}
SC_CTOR(Memory_module)
{
SC_THREAD(Read);
sensitive << Clock.pos();
SC_THREAD(Write);
sensitive << Clock.pos();
}
};
interface_func.h
class simple_mem_if : virtual public sc_interface
{
public:
virtual bool Write(unsigned int addr, unsigned int data) = 0;
virtual bool Read(unsigned int addr, unsigned int& data) = 0;
};
After debugging the SystemC binder.cpp code, the following error arises:
(E112) get interface failed: port is not bound : port 'Controller.Write_Enable' (sc_out)
You cannot drive your unconnected ports in the Memory_Controller constructor. If you want to explicitly drive these ports during startup, move these calls to a start_of_simulation callback:
Memory_Controller(sc_module_name nm) : sc_module(nm)
{}
void start_of_simulation()
{
WEN.write(false);
REN.write(false);
ack.write(false);
}
I am trying to call the HappyTime onvif library from Swift.
I have the library linked in to my project and I am able to call some simple functions, but I am having trouble getting the syntax right in my call which passes my callback function.
Here is the Swift code:
func discoverCameras()
{
HappyInterface.sharedInstance().startProb()
//this line gives syntax error
HappyInterface.sharedInstance().setProbeCB(cameraDiscovered)
}
func cameraDiscovered(cameraFound:UnsafeMutablePointer<DEVICE_BINFO>)
{
table.reloadData()
}
my setProbeCB call gives this error:
Cannot convert value of type '(UnsafeMutablePointer) -> ()' to expected argument type 'UnsafeMutablePointer' (aka 'UnsafeMutablePointer, UnsafeMutablePointer<()>) -> ()>>')
Here is the Obj C implementation:
- (void) setProbeCB:(onvif_probe_cb *)cb {
set_probe_cb(*cb, 0);
}
This is the Obj C header:
- (void) setProbeCB:(onvif_probe_cb *)cb;
This is the C header:
#ifndef __H_ONVIF_PROBE_H__
#define __H_ONVIF_PROBE_H__
#include "onvif.h"
typedef void (* onvif_probe_cb)(DEVICE_BINFO * p_res, void * pdata);
#ifdef __cplusplus
extern "C" {
#endif
ONVIF_API void set_probe_cb(onvif_probe_cb cb, void * pdata);
ONVIF_API void set_probe_interval(int interval);
ONVIF_API int start_probe(int interval);
ONVIF_API void stop_probe();
ONVIF_API void send_probe_req();
#ifdef __cplusplus
}
#endif
#endif // __H_ONVIF_PROBE_H__
This is the C code:
/***************************************************************************************/
#define MAX_PROBE_FD 8
/***************************************************************************************/
onvif_probe_cb g_probe_cb = 0;
void * g_probe_cb_data = 0;
pthread_t g_probe_thread = 0;
int g_probe_fd[MAX_PROBE_FD];
int g_probe_interval = 30;
BOOL g_probe_running = FALSE;
/***************************************************************************************/
int onvif_probe_init(unsigned int ip)
{
int opt = 1;
SOCKET fd;
struct sockaddr_in addr;
struct ip_mreq mcast;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if(fd < 0)
{
log_print(LOG_ERR, "socket SOCK_DGRAM error!\n");
return -1;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(3702);
addr.sin_addr.s_addr = ip;
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
{
// if port 3702 already occupied, only receive unicast message
addr.sin_port = 0;
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
{
closesocket(fd);
log_print(LOG_ERR, "bind error! %s\n", sys_os_get_socket_error());
return -1;
}
}
/* reuse socket addr */
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)))
{
log_print(LOG_WARN, "setsockopt SO_REUSEADDR error!\n");
}
memset(&mcast, 0, sizeof(mcast));
mcast.imr_multiaddr.s_addr = inet_addr("239.255.255.250");
mcast.imr_interface.s_addr = ip;
if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast, sizeof(mcast)) < 0)
{
#if __WIN32_OS__
if(setsockopt(fd, IPPROTO_IP, 5, (char*)&mcast, sizeof(mcast)) < 0)
#endif
{
closesocket(fd);
log_print(LOG_ERR, "setsockopt IP_ADD_MEMBERSHIP error! %s\n", sys_os_get_socket_error());
return -1;
}
}
return fd;
}
char probe_req1[] =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<Envelope xmlns:tds=\"http://www.onvif.org/ver10/device/wsdl\" xmlns=\"http://www.w3.org/2003/05/soap-envelope\">"
"<Header>"
"<wsa:MessageID xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">uuid:%s</wsa:MessageID>"
"<wsa:To xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To>"
"<wsa:Action xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action>"
"</Header>"
"<Body>"
"<Probe xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\">"
"<Types>tds:Device</Types>"
"<Scopes />"
"</Probe>"
"</Body>"
"</Envelope>";
char probe_req2[] =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<Envelope xmlns:dn=\"http://www.onvif.org/ver10/network/wsdl\" xmlns=\"http://www.w3.org/2003/05/soap-envelope\">"
"<Header>"
"<wsa:MessageID xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">uuid:%s</wsa:MessageID>"
"<wsa:To xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To>"
"<wsa:Action xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action>"
"</Header>"
"<Body>"
"<Probe xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\">"
"<Types>dn:NetworkVideoTransmitter</Types>"
"<Scopes />"
"</Probe>"
"</Body>"
"</Envelope>";
int onvif_probe_req_tx(int fd)
{
int len;
int rlen;
char * p_bufs = NULL;
struct sockaddr_in addr;
int buflen = 10*1024;
p_bufs = (char *)malloc(buflen);
if (NULL == p_bufs)
{
return -1;
}
memset(p_bufs, 0, buflen);
sprintf(p_bufs, probe_req1, onvif_uuid_create());
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("239.255.255.250");
addr.sin_port = htons(3702);
len = strlen(p_bufs);
rlen = sendto(fd, p_bufs, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
if (rlen != len)
{
log_print(LOG_ERR, "onvif_probe_req_tx::rlen = %d,slen = %d\r\n", rlen, len);
}
usleep(1000);
memset(p_bufs, 0, buflen);
sprintf(p_bufs, probe_req2, onvif_uuid_create());
len = strlen(p_bufs);
rlen = sendto(fd, p_bufs, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
if (rlen != len)
{
log_print(LOG_ERR, "onvif_probe_req_tx::rlen = %d,slen = %d\r\n", rlen, len);
}
free(p_bufs);
return rlen;
}
BOOL onvif_parse_device_binfo(XMLN * p_node, DEVICE_BINFO * p_res)
{
XMLN * p_EndpointReference;
XMLN * p_Types;
XMLN * p_XAddrs;
p_EndpointReference = xml_node_soap_get(p_node, "EndpointReference");
if (p_EndpointReference)
{
XMLN * p_Address = xml_node_soap_get(p_EndpointReference, "Address");
if (p_Address && p_Address->data)
{
strncpy(p_res->EndpointReference, p_Address->data, sizeof(p_res->EndpointReference)-1);
}
}
p_Types = xml_node_soap_get(p_node, "Types");
if (p_Types && p_Types->data)
{
p_res->type = parse_DeviceType(p_Types->data);
}
p_XAddrs = xml_node_soap_get(p_node, "XAddrs");
if (p_XAddrs && p_XAddrs->data)
{
parse_XAddr(p_XAddrs->data, &p_res->XAddr);
if (p_res->XAddr.host[0] == '\0' || p_res->XAddr.port == 0)
{
return FALSE;
}
}
else
{
return FALSE;
}
return TRUE;
}
BOOL onvif_probe_res(XMLN * p_node, DEVICE_BINFO * p_res)
{
XMLN * p_body = xml_node_soap_get(p_node, "Body");
if (p_body)
{
XMLN * p_ProbeMatches = xml_node_soap_get(p_body, "ProbeMatches");
if (p_ProbeMatches)
{
XMLN * p_ProbeMatch = xml_node_soap_get(p_ProbeMatches, "ProbeMatch");
while (p_ProbeMatch && soap_strcmp(p_ProbeMatch->name, "ProbeMatch") == 0)
{
if (onvif_parse_device_binfo(p_ProbeMatch, p_res))
{
if (g_probe_cb)
{
g_probe_cb(p_res, g_probe_cb_data);
}
}
p_ProbeMatch = p_ProbeMatch->next;
}
}
else
{
XMLN * p_Hello = xml_node_soap_get(p_body, "Hello");
if (p_Hello)
{
if (onvif_parse_device_binfo(p_Hello, p_res))
{
if (g_probe_cb)
{
g_probe_cb(p_res, g_probe_cb_data);
}
}
}
}
}
return TRUE;
}
int onvif_probe_net_rx()
{
int i;
int ret;
int maxfd = 0;
int fd = 0;
char rbuf[10*1024];
fd_set fdread;
struct timeval tv = {1, 0};
FD_ZERO(&fdread);
for (i = 0; i < MAX_PROBE_FD; i++)
{
if (g_probe_fd[i] > 0)
{
FD_SET(g_probe_fd[i], &fdread);
if (g_probe_fd[i] > maxfd)
{
maxfd = g_probe_fd[i];
}
}
}
ret = select(maxfd+1, &fdread, NULL, NULL, &tv);
if (ret == 0) // Time expired
{
return 0;
}
for (i = 0; i < MAX_PROBE_FD; i++)
{
if (g_probe_fd[i] > 0 && FD_ISSET(g_probe_fd[i], &fdread))
{
int rlen;
int addr_len;
struct sockaddr_in addr;
unsigned int src_ip;
unsigned int src_port;
XMLN * p_node;
fd = g_probe_fd[i];
addr_len = sizeof(struct sockaddr_in);
rlen = recvfrom(fd, rbuf, sizeof(rbuf), 0, (struct sockaddr *)&addr, (socklen_t*)&addr_len);
if (rlen <= 0)
{
log_print(LOG_ERR, "onvif_probe_net_rx::rlen = %d, fd = %d\r\n", rlen, fd);
continue;
}
src_ip = addr.sin_addr.s_addr;
src_port = addr.sin_port;
p_node = xxx_hxml_parse(rbuf, rlen);
if (p_node == NULL)
{
log_print(LOG_ERR, "onvif_probe_net_rx::hxml parse err!!!\r\n");
}
else
{
DEVICE_BINFO res;
memset(&res, 0, sizeof(DEVICE_BINFO));
onvif_probe_res(p_node, &res);
}
xml_node_del(p_node);
}
}
return 1;
}
void * onvif_probe_thread(void * argv)
{
int count = 0;
int i = 0;
int j = 0;
for (; i < get_if_nums() && j < MAX_PROBE_FD; i++, j++)
{
unsigned int ip = get_if_ip(i);
if (ip != 0 && ip != inet_addr("127.0.0.1"))
{
g_probe_fd[j] = onvif_probe_init(ip);
}
}
for (i = 0; i < MAX_PROBE_FD; i++)
{
if (g_probe_fd[i] > 0)
{
onvif_probe_req_tx(g_probe_fd[i]);
}
}
while (g_probe_running)
{
if (onvif_probe_net_rx() == 0)
{
count++;
}
if (count >= g_probe_interval)
{
count = 0;
for (i = 0; i < MAX_PROBE_FD; i++)
{
if (g_probe_fd[i] > 0)
{
onvif_probe_req_tx(g_probe_fd[i]);
}
}
}
usleep(1000);
}
g_probe_thread = 0;
return NULL;
}
ONVIF_API void set_probe_cb(onvif_probe_cb cb, void * pdata)
{
g_probe_cb = cb;
g_probe_cb_data = pdata;
}
ONVIF_API void send_probe_req()
{
int i;
for (i = 0; i < MAX_PROBE_FD; i++)
{
if (g_probe_fd[i] > 0)
{
onvif_probe_req_tx(g_probe_fd[i]);
}
}
}
ONVIF_API void set_probe_interval(int interval)
{
g_probe_interval = interval;
if (g_probe_interval < 10)
{
g_probe_interval = 30;
}
}
ONVIF_API int start_probe(int interval)
{
g_probe_running = TRUE;
set_probe_interval(interval);
g_probe_thread = sys_os_create_thread((void *)onvif_probe_thread, NULL);
if (g_probe_thread)
{
return 0;
}
return -1;
}
ONVIF_API void stop_probe()
{
int i;
g_probe_running = FALSE;
while (g_probe_thread)
{
usleep(1000);
}
for (i = 0; i < MAX_PROBE_FD; i++)
{
if (g_probe_fd[i] > 0)
{
closesocket(g_probe_fd[i]);
g_probe_fd[i] = 0;
}
}
}
Here is what the DEVICE_BINFO struct looks like:
typedef struct
{
int type; // device type
char EndpointReference[100];
onvif_XAddr XAddr; // xaddr, include port host, url
} DEVICE_BINFO;
One thing that should be fixed is a mismatch in the number of arguments to the callback. Swift calls the Objective-C setProbeCB() method, giving it a pointer to the cameraDiscovered() function, which takes a single argument. Then setProbeCB() gives the function pointer to the C set_probe_cb() function, which expects a pointer to a function that takes two arguments.
Another observation is that setProbeCB() could just take onvif_probe_cb instead of onvif_probe_cb* and then call C code simply as set_probe_cb(cb, 0). However, I don't think it makes much difference.
Also, I think the question could have been distilled to a smaller size.
The following is a simplified example based on your original code. It shows how to implement a callback in Swift and have C code call it, but the real fun starts when passing data via callback parameters and return values. It gets very tricky very fast, and that's why the example doesn't show how to deal with DEVICE_BINFO in Swift code. It's a topic in its own right.
The clue to using (Objective-)C functions and types in Swift is figuring out how they are imported into Swift. For example, to find out how onvif_probe_cb is imported, type it on a line in the Swift code, place the cursor in it, and Quick Help will show you this:
Declaration: typealias onvif_probe_cb = (UnsafeMutablePointer<DEVICE_BINFO>, UnsafeMutablePointer<Void>) -> Void
Declared in: clib.h
That tells us the parameter and return types to use in our Swift implementation of the callback.
The example is by no means production quality: there are all kinds of things that can go haywire in terms of memory management etc. Please see the code comments for additional info.
First, here is the C code header (clib.h):
#ifndef clib_h
#define clib_h
#include <stdio.h>
typedef struct {
char hostname[50];
int32_t port;
char url[200];
} onvif_XAddr;
typedef struct
{
int type; // device type
char EndpointReference[100];
onvif_XAddr XAddr; // xaddr, include port host, url
} DEVICE_BINFO;
/**
* This is the typedef of the function pointer to be used for our callback.
* The function takes a pointer to DEVICE_BINFO and a pointer to some arbitrary
* data meaningful to the code that provides the callback implementation. It will
* be NULL in this example.
*/
typedef void (* onvif_probe_cb)(DEVICE_BINFO * p_res, void * pdata);
/**
* A function to set the callback.
*/
void set_probe_cb(onvif_probe_cb cb, void * pdata);
/**
* This is a function that calls the callback.
*/
void find_device();
#endif /* clib_h */
Here is the rest of our C source (clib.c):
#include "clib.h"
#include <string.h>
onvif_probe_cb gCB = 0; // global variable to store the callback pointer
void * gUserData = 0; // global variable to store pointer to user data
DEVICE_BINFO gDeviceInfo; // global variable to store device info struct
void find_device() {
// Set up gDeviceInfo
gDeviceInfo.XAddr.port = 1234;
strcpy( gDeviceInfo.XAddr.hostname, "myhost");
strcpy( gDeviceInfo.XAddr.url, "http://junk.com");
gDeviceInfo.type = 777;
// ... and, if a callback is available, call it with the device info
if (gCB) gCB(&gDeviceInfo, gUserData);
else puts("No callback available");
}
void set_probe_cb(onvif_probe_cb cb, void * pdata) {
gCB = cb;
gUserData = pdata;
}
Here is the Objective-C wrapper header (oclib.h):
#ifndef oclib_h
#define oclib_h
#import "clib.h"
#import <Foundation/Foundation.h>
/**
* Interface of an Objective-C wrapper around C code in clib.*. We could have
* gone straight to C from Swift, but I'm trying to keep the example close to the
* code in the question. Also, this extra Objective C layer could be helpful in
* translating data structures, such as DEVICE_BINFO, between C and Swift, since
* Objective-C plays much nicer with C data types. This is no surprise: any C code
* is valid Objective-C (Objective-C is a strict superset of C).
*/
#interface MyWrapper : NSObject
-(id)init;
// Please note: this one takes a single argument, while the C function it wraps
// takes 2; see the implementation.
-(void) setProbeCB:(onvif_probe_cb) cb;
-(void) findDevice;
#end
#endif /* oclib_h */
And the wrapper implementation (oclib.m):
#import "oclib.h"
/**
* Implementation of our Objective-C wrapper.
*/
#implementation MyWrapper
-(id)init { return self; }
-(void) setProbeCB:(onvif_probe_cb) cb {
// We don't want anything other than device info to be passed back and
// forth via the callback, so this wrapper function takes a single argument
// and passes 0 as the 2nd argument to the wrapped C function.
set_probe_cb(cb, 0);
}
-(void) findDevice {
find_device();
}
#end
Finally, here is the Swift code that implements the callback (main.swift):
var w : MyWrapper = MyWrapper()
/**
* This is the callback implementation in Swift. We don't use the 2nd argument, userData, but it still
* has to be present to satisfy the way the callback function pointer is specified in C code.
*/
func cameraDiscovered( info : UnsafeMutablePointer<DEVICE_BINFO>, userData : UnsafeMutablePointer<Void>) {
print("Called the Swift callback!")
let devInfo : DEVICE_BINFO = info.memory;
print( "The device type is \(devInfo.type)")
print( "The device port is \(devInfo.XAddr.port)")
}
// Provide the callback to C code via Objective-C
w.setProbeCB(cameraDiscovered)
// ... and call a function that will cause the C code to invoke the callback.
w.findDevice()
The bridging header just has #import oclib.h, thus exposing the contents of both C and Objective-C headers to Swift.
The expected output:
Called the Swift callback!
The device type is 777
The device port is 1234
This is what I have so far and I want to save pcd file from it
I know I have to do something like this but not exactly sure
pcl::PointCloud::PointPointXYZRGBA> cloud;
pcl::io:;savePCDFileASCII("test.pcd",cloud);
what do i have to add in my current code that i will have test.pcd
Thanks
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/io/openni_grabber.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/common/time.h>
class SimpleOpenNIProcessor
{
public:
SimpleOpenNIProcessor () : viewer ("PCL OpenNI Viewer") {}
void cloud_cb_ (const pcl::PointCloud<pcl::PointXYZRGBA>::ConstPtr &cloud)
{
static unsigned count = 0;
static double last = pcl::getTime ();
if (++count == 30)
{
double now = pcl::getTime ();
std::cout << "distance of center pixel :" << cloud->points [(cloud->width >> 1) * (cloud->height + 1)].z << " mm. Average framerate: " << double(count)/double(now - last) << " Hz" << std::endl;
count = 0;
last = now;
}
if (!viewer.wasStopped())
viewer.showCloud (cloud);
}
void run ()
{
// create a new grabber for OpenNI devices
pcl::Grabber* interface = new pcl::OpenNIGrabber();
// make callback function from member function
boost::function<void (const pcl::PointCloud<pcl::PointXYZRGBA>::ConstPtr&)> f =
boost::bind (&SimpleOpenNIProcessor::cloud_cb_, this, _1);
// connect callback function for desired signal. In this case its a point cloud with color values
boost::signals2::connection c = interface->registerCallback (f);
// start receiving point clouds
interface->start ();
// wait until user quits program with Ctrl-C, but no busy-waiting -> sleep (1);
while (true)
boost::this_thread::sleep (boost::posix_time::seconds (1));
// stop the grabber
interface->stop ();
}
pcl::visualization::CloudViewer viewer;
};
int main ()
{
SimpleOpenNIProcessor v;
v.run ();
return (0);
}
#include <iostream>
#include <string>
#include <sstream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/io/openni_grabber.h>
#include <pcl/visualization/cloud_viewer.h>
using namespace std;
const string OUT_DIR = "D:\\frame_saver_output\\";
class SimpleOpenNIViewer
{
public:
SimpleOpenNIViewer () : viewer ("PCL Viewer")
{
frames_saved = 0;
save_one = false;
}
void cloud_cb_ (const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr &cloud)
{
if (!viewer.wasStopped()) {
viewer.showCloud (cloud);
if( save_one ) {
save_one = false;
std::stringstream out;
out << frames_saved;
std::string name = OUT_DIR + "cloud" + out.str() + ".pcd";
pcl::io::savePCDFileASCII( name, *cloud );
}
}
}
void run ()
{
pcl::Grabber* interface = new pcl::OpenNIGrabber();
boost::function<void (const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr&)> f =
boost::bind (&SimpleOpenNIViewer::cloud_cb_, this, _1);
interface->registerCallback (f);
interface->start ();
char c;
while (!viewer.wasStopped())
{
//sleep (1);
c = getchar();
if( c == 's' ) {
cout << "Saving frame " << frames_saved << ".\n";
frames_saved++;
save_one = true;
}
}
interface->stop ();
}
pcl::visualization::CloudViewer viewer;
private:
int frames_saved;
bool save_one;
};
int main ()
{
SimpleOpenNIViewer v;
v.run ();
return 0;
}
Here you go.