NesC (variant of C): implicit declaration of function startTimers() - nesc

I am compiling the following code (on a mote). And I have the following error:
line 174: implicit declaration of function 'startTimers'
Could you please help me?
Here is my code.
#include "Timer.h"
#include "Oscilloscope.h"
module Oscilloscope2C #safe(){
uses {
interface Boot;
interface SplitControl as RadioControl;
interface AMSend;
interface Receive;
//interface Timer<TMilli>;
interface Timer<TMilli> as Timer0;
interface Timer<TMilli> as Timer1;
interface Timer<TMilli> as Timer2;
//interface Read<uint16_t>;
interface Leds;
interface Read<uint16_t> as TempRead;
interface Read<uint16_t> as HumidtyRead;
interface Read<uint16_t> as LightRead;
//interface Read<uint16_t> as VoltageRead;
}
}
implementation{
message_t sendBuf;
bool sendBusy;
/* Current local state - interval, version and accumulated readings */
oscilloscope_t local;
uint8_t reading; /* 0 to NREADINGS */
/* When we head an Oscilloscope message, we check it's sample count. If
it's ahead of ours, we "jump" forwards (set our count to the received
count). However, we must then suppress our next count increment. This
is a very simple form of "time" synchronization (for an abstract
notion of time). */
bool suppressCountChange;
// Use LEDs to report various status issues.
void report_problem() { call Leds.led0Toggle(); }
void report_sent() { call Leds.led1Toggle(); }
void report_received() { call Leds.led2Toggle(); }
event void Boot.booted() {
//local.interval = DEFAULT_INTERVAL;
//local.id = TOS_NODE_ID;
if (call RadioControl.start() != SUCCESS)
report_problem();
}
event void Timer0.fired(){
//dbg("BlinkC", "Timer 0 fired # %s.\n", sim_time_string());
//call Leds.led0Toggle();
if (reading == NREADINGS){
if (!sendBusy && sizeof local <= call AMSend.maxPayloadLength()){
memcpy(call AMSend.getPayload(&sendBuf, sizeof(local)), &local, sizeof local);
if (call AMSend.send(AM_BROADCAST_ADDR, &sendBuf, sizeof local) == SUCCESS)
sendBusy = TRUE;
}
if (!sendBusy)
report_problem();
reading = 0;
if (!suppressCountChange)
local.count++;
suppressCountChange = FALSE;
}
if (call TempRead.read() != SUCCESS)
report_problem();
}
event void Timer1.fired(){
//dbg("BlinkC", "Timer 1 fired # %s \n", sim_time_string());
//call Leds.led1Toggle();
if (reading == NREADINGS){
if (!sendBusy && sizeof local <= call AMSend.maxPayloadLength()){
memcpy(call AMSend.getPayload(&sendBuf, sizeof(local)), &local, sizeof local);
if (call AMSend.send(AM_BROADCAST_ADDR, &sendBuf, sizeof local) == SUCCESS)
sendBusy = TRUE;
}
if (!sendBusy)
report_problem();
reading = 0;
if (!suppressCountChange)
local.count++;
suppressCountChange = FALSE;
}
if (call HumidtyRead.read() != SUCCESS)
report_problem();
//}
}
event void Timer2.fired(){
//dbg("BlinkC", "Timer 2 fired # %s.\n", sim_time_string());
//call Leds.led2Toggle();
if (reading == NREADINGS){
if (!sendBusy && sizeof local <= call AMSend.maxPayloadLength()){
memcpy(call AMSend.getPayload(&sendBuf, sizeof(local)), &local, sizeof local);
if (call AMSend.send(AM_BROADCAST_ADDR, &sendBuf, sizeof local) == SUCCESS)
sendBusy = TRUE;
}
if (!sendBusy)
report_problem();
reading = 0;
if (!suppressCountChange)
local.count++;
suppressCountChange = FALSE;
}
if (call LightRead.read() != SUCCESS)
report_problem();
//}
}
event void TempRead.readDone(error_t result, uint16_t data) {
//int32_t Temp;
if (result != SUCCESS){
data = 0xffff;
report_problem();
}
if (reading < NREADINGS)
local.readings[reading++] = data;
//Temp=-3960+(int32_t)data;
//if(Temp>=3000){
//remplacer par buzzer
//call Leds.led1On();
//call Leds.led2Off();
// }
//else{
//call Leds.led2On();
//call Leds.led1Off();
//}
}//end TempRead
event void HumidtyRead.readDone(error_t result, uint16_t data) {
//int32_t Humidity;
//int32_t RH;
//int32_t Temp;
//int32_t Temp_25;
if (result != SUCCESS){
data = 0xffff;
report_problem();
}
if (reading < NREADINGS)
local.readings[reading++] = data;
//Humidity=-2.0468+0.0367*(int32_t)data-0.0000015955*((int32_t)data*(int32_t)data);
//Temp= 0.01*data -39.60;
//Temp_25 = Temp-25;
//RH = Temp_25*(0.01+0.00008*(int32_t)data)+Humidity;
//if (RH > 100) report_problem();
}//end HumidityRead
event void LightRead.readDone(error_t result, uint16_t data) {
//int32_t Light;
if (result != SUCCESS){
data = 0xffff;
report_problem();
}
if (reading < NREADINGS)
local.readings[reading++] = data;
}//end LightRead
event void RadioControl.startDone(error_t error) {
startTimers();
}
event void RadioControl.stopDone(error_t error) {
}
void startTimers() {
//call Timer.startPeriodic(local.interval);
call Timer0.startPeriodic(250);
call Timer1.startPeriodic(500);
call Timer2.startPeriodic(1000);
reading = 0;
}
event void AMSend.sendDone(message_t* msg, error_t error) {
if (error == SUCCESS)
report_sent();
else
report_problem();
sendBusy = FALSE;
}
event message_t* Receive.receive(message_t* msg, void* payload, uint8_t len) {
oscilloscope_t *omsg = payload;
report_received();
/* If we receive a newer version, update our interval.
If we hear from a future count, jump ahead but suppress our own change
*/
if (omsg->version > local.version)
{
local.version = omsg->version;
local.interval = omsg->interval;
startTimers();
}
if (omsg->count > local.count)
{
local.count = omsg->count;
suppressCountChange = TRUE;
}
return msg;
}
/*
void startTimer() {
call Timer.startPeriodic(local.interval);
reading = 0;
}
event void RadioControl.startDone(error_t error) {
startTimer();
}
event void RadioControl.stopDone(error_t error) {}
event message_t* Receive.receive(message_t* msg, void* payload, uint8_t len){
oscilloscope_t *omsg = payload;
report_received();
if (omsg->version > local.version){
local.version = omsg->version;
local.interval = omsg->interval;
startTimer();
}
if (omsg->count > local.count)
{
local.count = omsg->count;
suppressCountChange = TRUE;
}
return msg;
}
event void Timer.fired() {
if (reading == NREADINGS){
if (!sendBusy && sizeof local <= call AMSend.maxPayloadLength()){
// Don't need to check for null because we've already checked length
// above
memcpy(call AMSend.getPayload(&sendBuf, sizeof(local)), &local, sizeof local);
if (call AMSend.send(AM_BROADCAST_ADDR, &sendBuf, sizeof local) == SUCCESS)
sendBusy = TRUE;
}
if (!sendBusy)
report_problem();
reading = 0;
if (!suppressCountChange)
local.count++;
suppressCountChange = FALSE;
}
// if (call Read.read() != SUCCESS)
// report_problem();
if (call TempRead.read() != SUCCESS)
report_problem();
if (call HumidityRead.read() != SUCCESS)
report_problem();
if (call LightRead.read() != SUCCESS)
report_problem();
}
event void AMSend.sendDone(message_t* msg, error_t error) {
if (error == SUCCESS)
report_sent();
else
report_problem();
sendBusy = FALSE;
}
event void Read.readDone(error_t result, uint16_t data) {
if (result != SUCCESS){
data = 0xffff;
report_problem();
}
if (reading < NREADINGS)
local.readings[reading++] = data;
}
event void TempRead.readDone(error_t result, uint16_t data) {
int32_t Temp;
if (result != SUCCESS){
data = 0xffff;
report_problem();
}
if (reading < NREADINGS)
local.readings[reading++] = data;
Temp=-3960+(int32_t)data;
if(Temp>=3000){
//remplacer par buzzer
call Leds.led1On();
call Leds.led2Off();
}else{
call Leds.led2On();
call Leds.led1Off();
}
}
event void HumidityRead.readDone(error_t result, uint16_t data) {
int32_t Humidity;
int32_t RH;
int32_t Temp;
int32_t Temp_25;
if (result != SUCCESS){
data = 0xffff;
report_problem();
}
if (reading < NREADINGS)
local.readings[reading++] = data;
Humidity=-2.0468+0.0367*(int32_t)data-0.0000015955*((int32_t)data*(int32_t)data);
Temp= 0.01*data -39.60;
Temp_25 = Temp-25;
RH = Temp_25*(0.01+0.00008*(int32_t)data)+Humidity;
if (RH > 100) report_problem();
}
event void LightRead.readDone(error_t result, uint16_t data) {
int32_t Light;
if (result != SUCCESS){
data = 0xffff;
report_problem();
}
if (reading < NREADINGS)
local.readings[reading++] = data;
}
event void VoltageRead.readDone(error_t result, uint16_t data) {
if (result != SUCCESS){
data = 0xffff;
report_problem();
}
if (reading < NREADINGS)
local.readings[reading++] = data;
}
*/
}
Many thanks in advance for your help !
Tom

