ShmemInterface.cpp 5.08 KB
Newer Older
1
2
#ifdef __linux__

3
4
5
6
7
8
#include <cstdio>
#include <cstdlib>

#include <villas/sample.h>
#include <villas/shmem.h>

9
#include "ShmemInterface.h"
10
11
12

using namespace DPsim;

13
14
15
void ShmemInterface::init(const char* wname, const char* rname, struct shmem_conf* conf) {
	/* using a static shmem_conf as a default argument for the constructor
	 * doesn't seem to work, so use this as a workaround */
Georg Martin Reinke's avatar
Georg Martin Reinke committed
16
17
18
19
20
21

	// make local copies of the filenames, because shmem_int doesn't make copies
	// and needs them for the close function
	this->wname = std::string(wname);
	this->rname = std::string(rname);
	if (shmem_int_open(this->wname.c_str(), this->rname.c_str(), &this->mShmem, conf) < 0) {
22
23
24
25
		std::perror("Failed to open/map shared memory object");
		std::exit(1);
	}
	mSeq = 0;
26
27
28
29
30
31
32
33
	if (shmem_int_alloc(&mShmem, &mLastSample, 1) < 0) {
		std::cerr << "Failed to allocate single sample from shmem pool" << std::endl;
		std::exit(1);
	}
	mLastSample->sequence = 0;
	mLastSample->ts.origin.tv_sec = 0;
	mLastSample->ts.origin.tv_nsec = 0;
	std::memset(&mLastSample->data, 0, mLastSample->capacity * sizeof(float));
34
35
}

36
37
38
39
40
41
42
43
ShmemInterface::ShmemInterface(const char* wname, const char* rname) {
	struct shmem_conf conf;
	conf.queuelen = 512;
	conf.samplelen = 64;
	conf.polling = 0;
	init(wname, rname, &conf);
}

44
ShmemInterface::ShmemInterface(const char* wname, const char *rname, struct shmem_conf* conf) {
45
	init(wname, rname, conf);
46
47
}

48
ShmemInterface::~ShmemInterface() {
49
	shmem_int_close(&mShmem);
50
51
}

52
void ShmemInterface::readValues(bool blocking) {
53
54
55
56
	if (!mInit) {
		mInit = 1;
		return;
	}
57
	struct sample *sample = nullptr;
58
	int ret = 0;
59
	try {
Georg Martin Reinke's avatar
Georg Martin Reinke committed
60
		if (!blocking) {
61
			ret = shmem_int_read(&mShmem, &sample, 1);
62
63
64
65
66
67
			if (ret == 0)
				return;
		} else {
			while (ret == 0)
				ret = shmem_int_read(&mShmem, &sample, 1);
		}
68
69
		if (ret < 0) {
			std::cerr << "Fatal error: failed to read sample from shmem interface" << std::endl;
70
71
			std::exit(1);
		}
72
73
74
75
76
77
78
79
		for (ExtComponent extComp : mExtComponents) {
			if (extComp.realIdx >= sample->length || extComp.imagIdx >= sample->length) {
				std::cerr << "Fatal error: incomplete data received from shmem interface" << std::endl;
				std::exit(1);
			}
			// TODO integer format?
			Real real = sample->data[extComp.realIdx].f;
			Real imag = sample->data[extComp.imagIdx].f;
80

81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
			ExternalCurrentSource *ecs = dynamic_cast<ExternalCurrentSource*>(extComp.comp);
			if (ecs)
				ecs->setCurrent(real, imag);
			ExternalVoltageSource *evs = dynamic_cast<ExternalVoltageSource*>(extComp.comp);
			if (evs)
				evs->setVoltage(real, imag);
		}
		sample_put(sample);
	} catch (std::exception& exc) {
		/* probably won't happen (if the timer expires while we're still reading data,
		 * we have a bigger problem somewhere else), but nevertheless, make sure that
		 * we're not leaking memory from the queue pool */
		if (sample)
			sample_put(sample);
		throw exc;
96
97
	}
}
98

99
void ShmemInterface::writeValues(SystemModel& model) {
100
101
102
103
104
105
	struct sample *sample = nullptr;
	Int len = 0, ret = 0;
	bool done = false;
	try {
		if (shmem_int_alloc(&mShmem, &sample, 1) < 1) {
			std::cerr << "fatal error: shmem pool underrun" << std::endl;
106
			std::cerr << "at seq" << mSeq << std::endl;
107
108
			std::exit(1);
		}
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
		for (auto vd : mExportedVoltages) {
			Real real = 0, imag = 0;
			if (vd.from > 0) {
				real += model.getRealFromLeftSideVector(vd.from-1);
				imag += model.getImagFromLeftSideVector(vd.from-1);
			}
			if (vd.to > 0) {
				real -= model.getRealFromLeftSideVector(vd.to-1);
				imag -= model.getImagFromLeftSideVector(vd.to-1);
			}
			if (vd.realIdx >= sample->capacity || vd.imagIdx >= sample->capacity) {
				std::cerr << "fatal error: not enough space in allocated struct sample" << std::endl;
				std::exit(1);
			}
			sample->data[vd.realIdx].f = real;
			sample->data[vd.imagIdx].f = imag;
			if (vd.realIdx > len)
				len = vd.realIdx;
			if (vd.imagIdx > len)
				len = vd.imagIdx;
129
		}
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
		for (auto cd : mExportedCurrents) {
			Complex current = cd.comp->getCurrent(model);
			if (cd.realIdx >= sample->capacity || cd.imagIdx >= sample->capacity) {
				std::cerr << "fatal error: not enough space in allocated struct sample" << std::endl;
				std::exit(1);
			}
			sample->data[cd.realIdx].f = current.real();
			sample->data[cd.imagIdx].f = current.imag();
			if (cd.realIdx > len)
				len = cd.realIdx;
			if (cd.imagIdx > len)
				len = cd.imagIdx;
		}
		sample->length = len+1;
		sample->sequence = mSeq++;
145
		clock_gettime(CLOCK_REALTIME, &sample->ts.origin);
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
		done = true;
		while (ret == 0)
			ret = shmem_int_write(&mShmem, &sample, 1);
		if (ret < 0)
			std::cerr << "Failed to write samples to shmem interface" << std::endl;
		sample_copy(mLastSample, sample);
	} catch (std::exception& exc) {
		/* We need to at least send something, so determine where exactly the
		 * timer expired and either resend the last successfully sent sample or
		 * just try to send this one again.
		 * TODO: can this be handled better? */
		if (!done)
			sample = mLastSample;
		while (ret == 0)
			ret = shmem_int_write(&mShmem, &sample, 1);
		if (ret < 0)
			std::cerr << "Failed to write samples to shmem interface" << std::endl;
		/* Don't throw here, because we managed to send something */
164
165
	}
}
166
167

#endif