Wednesday, December 15, 2021

Getting file/folder list in Windows and Linux

Getting file/folder list in Windows and Linux without using libraries is a tricky business. In Windows Visual Studio, there is also a character set setting (Project properties - Advanced - Character Set) which has to be taken into account.

#ifdef _WIN32
#include <windows.h> //NOTE: The first include must be windows.h
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#include<comdef.h> //to convert WCHAR to char*
#pragma comment(lib, "User32.lib")
namespace FileUtils { //NOTE: namespace must come after #include statements, otherwise you will get strange errors.
//https://docs.microsoft.com/en-us/windows/win32/fileio/listing-the-files-in-a-directory
int GetAll(DWORD fileAttribute, const char* folderName, char fileNames[][MAX_FILE_NAME_LENGTH]) {
WIN32_FIND_DATA ffd;
HANDLE hFind = INVALID_HANDLE_VALUE;
DWORD dwError = 0;
TCHAR szDir[MAX_FOLDER_NAME_LENGTH];
#ifdef UNICODE //Project properties - Advanced - Character Set
//convert const char* to LPCWSTR:
int size = MultiByteToWideChar(CP_ACP, 0, folderName, -1, NULL, 0);
TCHAR folderNameLPCWSTR[MAX_FOLDER_NAME_LENGTH];
MultiByteToWideChar(CP_ACP, 0, folderName, -1, (WCHAR*)folderNameLPCWSTR, size);
// Prepare string for use with FindFile functions. First, copy the string to a buffer, then append '\*' to the directory name.
StringCchCopy(szDir, MAX_FOLDER_NAME_LENGTH, (STRSAFE_LPCWSTR)folderNameLPCWSTR);
StringCchCat(szDir, MAX_FOLDER_NAME_LENGTH, TEXT("\\*"));
#else //Multi-Byte
sprintf(szDir, "%s\\*", folderName);
#endif
// Find the first file in the directory:
//hFind = FindFirstFile(szDirLPCWSTR, &ffd);
hFind = FindFirstFile(szDir, &ffd);
if (INVALID_HANDLE_VALUE == hFind) {
printf("Error: Folder (%s) does not exist!\n", folderName);
return 0;
}
// List all the files in the directory with some info about them.
size_t nFiles = 0;
do {
if (ffd.dwFileAttributes & fileAttribute) {
//convert wchar to char*:
_bstr_t b(ffd.cFileName);
const char* fileName = b;
strcpy(fileNames[nFiles], fileName);
//filesize.LowPart = ffd.nFileSizeLow;
//filesize.HighPart = ffd.nFileSizeHigh;
//_tprintf(TEXT(" %s %ld bytes\n"), ffd.cFileName, filesize.QuadPart);
nFiles++;
if (nFiles > MAX_NB_OF_FILES) {
printf("Error: nFiles (%d) > MAX_NB_OF_FILES (%d)!\n", nFiles, MAX_NB_OF_FILES);
return 0;
}
}
} while (FindNextFile(hFind, &ffd) != 0);
dwError = GetLastError();
if (dwError != ERROR_NO_MORE_FILES) {
printf("Error: %d\n", dwError);
return 0;
}
FindClose(hFind);
//return dwError;
return nFiles;
}
int GetAllFolders(const char* folderName, char fileNames[][MAX_FILE_NAME_LENGTH]) {
return GetAll(FILE_ATTRIBUTE_DIRECTORY, folderName, fileNames);
}
#define FILE_ATTRIBUTE_FILE (8224)
int GetAllFiles(const char* folderName, char fileNames[][MAX_FILE_NAME_LENGTH]) {
return GetAll(FILE_ATTRIBUTE_FILE, folderName, fileNames);
}
#else //POSIX, Linux
//https://stackoverflow.com/a/4204758/51358
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#define FILE_ATTRIBUTE_DIRECTORY (4)
#define FILE_ATTRIBUTE_FILE (8)
namespace FileUtils {
int GetAll(const char* folderName, unsigned char d_type, char fileNames[][MAX_FILE_NAME_LENGTH]) {
DIR* d = opendir(folderName);
int nFiles = 0;
if (d) {
struct dirent* dir;
while ((dir = readdir(d)) != NULL) {
if(d_type == dir->d_type) {
//printf("name: %s, type: %d\n", dir->d_name, dir->d_type);
strcpy(fileNames[nFiles], dir->d_name);
nFiles++;
}
}
closedir(d);
}
return nFiles;
}
int GetAllFolders(const char* folderName, char fileNames[][MAX_FILE_NAME_LENGTH]) {
return GetAll(folderName, FILE_ATTRIBUTE_DIRECTORY, fileNames);
}
int GetAllFiles(const char* folderName, char fileNames[][MAX_FILE_NAME_LENGTH]) {
return GetAll(folderName, FILE_ATTRIBUTE_FILE, fileNames);
}
#endif // _WIN32
}

Wednesday, December 1, 2021

Vectors for Software Engineers

Vectors are common in simulations but software engineers can forget the basics of vector operations. Here is a quick refresher:


In equation (1), we have a velocity vector of point m with respect to point c, expressed as a sum of two vectors.

In equation (2), we have the same equation but in scalar form. The "(n)" represents the reference frame in which the components of the vectors are expressed. In order to perform addition, all vector components must be expressed in the same reference frame.

If you want more detail, check out my advanced dynamics lecture notes (PDF).

Tuesday, November 30, 2021

Generate C/C++ Code From Matlab Function

Use MATLAB Coder to generate C/C++ code from an m file (must be a function). At the end of the process, click on "View Report" and on the Report Viewer window, click on Package Code to package all the dependencies (e.g. tmwtypes.h) into a single zip file.

Friday, November 26, 2021

Check if the MATLAB instance you created has exited

When you run MATLAB from a Windows bat file and want to know if it has finished its job and exited (without considering other already open MATLAB instance), you can use the following:

start matlab.exe -nosplash -nodesktop -noFigureWindows
set pid=not found
for /f "tokens=2" %%a in ('tasklist^|find /i "matlab") do (set pid=%%a)
echo matlab pid = %pid%

:waitForMatlabToExit
timeout /t /5 /nobreak
tasklist /fi "pid eq %pid%" | find ":">nul
if errorlevel 1 goto waitForMatlabToExit
echo Matlab exited
exit /b

Thursday, November 25, 2021

Windows batch file: goto vs call

In a Windows batch file, goto label2 command goes to the label2 and continues from the end of label2. call :label2 goes to label2 and after seeing a exit /b (or end of script, eof), returns back right after the call command. In other words, call behaves like a function call in Java, C++ etc. but you have to use exit /b instead of return.

Calling a Matlab function from Windows command line (cmd)

Calling a Matlab function from a Windows cmd batch file is easy if the function has no string arguments. However if the function takes a string argument, you have to use two single quotes around the argument. Example:

start matlab -nosplash -nodesktop -noFigureWindows -r eval("'myFunction(''fileName.txt'')'")

Wednesday, November 10, 2021

Remote connection and file transfer

The easiest way to connect from Windows to a remote Linux computer is to fire up cmd.exe and type ssh user@ip where user is the Linux user name and ip is the IP address of the Linux computer. 

For file transfer, you can use sftp user@ip or scp fileName user@remoteIP/remoteFileName.

ssh, sftp and scp come with Windows 10, you don't have to install anything. Just make sure that SSH is enabled on Linux.

Tuesday, November 9, 2021

Simple way/pattern to separate GUI from model

Prepare your model as a separate executable that reads input from file and writes output to a file. Then you can code your GUI in whatever language you like (e.g. Excel macros) that takes input from user, writes them to file, runs the model executable and reads outputs from file when executable finishes.

This completely decouples GUI from implementation. Compared to a DLL, it has a simpler interface, it will not crash your JVM if there is an error in model, and let's you run models in parallel much easier and also facilitates easy batch run of model executable via scripts.

Wednesday, November 3, 2021

Debugging Effort

The hardest part of debugging is finding the root cause of the problem, therefore it is wise to optimize code for debugging, see clean code:


Tuesday, November 2, 2021

memcpy() vs memmove()

When you need to implement removal of an element from an arraylist, don't use memcpy, use memmove. Your program might run fine on Windows but cause trouble in Linux, whose root cause would be hard to find.


memcpy() vs memmove(): in case of memcpy(), there is no extra buffer taken for source memory. The copying is done directly on the memory so that when there is memory overlap, we get unexpected results

Monday, November 1, 2021

Simulink versions

Matlab release - Simulink version (helpful when you know the Simulink version and wonder which Matlab release it corresponds to):


Wednesday, September 29, 2021

Safety-Critical System Development

Safety-critical system development requires safety certification. Examples for railways are EN 50128 (software) and EN 50129 (hardware). For aerospace there are DO-178C (software) and DO-254 (hardware). Since it it not practically possible to have 100% test coverage for complex systems, these documents require that development processes adhere to practices that minimize risk of catastrophic failure.

Companies with no experience in these standards grossly underestimate time and budget requirements of making the necessary changes for compliance. It takes at least two years to get a company from zero to certified. If the company has the vision to enter the aerospace market, compliance preparations have to be started before any system development contract, because to both change company culture and develop the system at the same time is a sure way to fail.

One way to avoid DO-178C is to just use an electronic circuit, without any software, provided that the requirements are simple enough. For example, an aircraft climate controller consisting of temperature and airflow sensors and fan and valve actuators could be realized with a simple PID controller using only operational amplifiers. Since it has no software and the relatively simple hardware can be tested with 100% coverage, there is no need to demonstrate that company development processes are sound. Hardware still has to comply with DO-254.

Monday, September 27, 2021

Binary string permutations

Previously, I had solved printing all permutations of a string. As part of a programming contest, I solved binary string permutations using C++. What is interesting in this solution is that is uses a simple for loop and obtains each binary permutation by converting a decimal number to a binary number:

//Print all binary string combinations of length n.
//You can think of the strings as binary forms of numbers from 0 to 2^n-1.
//Solution of https://www.algoleague.com/contest/algorithm-training-beginner-set/problem/the-pit/detail
//Şamil Korkmaz, 27.09.2021
#include <iostream>
using namespace std;
//Convert decimal number to binary number string
void dec2binStr(int dec, const int n) {
//printf("-------\n");
char s[n+1];
s[n] = 0; //end of line character
for(int i=0; i < n; i++) {
//printf("dec = %d\n", dec);
int digit = dec & 1; //get rightmost digit
dec = dec >> 1; //divide by 2
//printf("digit = %d\n", digit);
s[n-1-i] = digit + 48; //ascii '0' = 48
//printf("digit = %d\n", digit);
}
cout << s << endl;
}
int main() {
int n; cin >> n;
//int n = 4;
int maxVal = 1 << n; //2^n
//printf("maxVal = %d\n", maxVal);
for(int i = 0; i < maxVal; i++) { //numbers from 0 to 2^n-1
dec2binStr(i, n);
//cout << "s = " << s << endl;
}
return 0;
}

Sunday, September 12, 2021

Hiding internal details of a C++ library

When you need to provide your simulation as an external library (dll, lib) to someone, you should simplify the API as much as possible so that you are able to provide the minimal amount of header files, without exposing details the user doesn't care about. You can achieve this by hiding all the internal dependencies in the implementation (cpp) file. If the user has to be able to create multiple (concurrent) simulations, you can use a static map to hold each simulation object. Here is an example (C++11):

//Blog post: https://simulinkforsoftwareengineers.blogspot.com/2021/09/hiding-simulation-details-in-c-library.html
#include "SimAPI.h"
#include<map>
using namespace std;
#include<SimState.h> //this is internal to simulation, which the library user does not need to know
#include<Solver.h> //this is internal to simulation
class Simulation {
public:
State state; //State is defined in SimState.h
Solver solver; //Solver is defined in Solver.h
};
Simulation* sim;
static map<int, Simulation*> sims;
SimAPI::SimAPI(int simKey) {
if (sims.count(simKey) > 0) throw SimKeyAlreadyExistsException(simKey);
sim = new Simulation();
sims.insert({ simKey, sim });
}
SimAPI::~SimAPI() {} //Note: The desctructor is called right after constructor when this class is used like SimAPI s = SimAPI(...) due to a temp object being created. It is not the case for SimAPI* s = new SimAPI(...).
Outputs SimAPI::runOneStep(int simKey, int timeStep_s) {
Simulation* pSim = sims[simKey];
State currentState = pSim->state;
State newState = pSim->solver(currentState, timeStep_s);
pSim->state = newState; //update simulation state
Outputs out = Outputs();
out.time_s = newState.time_s;
out.hMSL_m = newState.hMSL_m;
return out;
}
void SimAPI::terminate(int simKey) {
sims.erase(simKey);
}
view raw simAPI.cpp hosted with ❤ by GitHub
#pragma once
#include<exception>
#include<stdio.h>
class SimKeyAlreadyExistsException : public std::exception {
public:
SimKeyAlreadyExistsException(int simKey) : existingKey(simKey) {}
const char* what() const throw() {
char message[200];
sprintf_s(message, "simKey (%d) already exists, please enter a unique simKey!\n", existingKey);
return message;
}
int getExistingKey() {
return existingKey;
}
private:
int existingKey;
};
class Outputs {
public:
int time_s;
double hMSL_m;
};
class SimAPI {
public:
SimAPI(int simIndex);
~SimAPI();
Outputs runOneStep(int simIndex, int timeStep_s);
void terminate(int simIndex);
};
view raw simAPI.h hosted with ❤ by GitHub

Wednesday, July 14, 2021

C++: Using 1 byte enum

By default, enum size in C++ is usually 4 bytes, both in Windows and Linux. If you need to use 1 byte enum, you have to declare it as follows:

enum e : unsigned char { a, b };

This requires at least C++ 11. printf("size = %ld\n", sizeof(e)) will output 1.

If you are using gcc (e.g. Eclipse), you can also use the compiler flag -fshort-enum (Eclipse properties - C/C++ Build - Settings - Tool Settings - GCC C++ Compiler - Miscellaneous) to convert all enums in code to 1 byte, but you won't have that option in Visual Studio. So it is better to use the unsigned char option.

Friday, July 9, 2021

Use memset only with zero

Consider the following C++ definitions:

typedef struct {

int i;

double d[3];

} A_STRUCT

A_STRUCT s;

When I use memset(&s, 0, sizeof(s)), i and d values are set to zero as expected. When I use memset(&s, -1, sizeof(s)), I would expect all values to be -1 but on inspection you will see that they have strange values, in my case i was -1 but d values were -nan. When I use memset(&s, 1, sizeof(s)), I get 16843009 for i and 7.74...e-304 for d values.

Reason: The memset() function writes bytes, not words. So writing 1 to sizeof(int)*100 bytes writes 00000001 to every set of 8-bits.Thus, each integer in binary looks like the following:

0000 0001 0000 0001 0000 0001 0000 0001 (1 int = 4 bytes)

which in decimal is, exactly, 16843009..

memset doesn't only work with 0. It also works with all numbers with identically repeating byte pattern. Like for example ~0.

Tuesday, January 12, 2021

#define considered harmful

 In C++, #define has global scope. If you have a #define FAIL (-1) in a header file that you include, you cannot declare a variable named FAIL anywhere else, not even inside a namespace. If you do, you will get the cryptic error message "expected an identifier":

So, don't use define for named constants in C++, use const int etc. If you are using C instead of C++, read this.