The function startTimers is called in RadioControl.startDone before it is declared and defined. Just move the definition of startTimers higher, before it is used in the code.

Related

3x Arduino + ESP32 with ESP-NOW

The configuration on "machine" looks like this:
Computer with custom app WPF C#, have connected ESP32-sender.
On other side of room there is ESP32 as recaiver that have connected to it 3 arduino due, that controls a lot of stepper motors.
My problem is, that if I send a single command all works great. Directly on other side it executes.
The problem starts when there is a lot of commands to send. ESP32 starts to makes Delivery fail status and after that moment all further commands are lost.
That's the code of Master-ESP connected to PC.
Which arduino to send is decide by one of the symbols on begin of message "!,#,#"
#include <esp_now.h>
#include <WiFi.h>
#include <Wire.h>
uint8_t broadcastAddress[] = {0x94, 0xB9, 0x7E, 0xD0, 0x93, 0x64};
String inMess;
typedef struct Message {
char a[100];
} Message;
// Variable to store if sending data was successful
String success;
Message message;
Message send_message;
esp_now_peer_info_t peerInfo;
char incoming;
String full = "";
bool text_done = false;
bool sended = false;
// Callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
//Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success*" : "Delivery Fail*");
if (status ==0){
success = "Delivery Success :)";
text_done = false;
full = "";
sended = false;
}
else{
success = "Delivery Fail :(";
delay(10);
sended = false;
Serial.println(success);
}
}
// Callback when data is received
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
memcpy(&message, incomingData, sizeof(message));
Serial.println(message.a);
}
void setup() {
// Init Serial Monitor
Serial.begin(115200);
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
Serial.println(WiFi.macAddress());
// Init ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
esp_now_register_send_cb(OnDataSent);
// Register peer
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
// Add peer
if (esp_now_add_peer(&peerInfo) != ESP_OK){
Serial.println("Failed to add peer");
return;
}
esp_now_register_recv_cb(OnDataRecv);
}
void loop() {
if(Serial.available() > 0 && !text_done){
incoming = Serial.read();
if(incoming == '\n'){
text_done = true;
full.trim();
full.toUpperCase();
}
if(text_done == false){
full += incoming;
}
}
if(text_done){
if(full[0] != '!' && full[0] != '#' && full[0] != '#'){ //check if text is worth sending to other esp
text_done = false;
full = "";
}
}
if(text_done){
if(!sended){
full.toCharArray(send_message.a,sizeof(send_message));
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &send_message, sizeof(send_message));
if (result == ESP_OK) {
//Serial.println("Sent success*");
sended = true;
}
else {
Serial.println("Error sending*");
delay(10);
}
}
}
}
That's the code of recaiver ESP
#include <esp_now.h>
#include <WiFi.h>
#include <SoftwareSerial.h>
#include <Wire.h>
SoftwareSerial worker;
SoftwareSerial tool;
//Serial2 is Table, software serials are others
uint8_t broadcastAddress[] = {0x7C, 0x9E, 0xBD, 0x4B, 0x47, 0xA4};
String outMess;
String outMessTable;
String outMessTool;
String inMess;
typedef struct Message {
char a[100];
} Message;
String success;
Message message;
Message send_message;
bool sended = true;
String again_message = "";
esp_now_peer_info_t peerInfo;
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
//Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
if (status == 0) {
success = "Delivery Success :)";
sended = true;
again_message = "";
}
else {
success = "Delivery Fail :(";
sended = false;
delay(5);
}
}
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
memcpy(&message, incomingData, sizeof(message));
Serial.println(message.a);
sendTo((String)message.a);
}
void setup() {
// Init Serial Monitor
Serial.begin(115200);
Serial2.begin(115200, SERIAL_8N1, 16, 17);
//rx tx
worker.begin(57600, SWSERIAL_8N1, 25, 26, false);
if (!worker) {
Serial.println("Invalid SoftwareSerial pin configuration, check config");
while (1) {
delay (1000);
}
}
//rx tx
tool.begin(57600, SWSERIAL_8N1, 32, 33, false);
if (!tool) {
Serial.println("Invalid SoftwareSerial pin configuration, check config");
while (1) {
delay (1000);
}
}
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
Serial.println(WiFi.macAddress());
// Init ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
// Once ESPNow is successfully Init, we will register for Send CB to
// get the status of Trasnmitted packet
esp_now_register_send_cb(OnDataSent);
// Register peer
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
// Add peer
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
Serial.println("Failed to add peer");
return;
}
// Register for a callback function that will be called when data is received
esp_now_register_recv_cb(OnDataRecv);
}
void loop() {
// Set values to send
if(sended){
if (worker.available() > 0) {//!
char t = worker.read();
if(t == '\n'){
outMess.trim();
sendToEsp(outMess,"!");
outMess = "";
}
else{
outMess += t;
}
}
}else{
if(again_message.length()>0){
delay(10);
sendToEsp(again_message.substring(1),again_message.substring(0,1));
}else{
sended = true;
}
}
if(sended){
if (tool.available() > 0) {//#
char t = tool.read();
if(t == '\n'){
outMessTool.trim();
sendToEsp(outMessTool,"#");
outMessTool = "";
}
else{
outMessTool += t;
}
}
}else{
if(again_message.length()>0){
delay(10);
sendToEsp(again_message.substring(1),again_message.substring(0,1));
}else{
sended = true;
}
}
if(sended){
if (Serial2.available() > 0) { //#
char t = Serial2.read();
if(t == '\n'){
outMessTable.trim();
sendToEsp(outMessTable,"#");
outMessTable = "";
}else{
outMessTable += t;
}
}
}else{
if(again_message.length()>0){
delay(10);
sendToEsp(again_message.substring(1),again_message.substring(0,1));
}else{
sended = true;
}
}
if(Serial.available() > 0){
outMess = Serial.readStringUntil('\n'); //read command from pc
outMess.trim(); // remove uneccesery spaces
sendTo(outMess);
}
}
void sendToEsp(String text, String which){
String mess = which + text;
again_message = mess;
mess.toCharArray(send_message.a,sizeof(send_message));
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &send_message, sizeof(send_message));
if (result == ESP_OK) {
// Serial.println("Sent with success");
}
else {
Serial.println("Error sending the data");
sended = true;
again_message = "";
}
}
void sendTo(String outMess){
Serial.print("=");
Serial.print(outMess);
if(outMess[0] == '!'){ //worker
worker.enableTx(true);
worker.println(outMess.substring(1));
worker.enableTx(false);
Serial.println("send to worker");
}
if(outMess[0] == '#') {//table
Serial2.println(outMess.substring(1));
Serial.println("send to table");
}
if(outMess[0] == '#'){ //tool
tool.enableTx(true);
tool.println(outMess.substring(1));
tool.enableTx(false);
Serial.println("send to tool");
}
}
If I send one message with a delay of writing next one by hand it works great.
If the message is sended via c# very fast, esp32 is handling first few of them then losing on sending or recaiving, sometimes both of esp32 sometimes only one.
How could I prevent that to make stable connection ?

