📄 simulate.cpp
字号:
} else {
SetSingleBit(a->name2, FALSE);
}
break;
case INT_END_IF:
case INT_ELSE:
return;
case INT_COMMENT:
break;
default:
oops();
break;
}
}
}
//-----------------------------------------------------------------------------
// Called by the Windows timer that triggers cycles when we are running
// in real time.
//-----------------------------------------------------------------------------
void CALLBACK PlcCycleTimer(HWND hwnd, UINT msg, UINT_PTR id, DWORD time)
{
int i;
for(i = 0; i < CyclesPerTimerTick; i++) {
SimulateOneCycle(FALSE);
}
}
//-----------------------------------------------------------------------------
// Simulate one cycle of the PLC. Update everything, and keep track of whether
// any outputs have changed. If so, force a screen refresh. If requested do
// a screen refresh regardless.
//-----------------------------------------------------------------------------
void SimulateOneCycle(BOOL forceRefresh)
{
// When there is an error message up, the modal dialog makes its own
// event loop, and there is risk that we would go recursive. So let
// us fix that. (Note that there are no concurrency issues; we really
// would get called recursively, not just reentrantly.)
static BOOL Simulating = FALSE;
if(Simulating) return;
Simulating = TRUE;
NeedRedraw = FALSE;
if(SimulateUartTxCountdown > 0) {
SimulateUartTxCountdown--;
} else {
SimulateUartTxCountdown = 0;
}
IntPc = 0;
SimulateIntCode();
if(NeedRedraw || SimulateRedrawAfterNextCycle || forceRefresh) {
InvalidateRect(MainWindow, NULL, FALSE);
ListView_RedrawItems(IoList, 0, Prog.io.count - 1);
}
SimulateRedrawAfterNextCycle = FALSE;
if(NeedRedraw) SimulateRedrawAfterNextCycle = TRUE;
Simulating = FALSE;
}
//-----------------------------------------------------------------------------
// Start the timer that we use to trigger PLC cycles in approximately real
// time. Independently of the given cycle time, just go at 40 Hz, since that
// is about as fast as anyone could follow by eye. Faster timers will just
// go instantly.
//-----------------------------------------------------------------------------
void StartSimulationTimer(void)
{
int p = Prog.cycleTime/1000;
if(p < 5) {
SetTimer(MainWindow, TIMER_SIMULATE, 10, PlcCycleTimer);
CyclesPerTimerTick = 10000 / Prog.cycleTime;
} else {
SetTimer(MainWindow, TIMER_SIMULATE, p, PlcCycleTimer);
CyclesPerTimerTick = 1;
}
}
//-----------------------------------------------------------------------------
// Clear out all the parameters relating to the previous simulation.
//-----------------------------------------------------------------------------
void ClearSimulationData(void)
{
VariablesCount = 0;
SingleBitItemsCount = 0;
AdcShadowsCount = 0;
QueuedUartCharacter = -1;
SimulateUartTxCountdown = 0;
CheckVariableNames();
SimulateRedrawAfterNextCycle = TRUE;
if(!GenerateIntermediateCode()) {
ToggleSimulationMode();
return;
}
SimulateOneCycle(TRUE);
}
//-----------------------------------------------------------------------------
// Provide a description for an item (Xcontacts, Ycoil, Rrelay, Ttimer,
// or other) in the I/O list.
//-----------------------------------------------------------------------------
void DescribeForIoList(char *name, char *out)
{
switch(name[0]) {
case 'R':
case 'X':
case 'Y':
sprintf(out, "%d", SingleBitOn(name));
break;
case 'T': {
double dtms = GetSimulationVariable(name) *
(Prog.cycleTime / 1000.0);
if(dtms < 1000) {
sprintf(out, "%.2f ms", dtms);
} else {
sprintf(out, "%.3f s", dtms / 1000);
}
break;
}
default: {
SWORD v = GetSimulationVariable(name);
sprintf(out, "%hd (0x%04hx)", v, v);
break;
}
}
}
//-----------------------------------------------------------------------------
// Toggle the state of a contact input; for simulation purposes, so that we
// can set the input state of the program.
//-----------------------------------------------------------------------------
void SimulationToggleContact(char *name)
{
SetSingleBit(name, !SingleBitOn(name));
ListView_RedrawItems(IoList, 0, Prog.io.count - 1);
}
//-----------------------------------------------------------------------------
// Dialog proc for the popup that lets you interact with the UART stuff.
//-----------------------------------------------------------------------------
static LRESULT CALLBACK UartSimulationProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_DESTROY:
DestroyUartSimulationWindow();
break;
case WM_CLOSE:
break;
case WM_SIZE:
MoveWindow(UartSimulationTextControl, 0, 0, LOWORD(lParam),
HIWORD(lParam), TRUE);
break;
case WM_ACTIVATE:
if(wParam != WA_INACTIVE) {
SetFocus(UartSimulationTextControl);
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 1;
}
//-----------------------------------------------------------------------------
// Intercept WM_CHAR messages that to the terminal simulation window so that
// we can redirect them to the PLC program.
//-----------------------------------------------------------------------------
static LRESULT CALLBACK UartSimulationTextProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
if(msg == WM_CHAR) {
QueuedUartCharacter = (BYTE)wParam;
return 0;
}
return CallWindowProc((WNDPROC)PrevTextProc, hwnd, msg, wParam, lParam);
}
//-----------------------------------------------------------------------------
// Pop up the UART simulation window; like a terminal window where the
// characters that you type go into UART RECV instruction and whatever
// the program puts into UART SEND shows up as text.
//-----------------------------------------------------------------------------
void ShowUartSimulationWindow(void)
{
WNDCLASSEX wc;
memset(&wc, 0, sizeof(wc));
wc.cbSize = sizeof(wc);
wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC |
CS_DBLCLKS;
wc.lpfnWndProc = (WNDPROC)UartSimulationProc;
wc.hInstance = Instance;
wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
wc.lpszClassName = "LDmicroUartSimulationWindow";
wc.lpszMenuName = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClassEx(&wc);
DWORD TerminalX = 200, TerminalY = 200, TerminalW = 300, TerminalH = 150;
ThawDWORD(TerminalX);
ThawDWORD(TerminalY);
ThawDWORD(TerminalW);
ThawDWORD(TerminalH);
if(TerminalW > 800) TerminalW = 100;
if(TerminalH > 800) TerminalH = 100;
RECT r;
GetClientRect(GetDesktopWindow(), &r);
if(TerminalX >= (DWORD)(r.right - 10)) TerminalX = 100;
if(TerminalY >= (DWORD)(r.bottom - 10)) TerminalY = 100;
UartSimulationWindow = CreateWindowClient(WS_EX_TOOLWINDOW |
WS_EX_APPWINDOW, "LDmicroUartSimulationWindow",
"UART Simulation (Terminal)", WS_VISIBLE | WS_SIZEBOX,
TerminalX, TerminalY, TerminalW, TerminalH,
NULL, NULL, Instance, NULL);
UartSimulationTextControl = CreateWindowEx(0, WC_EDIT, "", WS_CHILD |
WS_CLIPSIBLINGS | WS_VISIBLE | ES_AUTOVSCROLL | ES_MULTILINE |
WS_VSCROLL, 0, 0, TerminalW, TerminalH, UartSimulationWindow, NULL,
Instance, NULL);
HFONT fixedFont = CreateFont(14, 0, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
FF_DONTCARE, "Lucida Console");
if(!fixedFont)
fixedFont = (HFONT)GetStockObject(SYSTEM_FONT);
SendMessage((HWND)UartSimulationTextControl, WM_SETFONT, (WPARAM)fixedFont,
TRUE);
PrevTextProc = SetWindowLongPtr(UartSimulationTextControl,
GWLP_WNDPROC, (LONG_PTR)UartSimulationTextProc);
ShowWindow(UartSimulationWindow, TRUE);
SetFocus(MainWindow);
}
//-----------------------------------------------------------------------------
// Get rid of the UART simulation terminal-type window.
//-----------------------------------------------------------------------------
void DestroyUartSimulationWindow(void)
{
// Try not to destroy the window if it is already destroyed; that is
// not for the sake of the window, but so that we don't trash the
// stored position.
if(UartSimulationWindow == NULL) return;
DWORD TerminalX, TerminalY, TerminalW, TerminalH;
RECT r;
GetClientRect(UartSimulationWindow, &r);
TerminalW = r.right - r.left;
TerminalH = r.bottom - r.top;
GetWindowRect(UartSimulationWindow, &r);
TerminalX = r.left;
TerminalY = r.top;
FreezeDWORD(TerminalX);
FreezeDWORD(TerminalY);
FreezeDWORD(TerminalW);
FreezeDWORD(TerminalH);
DestroyWindow(UartSimulationWindow);
UartSimulationWindow = NULL;
}
//-----------------------------------------------------------------------------
// Append a received character to the terminal buffer.
//-----------------------------------------------------------------------------
static void AppendToUartSimulationTextControl(BYTE b)
{
char append[5];
if((isalnum(b) || strchr("[]{};':\",.<>/?`~ !@#$%^&*()-=_+|", b) ||
b == '\r' || b == '\n') && b != '\0')
{
append[0] = b;
append[1] = '\0';
} else {
sprintf(append, "\\x%02x", b);
}
#define MAX_SCROLLBACK 256
char buf[MAX_SCROLLBACK];
SendMessage(UartSimulationTextControl, WM_GETTEXT, (WPARAM)sizeof(buf),
(LPARAM)buf);
int overBy = (strlen(buf) + strlen(append) + 1) - sizeof(buf);
if(overBy > 0) {
memmove(buf, buf + overBy, strlen(buf));
}
strcat(buf, append);
SendMessage(UartSimulationTextControl, WM_SETTEXT, 0, (LPARAM)buf);
SendMessage(UartSimulationTextControl, EM_LINESCROLL, 0, (LPARAM)INT_MAX);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -