diff options
| author | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
|---|---|---|
| committer | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
| commit | 47e6e7c84f008a53061e661f31ae96629bc694ef (patch) | |
| tree | 648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmtime/timelord.cpp | |
| download | pcp-debian.tar.gz | |
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmtime/timelord.cpp')
| -rw-r--r-- | src/pmtime/timelord.cpp | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/src/pmtime/timelord.cpp b/src/pmtime/timelord.cpp new file mode 100644 index 0000000..64ebcf0 --- /dev/null +++ b/src/pmtime/timelord.cpp @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2007, Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include "timelord.h" +#include <stdlib.h> + +TimeClient::TimeClient(QTcpSocket *s, QObject *p) : QObject(p) +{ + my.ac = NULL; + my.hc = NULL; + my.socket = s; + my.state = TimeClient::Disconnected; + my.source = PmTime::NoSource; + memset(&my.acktime, 0, sizeof(my.acktime)); + console->post(PmTime::DebugProtocol, "TimeClient initialised"); + connect(my.socket, SIGNAL(readyRead()), SLOT(readClient())); + connect(my.socket, SIGNAL(disconnected()), SLOT(disconnectClient())); +} + +TimeClient::~TimeClient() +{ + console->post(PmTime::DebugProtocol, "Destroying client %p", this); + my.state = TimeClient::Disconnected; + my.source = PmTime::NoSource; + emit endConnect(this); +} + +static char *stateString(int state) +{ + static char buffer[32]; + + if (state == TimeClient::Disconnected) + strcpy(buffer, "Disconnected State"); + else if (state == TimeClient::ClientConnectSET) + strcpy(buffer, "ClientConnectSET State"); + else if (state == TimeClient::ServerConnectACK) + strcpy(buffer, "ServerConnectACK State"); + else if (state == TimeClient::ServerNeedACK) + strcpy(buffer, "ServerNeedACK State"); + else if (state == TimeClient::ClientReady) + strcpy(buffer, "ClientReady State"); + else + strcpy(buffer, "Unknown State"); + return buffer; +} + +void TimeClient::disconnectClient() +{ + console->post(PmTime::DebugProtocol, "TimeClient::disconnectClient"); + delete this; +} + +bool TimeClient::writeClient(PmTime::Packet *packet, + char *tz, int tzlen, char *label, int llen) +{ + if (packet->source != my.source) + return true; + + switch (my.state) { + case TimeClient::Disconnected: + return true; + case TimeClient::ClientConnectSET: + if (packet->command == PmTime::ACK) { + my.state = TimeClient::ServerConnectACK; + break; + } + return true; + case TimeClient::ServerConnectACK: + break; + case TimeClient::ClientReady: + if (packet->command == PmTime::Step) { + my.state = TimeClient::ServerNeedACK; + my.acktime = packet->position; + } + else { + my.state = TimeClient::ClientReady; + memset(&my.acktime, 0, sizeof(my.acktime)); + } + break; + case TimeClient::ServerNeedACK: + if (packet->command != PmTime::Step) { + my.state = TimeClient::ClientReady; // clear NEED_ACK + memset(&my.acktime, 0, sizeof(my.acktime)); + break; + } + console->post(PmTime::DebugProtocol, "TimeClient::writeClient " + "SKIP STEP to pos=%u.%u when client %p in NEED_ACK", + packet->position.tv_sec,packet->position.tv_usec, this); + return false; + } + + int len = my.socket->write((const char *)packet, sizeof(PmTime::Packet)); + if (len != sizeof(PmTime::Packet)) { + console->post(PmTime::DebugProtocol, "TimeCient::writeClient " + "wrote %d bytes not %d (%x command)", + len, packet->length, packet->command); + my.state = TimeClient::Disconnected; + endConnect(this); + } else { + console->post(PmTime::DebugProtocol, "TimeClient::writeClient " + "wrote %d bytes command=%x state=%u", + len, packet->command, my.state); + } + if (tzlen > 0 && len > 0 && + (len = my.socket->write(tz, tzlen)) != tzlen) { + console->post(PmTime::DebugProtocol, "TimeClient::writeClient " + "wrote %d bytes not %d (timezone)", len, tzlen); + my.state = TimeClient::Disconnected; + endConnect(this); + } else if (tzlen) { + console->post(PmTime::DebugProtocol, "TimeClient::writeClient " + "wrote %d bytes of timezone successfully", len); + } + if (llen > 0 && len > 0 && + (len = my.socket->write(label, llen)) != llen) { + console->post(PmTime::DebugProtocol, "TimeClient::writeClient " + "wrote %d bytes not %d (tz label)", len, llen); + my.state = TimeClient::Disconnected; + endConnect(this); + } else if (llen) { + console->post(PmTime::DebugProtocol, "TimeClient::writeClient " + "wrote %d bytes of tz label successfully", len); + } + return true; +} + +void TimeClient::readClient(void) +{ + PmTime::Packet packet; + char *payload = NULL; + int bad = 0, len, sz; + + console->post(PmTime::DebugProtocol, "Reading data from client %p", this); + + len = my.socket->read((char *)&packet, sizeof(PmTime::Packet)); + if (len < 0) { + console->post(PmTime::DebugProtocol, "Read error on client %p", this); + bad = 1; + } else if (packet.magic != PmTime::Magic) { + console->post(PmTime::DebugProtocol, "Bad magic (%x) from client %p", + packet.magic, this); + bad = 1; + } else if (len != sizeof(PmTime::Packet)) { + console->post(PmTime::DebugProtocol, + "Bad 1st read (want %d, got %d) on client %p", + len, sizeof(PmTime::Packet), this); + bad = 1; + } else if (packet.length > sizeof(PmTime::Packet)) { + sz = packet.length - sizeof(PmTime::Packet); + payload = (char *)malloc(sz); + if (payload == NULL) { + console->post(PmTime::DebugProtocol, + "No memory (%d) for second read on client %p", + sz, len, this); + bad = 1; + } else if ((len = my.socket->read(payload, sz)) != sz) { + console->post(PmTime::DebugProtocol, + "Bad 2nd read (want %d, got %d) on client %p", + sz, len, this); + bad = 1; + } + console->post(PmTime::DebugProtocol, "+%d message from client %p", + sz, this); + } else { + console->post(PmTime::DebugProtocol, "good message from client %p", + this); + } + + if (!bad) { + console->post(PmTime::DebugProtocol, "state %s message %d", + stateString(my.state), packet.command); + switch(my.state) { + case TimeClient::Disconnected: + if (packet.command == PmTime::Set) + console->post(PmTime::DebugProtocol, + "%s got new SET from client %p", + __func__, this); + if (packet.source == PmTime::HostSource) { + my.source = PmTime::HostSource; + my.hc->setTime(&packet, payload); + } else { + my.source = PmTime::ArchiveSource; + my.ac->setTime(&packet, payload); + } + my.state = TimeClient::ClientConnectSET; + packet.command = PmTime::ACK; + packet.length = sizeof(PmTime::Packet); + writeClient(&packet); + return; + + case TimeClient::ClientConnectSET: + console->post(PmTime::DebugProtocol, "TimeClient::readClient " + "bad client %p command %d in ConnectSET state", + this, packet.command); + break; + + case TimeClient::ServerConnectACK: + if (packet.command == PmTime::ACK) + my.state = TimeClient::ClientReady; + break; + + case TimeClient::ServerNeedACK: + if (packet.command != PmTime::ACK) + break; + if (packet.position.tv_sec == my.acktime.tv_sec && + packet.position.tv_usec == my.acktime.tv_usec) { + console->post(PmTime::DebugProtocol, "TimeClient::readClient " + "good ACK client=%p (%u.%u)", this, + my.acktime.tv_sec, my.acktime.tv_usec); + my.state = TimeClient::ClientReady; + break; + } + console->post(PmTime::DebugProtocol, "TimeClient::readClient " + "BAD ACK client=%p (got %u.%u vs %u.%u)", this, + packet.position.tv_sec, packet.position.tv_usec, + my.acktime.tv_sec, my.acktime.tv_usec); + bad = 1; + break; + + case TimeClient::ClientReady: + if (packet.command == PmTime::ACK) { + console->post(PmTime::DebugProtocol, "TimeClient:: readClient " + "unexpected client %p ACK in Ready state", this); + } + break; + } + + switch(packet.command) { + case PmTime::GUIHide: + case PmTime::GUIShow: + console->post(PmTime::DebugProtocol, "TimeClient::readClient " + "HIDE/SHOW from client %p", this); + if (my.source == PmTime::HostSource) + my.hc->popup(packet.command == PmTime::GUIShow); + if (my.source == PmTime::ArchiveSource) + my.ac->popup(packet.command == PmTime::GUIShow); + break; + case PmTime::Bounds: + console->post(PmTime::DebugProtocol, "TimeClient::readClient " + "BOUNDS from client %p", this); + my.ac->addBound(&packet, payload); + break; + case PmTime::ACK: + break; + default: + console->post(PmTime::DebugProtocol, "TimeClient::readClient " + "unknown command %d from client %p", + packet.command, this); + bad = 1; + } + } + + if (bad) + reset(); +} + +void TimeClient::reset() +{ + console->post(PmTime::DebugProtocol, "TimeClient::reset"); +#if 0 + if (my.source == PmTime::HostSource) + my.hc->stop(); + else + my.ac->stop(); +#endif +} + +TimeLord::TimeLord(QApplication *app) +{ + my.ac = NULL; + my.hc = NULL; + connect(this, SIGNAL(lastClientExit()), app, SLOT(quit())); + connect(this, SIGNAL(lastClientExit()), this, SLOT(quit())); + connect(this, SIGNAL(newConnection()), SLOT(newConnection())); + console->post(PmTime::DebugProtocol, "TimeLord initialised"); +} + +void TimeLord::quit() +{ + if (my.ac) + my.ac->quit(); + if (my.hc) + my.hc->quit(); +} + +void TimeLord::setContext(PmTimeLive *live, PmTimeArch *arch) +{ + my.hc = live; + connect(live, SIGNAL(timePulse(PmTime::Packet *)), + SLOT(timePulse(PmTime::Packet *))); + connect(live, SIGNAL(vcrModePulse(PmTime::Packet *, int)), + SLOT(vcrModePulse(PmTime::Packet *, int))); + connect(live, SIGNAL(tzPulse(PmTime::Packet *, char *, int, char *, int)), + SLOT(tzPulse(PmTime::Packet *, char *, int, char *, int))); + my.ac = arch; + connect(arch, SIGNAL(timePulse(PmTime::Packet *)), + SLOT(timePulse(PmTime::Packet *))); + connect(arch, SIGNAL(boundsPulse(PmTime::Packet *)), + SLOT(boundsPulse(PmTime::Packet *))); + connect(arch, SIGNAL(vcrModePulse(PmTime::Packet *, int)), + SLOT(vcrModePulse(PmTime::Packet *, int))); + connect(arch, SIGNAL(tzPulse(PmTime::Packet *, char *, int, char *, int)), + SLOT(tzPulse(PmTime::Packet *, char *, int, char *, int))); +} + +void TimeLord::newConnection(void) +{ + TimeClient *c = new TimeClient(nextPendingConnection(), this); + + console->post(PmTime::DebugProtocol, "Adding new client %p", c); + c->setContext(my.ac, my.hc); + connect(c, SIGNAL(endConnect(TimeClient *)), + SLOT(endConnect(TimeClient *))); + my.clientlist.append(c); +} + +void TimeLord::endConnect(TimeClient *client) +{ + console->post(PmTime::DebugProtocol, "Removing client %p", client); + my.clientlist.removeAll(client); + if (my.clientlist.isEmpty()) { + console->post(PmTime::DebugProtocol, "No clients remain, exiting"); + emit lastClientExit(); + } +} + +void TimeLord::timePulse(PmTime::Packet *packet) +{ + QList<TimeClient*> overrunClients; + +#if DESPERATE + static int sequence; + int localSequence = sequence++; + console->post(PmTime::DebugProtocol, "TimeLord::timePulse %d (%d clients)", + localSequence, my.clientlist.count()); +#endif + + packet->magic = PmTime::Magic; + packet->length = sizeof(PmTime::Packet); + packet->command = PmTime::Step; + for (int i = 0; i < my.clientlist.size(); i++) + if (my.clientlist.at(i)->writeClient(packet) == false) + overrunClients.append(my.clientlist.at(i)); + for (int i = 0; i < overrunClients.size(); i++) + overrunClients.at(i)->reset(); + +#if DESPERATE + console->post(PmTime::DebugProtocol, "TimeLord::timePulse ended %d (%d)", + localSequence, overrunClients.size()); +#endif +} + +void TimeLord::boundsPulse(PmTime::Packet *packet) +{ + console->post(PmTime::DebugProtocol, "TimeLord::boundsPulse (%d clients)", + my.clientlist.count()); + packet->magic = PmTime::Magic; + packet->length = sizeof(PmTime::Packet); + packet->command = PmTime::Bounds; + for (int i = 0; i < my.clientlist.size(); i++) + my.clientlist.at(i)->writeClient(packet); +} + +void TimeLord::vcrModePulse(PmTime::Packet *packet, int drag) +{ + console->post(PmTime::DebugProtocol, "TimeLord::vcrModePulse (%d clients)" + " %d", my.clientlist.count(), drag); + packet->magic = PmTime::Magic; + packet->length = sizeof(PmTime::Packet); + packet->command = drag ? PmTime::VCRModeDrag : PmTime::VCRMode; + for (int i = 0; i < my.clientlist.size(); i++) + my.clientlist.at(i)->writeClient(packet); +} + +void TimeLord::tzPulse(PmTime::Packet *packet, + char *tz, int tzlen, char *l, int llen) +{ + console->post(PmTime::DebugProtocol, "TimeLord::tzPulse (%d clients)" + " - %s/%d/%d", my.clientlist.count(), tz, tzlen, llen); + packet->magic = PmTime::Magic; + packet->length = sizeof(PmTime::Packet) + tzlen + llen; + packet->command = PmTime::TZ; + for (int i = 0; i < my.clientlist.size(); i++) + my.clientlist.at(i)->writeClient(packet, tz, tzlen, l, llen); +} |