In Visual Studio, After passing parameters by pointer or reference of visual studio function, the access parameter value in the function is null

This is the main code where error happened:
void VRInputRemapping::DigitalInputConfig() {
bool orginput = this->curVRConfig->GetorgDigitalInput();
uint32_t deviceId = this->curVRConfig->GetcurDeviceId();
uint32_t buttonId = this->curVRConfig->GetcurButtonId();
printf("Digital orginput: %d device:%d butonid:%d\n", orginput, deviceId, buttonId);
vrinputemulator::DigitalInputRemapping* m_currentRemapping =new vrinputemulator::DigitalInputRemapping(true);
vrinputemulator::DigitalBinding* m_currentBinding = new vrinputemulator::DigitalBinding;
if (orginput == false) {
m_currentBinding->type = vrinputemulator::DigitalBindingType::Disabled;
memset(&m_currentBinding->data, 0, sizeof(m_currentBinding->data));
m_currentBinding->data.openvr.buttonId = buttonId;
m_currentBinding->toggleEnabled = false;
m_currentBinding->toggleDelay = 0;
m_currentBinding->autoTriggerEnabled = false;
m_currentBinding->autoTriggerFrequency = 1;
m_currentRemapping->binding = *m_currentBinding;
}
else if (orginput == true) {
m_currentBinding->type = vrinputemulator::DigitalBindingType::NoRemapping;
memset(&m_currentBinding->data, 0, sizeof(m_currentBinding->data));
m_currentBinding->data.openvr.buttonId = buttonId;
m_currentBinding->toggleEnabled = false;
m_currentBinding->toggleDelay = 0;
m_currentBinding->autoTriggerEnabled = false;
m_currentBinding->autoTriggerFrequency = 1;
m_currentRemapping->binding = *m_currentBinding;
}
printf(" void VRInputRemapping::DigitalInputConfig() m_currentRemapping->binding.type:%d device:%d buttonId:%d remapping.data.openvr.buttonId%d\n", m_currentRemapping->binding.type, deviceId, buttonId, m_currentRemapping->binding.data.openvr.buttonId);
//<--------------------Here is the function call-------------------------------------->
this->inputEmulator->setDigitalInputRemapping(deviceId, buttonId, m_currentRemapping);
//<--------------------Here is the function call-------------------------------------->
printf(" void VRInputRemapping::DigitalInputConfig() m_currentRemapping->binding.type:%d device:%d buttonId:%d remapping.data.openvr.buttonId%d\n", m_currentRemapping->binding.type, deviceId, buttonId, m_currentRemapping->binding.data.openvr.buttonId);
delete m_currentBinding;
delete m_currentRemapping;
Sleep(1);
}
This is the Function:
void VRInputEmulator::setDigitalInputRemapping(uint32_t deviceId, uint32_t buttonId, DigitalInputRemapping* remapping, bool modal) {
printf("VRInputEmulator::setDigitalInputRemapping Digital orginput: %d device:%d butonid:%d remapping->binding.data.openvr.buttonId%d\n", remapping->binding.type, deviceId, buttonId, remapping->binding.data.openvr.buttonId);
if (_ipcServerQueue) {
ipc::Request message(ipc::RequestType::InputRemapping_SetDigitalRemapping);
memset(&message.msg, 0, sizeof(message.msg));
message.msg.ir_SetDigitalRemapping.clientId = m_clientId;
message.msg.ir_SetDigitalRemapping.messageId = 0;
message.msg.ir_SetDigitalRemapping.controllerId = deviceId;
message.msg.ir_SetDigitalRemapping.buttonId = buttonId;
message.msg.ir_SetDigitalRemapping.remapData = *remapping;
if (modal) {
uint32_t messageId = _ipcRandomDist(_ipcRandomDevice);
message.msg.ir_SetDigitalRemapping.messageId = messageId;
std::promise<ipc::Reply> respPromise;
auto respFuture = respPromise.get_future();
{
std::lock_guard<std::recursive_mutex> lock(_mutex);
_ipcPromiseMap.insert({ messageId, std::move(respPromise) });
}
_ipcServerQueue->send(&message, sizeof(ipc::Request), 0);
auto resp = respFuture.get();
{
std::lock_guard<std::recursive_mutex> lock(_mutex);
_ipcPromiseMap.erase(messageId);
}
std::stringstream ss;
ss << "Error while setting digital input remapping: ";
if (resp.status == ipc::ReplyStatus::InvalidId) {
ss << "Invalid device id";
throw vrinputemulator_invalidid(ss.str(), (int)resp.status);
} else if (resp.status == ipc::ReplyStatus::NotFound) {
ss << "Device not found";
throw vrinputemulator_notfound(ss.str(), (int)resp.status);
} else if (resp.status != ipc::ReplyStatus::Ok) {
ss << "Error code " << (int)resp.status;
throw vrinputemulator_exception(ss.str(), (int)resp.status);
}
} else {
_ipcServerQueue->send(&message, sizeof(ipc::Request), 0);
}
} else {
throw vrinputemulator_connectionerror("No active connection.");
}
}
You can See the Printf I had use before and after the function call, also used in the head of the dunction,This is the output:
void VRInputRemapping::DigitalInputConfig() m_currentRemapping->binding.type:0 device:1 buttonId:32 remapping.data.openvr.buttonId: 32
VRInputEmulator::setDigitalInputRemapping Digital orginput: 0 device:1 butonid:32 remapping->binding.data.openvr.buttonId: 0
void VRInputRemapping::DigitalInputConfig() m_currentRemapping->binding.type 0 device:1 buttonId:32 remapping.data.openvr.buttonId: 32
The output in the consloe

IOCP Socket Worker threads works too much slow when i test it with client simulator

I'm developing a mmorpg with iocp sockets.When im testing with my client simulator , after 70-80 connection , writing operation at socket worker thread slowing down than users get lagged.
This is my worker thread ;
typedef void(*OperationHandler)(Socket * s, uint32 len);
void HandleReadComplete(Socket * s, uint32 len);
void HandleWriteComplete(Socket * s, uint32 len);
void HandleShutdown(Socket * s, uint32 len);
static OperationHandler ophandlers[] =
{
&HandleReadComplete,
&HandleWriteComplete,
&HandleShutdown
};
uint32 THREADCALL SocketMgr::SocketWorkerThread(void * lpParam){
SocketMgr *socketMgr = (SocketMgr *)lpParam;
HANDLE cp = socketMgr->GetCompletionPort();
DWORD len;
Socket * s = nullptr;
OverlappedStruct * ov = nullptr;
LPOVERLAPPED ol_ptr;
while (socketMgr->m_bWorkerThreadsActive)
{
if (!GetQueuedCompletionStatus(cp, &len, (LPDWORD)&s, &ol_ptr, INFINITE))
{
if (s != nullptr)
s->Disconnect();
continue;
}
ov = CONTAINING_RECORD(ol_ptr, OverlappedStruct, m_overlap);
if (ov->m_event == SOCKET_IO_THREAD_SHUTDOWN)
{
delete ov;
return 0;
}
if (ov->m_event < NUM_SOCKET_IO_EVENTS)
ophandlers[ov->m_event](s, len);
}
return 0;}
This is Write Complete event handler;
void HandleWriteComplete(Socket * s, uint32 len)
{
if (s->IsDeleted()) {
return;
}
s->m_writeEvent.Unmark();
s->BurstBegin(); // Lock
s->GetWriteBuffer().Remove(len);
TRACE("SOCK = %d removed = %d",s->GetFd(),len);
if (s->GetWriteBuffer().GetContiguousBytes() > 0) {
s->WriteCallback();
}
else {
s->DecSendLock();
}
s->BurstEnd(); // Unlock
}
WriteCallBack function ;
void Socket::WriteCallback()
{
if (IsDeleted() || !IsConnected()) {
return;
}
// We don't want any writes going on while this is happening.
Guard lock(m_writeMutex);
if(writeBuffer.GetContiguousBytes())
{
DWORD w_length = 0;
DWORD flags = 0;
// attempt to push all the data out in a non-blocking fashion.
WSABUF buf;
buf.len = (ULONG)writeBuffer.GetContiguousBytes();
buf.buf = (char*)writeBuffer.GetBufferStart();
m_writeEvent.Mark();
m_writeEvent.Reset(SOCKET_IO_EVENT_WRITE_END);
TRACE("\n SOCK = %d aslında giden = %X THREADID = %d", GetFd(), buf.buf[4],GetCurrentThreadId());
int r = WSASend(m_fd, &buf, 1, &w_length, flags, &m_writeEvent.m_overlap, 0);
if (r == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING)
{
m_writeEvent.Unmark();
DecSendLock();
Disconnect();
}
}
else
{
// Write operation is completed.
DecSendLock();
}
}
this is stuffs about mutexes ;
public:
/* Atomic wrapper functions for increasing read/write locks */
INLINE void IncSendLock() { Guard lock(m_writeMutex); ++m_writeLock; }
INLINE void DecSendLock() { Guard lock(m_writeMutex);
--m_writeLock; }
INLINE bool HasSendLock() {Guard lock(m_writeMutex); return (m_writeLock != 0); }
INLINE bool AcquireSendLock()
{
Guard lock(m_writeMutex);
if (m_writeLock != 0)
return false;
++m_writeLock;
return true;
}
private:
// Write lock, stops multiple write events from being posted.
uint32 m_writeLock;
std::recursive_mutex m_writeLockMutex;
This is read event handler;
void HandleReadComplete(Socket * s, uint32 len)
{
if (s->IsDeleted())
return;
s->m_readEvent.Unmark();
if (len)
{
s->GetReadBuffer().IncrementWritten(len);
s->OnRead();
s->SetupReadEvent();
}
else
{
// s->Delete(); // Queue deletion.
s->Disconnect();
}
}
OnRead function ;
void KOSocket::OnRead()
{
Packet pkt;
for (;;)
{
if (m_remaining == 0)
{
if (GetReadBuffer().GetSize() < 5) {
//TRACE("pkt returnzzz GetFd %d", GetFd());
return; //check for opcode as well
}
uint16 header = 0;
GetReadBuffer().Read(&header, 2);
//printf("header : %X", header);//derle at k
if (header != 0x55AA)
{
TRACE("%s: Got packet without header 0x55AA, got 0x%X\n", GetRemoteIP().c_str(), header);
goto error_handler;
}
GetReadBuffer().Read(&m_remaining, 2);
if (m_remaining == 0)
{
TRACE("%s: Got packet without an opcode, this should never happen.\n", GetRemoteIP().c_str());
goto error_handler;
}
}
if (m_remaining > GetReadBuffer().GetAllocatedSize())
{
TRACE("%s: Packet received which was %u bytes in size, maximum of %u.\n", GetRemoteIP().c_str(), m_remaining, GetReadBuffer().GetAllocatedSize());
goto error_handler;
}
if (m_remaining > GetReadBuffer().GetSize())
{
if (m_readTries > 4)
{
TRACE("%s: packet fragmentation count is over 4, disconnecting as they're probably up to something bad\n", GetRemoteIP().c_str());
goto error_handler;
}
m_readTries++;
return;
}
uint8 *in_stream = new uint8[m_remaining];
m_readTries = 0;
GetReadBuffer().Read(in_stream, m_remaining);
uint16 footer = 0;
GetReadBuffer().Read(&footer, 2);
if (footer != 0xAA55
|| !DecryptPacket(in_stream, pkt))
{
TRACE("%s: Footer invalid (%X) or failed to decrypt.\n", GetRemoteIP().c_str(), footer);
delete [] in_stream;
goto error_handler;
}
delete [] in_stream;
// Update the time of the last (valid) response from the client.
m_lastResponse = UNIXTIME2;
//TRACE("pkt:%d GetFd %d", pkt.GetOpcode(), GetFd());
if (!HandlePacket(pkt))
{
TRACE("%s: Handler for packet %X returned false\n", GetRemoteIP().c_str(), pkt.GetOpcode());
#ifndef _DEBUG
goto error_handler;
#endif
}
m_remaining = 0;
}
//TRACE("pkt return11 GetFd %d", GetFd());
return;
error_handler:
Disconnect();
}
and this is how my server sends to packet to clients ;
BurstBegin();
//TRACE("\n SOCK = %d FREE SPACE = %d ",GetFd(),GetWriteBuffer().GetSpace()/*, GetWriteBuffer().m_writeLock*/);
if (GetWriteBuffer().GetSpace() < size_t(len + 6))
{
size_t freespace = GetWriteBuffer().GetSpace();
BurstEnd();
Disconnect();
return false;
}
TRACE("\n SOCK = %d gitmesi gereken paket = %X THREADID = %d", GetFd(), out_stream[0],GetCurrentThreadId());
r = BurstSend((const uint8*)"\xaa\x55", 2);
if (r) r = BurstSend((const uint8*)&len, 2);
if (r) r = BurstSend((const uint8*)out_stream, len);
if (r) r = BurstSend((const uint8*)"\x55\xaa", 2);
if (r) BurstPush();
BurstEnd();
The number of Worker threads according to processor number;
for(int i = 0; i < numberOfWorkerThreads; i++)
{
m_thread[i] = new Thread(SocketWorkerThread, this);
}
The server which i tested on it has Intel XEON E5-2630 v3 2.40 ghz (2 processor)
Can you guys help me about how can i improve performance ? For ex: when the client moves in the map per 1.5 second that sends to move packet to server and if it success server sends to every client in that region that move packet.
When client count increasing server starts to slow down to send back to packets to clients.My write buffer filling fully (it's capacity 16384 bytes) cause server couldn't send packets inside of the writebuffer.

Memory usage with IOCP [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I am converting our code to use IOCP and I got the communication relatively stable, but the memory usage of the application is increasing. Looks like I am getting back (on completion function calls) much fewer objects of OverlappedEx than I create. My code is below. What am I doing wrong?
#ifndef NETWORK_DATA
#define NETWORK_DATA
#include <afxwin.h>
#include <vector>
#include <string>
#include "CriticalSectionLocker.h"
using namespace std;
DWORD NetworkManager::NetworkThread(void* param)
{
bool bRun = true;
while (bRun)
{
DWORD wait = ::WaitForSingleObject(CCommunicationManager::s_hShutdownEvent, 0);
if (WAIT_OBJECT_0 == wait)
{
bRun = false;
DEBUG_LOG0("Shutdown event was signalled thread");
}
else
{
DWORD dwBytesTransfered = 0;
void* lpContext = nullptr;
OVERLAPPED* pOverlapped = nullptr;
BOOL bReturn = GetQueuedCompletionStatus(s_IOCompletionPort,
&dwBytesTransfered,
(LPDWORD)&lpContext,
&pOverlapped,
INFINITE);
if (nullptr == lpContext)
{
DEBUG_LOG0("invalid context");
/*continue;*/
}
else
{
if (bReturn && dwBytesTransfered > 0)
{
OverlappedEx* data = reinterpret_cast<OverlappedEx*>(pOverlapped);
ServerData* networkData = reinterpret_cast<ServerData*>(lpContext);
if (networkData && data)
{
switch(data->m_opType)
{
case OverlappedEx::OP_READ:
/*DEBUG_LOG4("device name: %s bytes received: %d socket: %d handle: %d",
networkData->Name().c_str(), dwBytesTransfered, networkData->Socket(), networkData->Handle());*/
networkData->CompleteReceive(dwBytesTransfered, data);
break;
case OverlappedEx::OP_WRITE:
/*DEBUG_LOG4("device name: %s bytes sent: %d socket: %d handle: %d",
networkData->Name().c_str(), dwBytesTransfered, networkData->Socket(), networkData->Handle());*/
networkData->CompleteSend(dwBytesTransfered, data);
break;
}
}
}
else
{
/*DEBUG_LOG2("GetQueuedCompletionStatus failed: bReturn: %d dwBytesTransferred: %u", bReturn, dwBytesTransfered);*/
}
}
}
}
return 0;
}
enum NetworkType
{
UDP,
TCP
};
struct OverlappedEx : public OVERLAPPED
{
enum OperationType
{
OP_READ,
OP_WRITE
};
const static int MAX_PACKET_SIZE = 2048;
WSABUF m_wBuf;
char m_buffer[MAX_PACKET_SIZE];
OperationType m_opType;
OverlappedEx()
{
Clear();
m_refCount = 1;
}
void AddRef()
{
::InterlockedIncrement(&m_refCount);
}
void Release()
{
::InterlockedDecrement(&m_refCount);
}
int Refcount() const
{
return InterlockedExchangeAdd((unsigned long*)&m_refCount, 0UL);
}
~OverlappedEx()
{
Clear();
}
void Clear()
{
memset(m_buffer, 0, MAX_PACKET_SIZE);
m_wBuf.buf = m_buffer;
m_wBuf.len = MAX_PACKET_SIZE;
Internal = 0;
InternalHigh = 0;
Offset = 0;
OffsetHigh = 0;
hEvent = nullptr;
m_opType = OP_READ;
}
private:
volatile LONG m_refCount;
};
class ServerData
{
public:
const static int MAX_REVEIVE_QUEUE_SIZE = 100;
const static int MAX_PACKET_SIZE = 2048;
const static int MAX_SEND_QUEUE_SIZE = 10;
const static int MAX_RECEIVE_QUEUE_SIZE = 100;
const static int MAX_OVERLAPPED_STRUCTS = 20;
ServerData(NetworkType netType, const string& sName, CCommunicationManager::CommHandle handle,
SOCKET sock, HANDLE IOPort) :
m_sName(sName)
{
InitializeCriticalSection(&m_receiveQueLock);
InitializeCriticalSection(&m_objectLock);
m_Handle = handle;
m_Socket = sock;
m_nIPAddress = 0;
m_netType = netType;
m_bEnabled = true;
m_ovlpIndex = 0;
for (int i = 0; i < MAX_OVERLAPPED_STRUCTS; ++i)
{
m_olps.push_back(new OverlappedEx);
}
/* Associate socket with completion handle */
if (m_Socket != 0)
{
CreateIoCompletionPort( reinterpret_cast<HANDLE>(m_Socket), IOPort, reinterpret_cast<ULONG_PTR>(this), 0 );
}
}
~ServerData()
{
CriticalSectionLocker lock(&m_receiveQueLock);
DeleteCriticalSection(&m_receiveQueLock);
DeleteCriticalSection(&m_objectLock);
closesocket(m_Socket);
}
const string& Name() const { return m_sName; }
bool Enabled() const { return m_bEnabled; }
void SetEnabled(bool bEnabled)
{
m_bEnabled = bEnabled;
}
int Handle() const { return m_Handle; }
void SetHandle(int handle)
{
m_Handle = handle;
}
unsigned long IPAddress() const { return m_nIPAddress; }
SOCKET Socket() const
{
return m_Socket;
}
void SetSocket(SOCKET sock)
{
m_Socket = sock;
}
void SetIPAddress(unsigned long nIP)
{
m_nIPAddress = nIP;
}
bool ValidTelegram(const vector<char>& telegram) const
{
return false;
}
OverlappedEx* GetBuffer()
{
OverlappedEx* ret = nullptr;
if (!m_olps.empty())
{
ret = m_olps.front();
m_olps.pop_front();
}
return ret;
}
void CompleteReceive(size_t numBytes, OverlappedEx* data)
{
//DEBUG_LOG1("%d buffers are available", AvailableBufferCount());
if (numBytes > 0)
{
vector<char> v(data->m_buffer, data->m_buffer + numBytes);
ReceivedData rd;
rd.SetData(v);
EnqueReceiveMessage(rd);
}
data->Release();
{
CriticalSectionLocker lock(&m_objectLock);
m_olps.push_back(data);
// DEBUG_LOG1("Queue size: %d", m_olps.size());
}
StartReceiving();
}
void CompleteSend(size_t numBytes, OverlappedEx* data)
{
data->Release();
{
CriticalSectionLocker lock(&m_objectLock);
m_olps.push_back(data);
//DEBUG_LOG1("Queue size: %d", m_olps.size());
}
//DEBUG_LOG2("Object: %s num sent: %d", Name().c_str(), numBytes);
}
void StartReceiving()
{
DWORD bytesRecv = 0;
sockaddr_in senderAddr;
DWORD flags = 0;
int senderAddrSize = sizeof(senderAddr);
int rc = 0;
CriticalSectionLocker lock(&m_objectLock);
auto olp = GetBuffer();
if (!olp)
{
if (...)
{
m_olps.push_back(new OverlappedEx);
olp = GetBuffer();
}
else
{
if (...)
{
DEBUG_LOG1("Name: %s ************* NO AVAILABLE BUFFERS - bailing ***************", Name().c_str());
}
return;
}
}
olp->Clear();
olp->m_opType = OverlappedEx::OP_READ;
olp->AddRef();
switch(GetNetworkType())
{
case UDP:
{
rc = WSARecvFrom(Socket(),
&olp->m_wBuf,
1,
&bytesRecv,
&flags,
(SOCKADDR *)&senderAddr,
&senderAddrSize, (OVERLAPPED*)olp, NULL);
}
break;
case TCP:
{
rc = WSARecv(Socket(),
&olp->m_wBuf,
1,
&bytesRecv,
&flags,
(OVERLAPPED*)olp, NULL);
}
break;
}
if (SOCKET_ERROR == rc)
{
DWORD err = WSAGetLastError();
if (err != WSA_IO_PENDING)
{
olp->Release();
m_olps.push_back(olp);
}
}
}
void SetWriteBuf(const SendData& msg, OverlappedEx* data)
{
int len = min(msg.Data().size(), MAX_PACKET_SIZE);
memcpy(data->m_buffer, &msg.Data()[0], len);
data->m_wBuf.buf = data->m_buffer;
data->m_wBuf.len = len;
}
void StartSending(const SendData& msg)
{
DEBUG_LOG1("device name: %s", Name().c_str());
int rc = 0;
DWORD bytesSent = 0;
DWORD flags = 0;
SOCKET sock = Socket();
int addrSize = sizeof(sockaddr_in);
CriticalSectionLocker lock(&m_objectLock);
//UpdateOverlapped(OverlappedEx::OP_WRITE);
auto olp = GetBuffer();
if (!olp)
{
if (...)
{
m_olps.push_back(new OverlappedEx);
olp = GetBuffer();
DEBUG_LOG2("name: %s ************* NO AVAILABLE BUFFERS new size: %d ***************", Name().c_str(), m_olps.size());
}
else
{
if (...)
{
DEBUG_LOG1("Name: %s ************* NO AVAILABLE BUFFERS - bailing ***************", Name().c_str());
}
return;
}
}
olp->Clear();
olp->m_opType = OverlappedEx::OP_WRITE;
olp->AddRef();
SetWriteBuf(msg, olp);
switch(GetNetworkType())
{
case UDP:
rc = WSASendTo(Socket(), &olp->m_wBuf, 1,
&bytesSent, flags, (sockaddr*)&msg.SendAddress(),
addrSize, (OVERLAPPED*)olp, NULL);
break;
case TCP:
rc = WSASend(Socket(), &olp->m_wBuf, 1,
&bytesSent, flags, (OVERLAPPED*)olp, NULL);
break;
}
if (SOCKET_ERROR == rc)
{
DWORD err = WSAGetLastError();
if (err != WSA_IO_PENDING)
{
olp->Release();
m_olps.push_back(olp);
}
}
}
size_t ReceiveQueueSize()
{
CriticalSectionLocker lock(&m_receiveQueLock);
return m_receiveDataQueue.size();
}
void GetAllData(vector <ReceivedData> & data)
{
CriticalSectionLocker lock(&m_receiveQueLock);
while (m_receiveDataQueue.size() > 0)
{
data.push_back(m_receiveDataQueue.front());
m_receiveDataQueue.pop_front();
}
}
void DequeReceiveMessage(ReceivedData& msg)
{
CriticalSectionLocker lock(&m_receiveQueLock);
if (m_receiveDataQueue.size() > 0)
{
msg = m_receiveDataQueue.front();
m_receiveDataQueue.pop_front();
}
}
template <class T>
void EnqueReceiveMessage(T&& data)
{
CriticalSectionLocker lock(&m_receiveQueLock);
if (m_receiveDataQueue.size() <= MAX_RECEIVE_QUEUE_SIZE)
{
m_receiveDataQueue.push_back(data);
}
else
{
static int s_nLogCount = 0;
if (s_nLogCount % 100 == 0)
{
DEBUG_LOG2("Max queue size was reached handle id: %d in %s", Handle(), Name().c_str());
}
s_nLogCount++;
}
}
NetworkType GetNetworkType() const
{
return m_netType;
}
private:
ServerData(const ServerData&);
ServerData& operator=(const ServerData&);
private:
bool m_bEnabled; //!< This member flags if this reciever is enabled for receiving incoming connections.
int m_Handle; //!< This member holds the handle for this receiver.
SOCKET m_Socket; //!< This member holds the socket information for this receiver.
unsigned long m_nIPAddress; //!< This member holds an IP address the socket is bound to.
deque < ReceivedData > m_receiveDataQueue;
CRITICAL_SECTION m_receiveQueLock;
CRITICAL_SECTION m_objectLock;
string m_sName;
NetworkType m_netType;
deque<OverlappedEx*> m_olps;
size_t m_ovlpIndex;
};
#endif
your implementation of void Release() have no sense - you decrement m_refCount and so what ? must be
void Release()
{
if (!InterlockedDecrement(&m_refCount)) delete this;
}
as result you never free OverlappedEx* data - this what i just view and this give memory leak.
also can advice - use WaitForSingleObject(CCommunicationManager::s_hShutdownEvent, 0); this is bad idea for detect shutdown. call only GetQueuedCompletionStatus and for shutdown call PostQueuedCompletionStatus(s_IOCompletionPort, 0, 0, 0) several times(number or threads listen on s_IOCompletionPort) and if thread view pOverlapped==0 - just exit.
use
OverlappedEx* data = static_cast<OverlappedEx*>(pOverlapped);
instead of reinterpret_cast
make ~OverlappedEx() private - it must not be direct called, only via Release
olp->Release();
m_olps.push_back(olp);
after you call Release() on object you must not it more access here, so or olp->Release() or m_olps.push_back(olp); but not both. this kill all logic of Release may be you need overwrite operator delete of OverlappedEx and inside it call m_olps.push_back(olp); and of course overwrite operator new too
again (OVERLAPPED*)olp - for what reinterpret_cast here ? because you inherit own struct from OVERLAPPED compiler auto do type cast here

Loop streaming .ogg audio - OpenAL

I have problem looping a streamed ogg vorbis file.
This is the code :
fslStream_OGG::fslStream_OGG()
{
className = "fslSound";
iMemSize = 0;
iLength = 0;
bSourceRelative = false;
bIsLooping = false;
bForceStop = false;
bActive = false;
source = buffer = 0;
current_gain = 1.0f;
outer_gain = 0;
snd_info.uiChannels = snd_info.uiFrequency = snd_info.uiSampling = 0;
}
fslStream_OGG::~fslStream_OGG()
{
if (bStreamCreated)
{
alSourceStop(source);
Empty();
alDeleteSources(1,&source);
alDeleteBuffers(2,buffers);
ov_clear(&oggStream);
}
}
bool fslStream_OGG::Update()
{
ALenum state;
alGetSourcei(source,AL_SOURCE_STATE,&state);
if (state == AL_PAUSED || !bActive) return false;
int processed;
alGetSourcei(source,AL_BUFFERS_PROCESSED,&processed);
while (processed--)
{
ALuint bufferI;
alSourceUnqueueBuffers(source,1,&bufferI);
Stream(bufferI);
if (bActive) alSourceQueueBuffers(source,1,&bufferI);
}
if (state == AL_STOPPED || !bActive)
{
bActive = false;
StreamSetPos(0.0f);
if (bForceStop) return false;
if (bIsLooping)
{
alSourceStop(source); // <- *** note these ***
Empty(); // <- *** 2 lines of code ***
StreamPlay();
alSourcePlay(source);
}
else
{
return true;
}
}
return false;
}
void fslStream_OGG::StreamPlay()
{
if (!bActive)
{
bActive = true;
bForceStop = false;
Stream(buffers[0]);
Stream(buffers[1]);
alSourceQueueBuffers(source,2,buffers);
}
}
bool fslStream_OGG::Open(const char* strFile)
{
bStreamCreated = false;
vorbis_info* vorbisInfo;
oggFile = fopen(strFile,"rb");
if (!oggFile) return false;
if (ov_open_callbacks(oggFile,&oggStream,NULL,0,OV_CALLBACKS_DEFAULT) != 0)
{
fclose(oggFile);
return false;
}
vorbisInfo = ov_info(&oggStream,-1);
if (vorbisInfo->channels == 1)
format = AL_FORMAT_MONO16;
else format = AL_FORMAT_STEREO16;
alGenBuffers(2,buffers);
alGenSources(1,&source);
iLength = (long)(ov_time_total(&oggStream,-1) * 1000.0);
snd_info.uiChannels = (format == AL_FORMAT_MONO8 || format == AL_FORMAT_MONO16)? 1:2;
snd_info.uiSampling = (format == AL_FORMAT_MONO8 || format == AL_FORMAT_STEREO8)? 8:16;
snd_info.uiFrequency = vorbisInfo->rate;
bStreamCreated = true;
bIsStream = true;
return true;
}
void fslStream_OGG::Stream(ALuint bufferI)
{
int size = 0;
int section;
int result;
bActive = true;
while (size < OGG_STREAM_BUFFER_SIZE)
{
result = ov_read(&oggStream,data + size,OGG_STREAM_BUFFER_SIZE - size,0,2,1,&section);
if (result > 0)
{
size += result;
}
else
{
if (result < 0) return; else break;
}
}
if (size == 0) { bActive = false; return; }
alBufferData(bufferI,format,data,size,snd_info.uiFrequency);
}
void fslStream_OGG::Empty()
{
int queued;
alGetSourcei(source,AL_BUFFERS_QUEUED,&queued);
while (queued--)
{
ALuint bufferI;
alSourceUnqueueBuffers(source,1,&bufferI);
}
}
void fslStream_OGG::StreamSetPos(float p)
{
ov_time_seek(&oggStream,p);
}
float fslStream_OGG::StreamGetPos()
{
return (float)ov_time_tell(&oggStream);
}
Note the 2 lines of code I have marked with ***.
In all cases, the file starts playing fine and rewinds when it ends. However :
Without those 2 lines of code, when repeated the file sounds "corrupted". If let to repeat again, it sounds even more "corrupted". I believe this is because OpenAl and the Vorbis decoder get "unsynchronized" writing/reading the buffer when the stream is repeated.
If I add those 2 lines of code, the file repeats without sounding corrupted. However, the file isn't repeated seamlessly; it rewinds a few centiseconds before it ends. I suspect this is because the buffers are not played to the end before rewinding to start.
I would be obliged if anyone could lend a helping hand.
Many thanks in advance,
Bill
It seems that I have fixed the problem (I won't be sure without extensive testing).
I have fixed the Update method as follows :
bool fslStream_OGG::Update()
{
ALenum state;
alGetSourcei(source,AL_SOURCE_STATE,&state);
//if (state == AL_PAUSED || !bActive) return false; // <- WRONG
if (state == AL_PAUSED) return false;
int processed;
alGetSourcei(source,AL_BUFFERS_PROCESSED,&processed);
while (processed--)
{
ALuint bufferI;
alSourceUnqueueBuffers(source,1,&bufferI);
Stream(bufferI);
if (bActive) alSourceQueueBuffers(source,1,&bufferI);
}
//if (state == AL_STOPPED || !bActive) /// <- WRONG
if (state == AL_STOPPED)
{
bActive = false;
StreamSetPos(0.0f);
if (bForceStop) return false;
if (bIsLooping)
{
//alSourceStop(source); // <- I have added these
//Empty(); // <- 2 lines of code
StreamPlay();
alSourcePlay(source);
}
else
{
return true;
}
}
return false;
}
Those 2 lines of code seem not to be necessary now. Will have to test it with different OSes, hardware etc...